limitusus’s diary

主に技術のことを書きます

Twitterの過去履歴を消去するtwdelを書いた

今まで数多くのTwitterクライアントアプリが書かれてきて、誰もが様々な方法で手軽にPOSTできるようになっている。
一方、過去の自分の歴史を抹消したいと考える需要もあるにもかかわらず、その需要に応えるアプリケーションは見当たらない。

そこで、今回 twdel というアプリケーションを実装した。
limitusus/twdel - Github

細かい必要モジュールなどはREADME.mdを読んでもらうとして、使い方は至って簡単である。

./authenticate

によってOAuth認証を行い、アクセストークンとアクセスシークレットを記録する(パスワードはもちろん保持しない)。
認証をやり直すときはいつでもこのコマンドを実行できる。基本的には最初の1回だけ。

./twdel

によって実際の消去を実行する。デフォルトではAPIの許す限り、2ページ目から100ページ目まで(0-originで)を消去しに行く。
オプションにより

./twdel -s 5 -e 10

などとすれば、5ページ目から10ページ目までの消去もできる。

ライセンスはMITです。
過去の自分を隠したい方はどうぞ。

libsqlite3.so を差し替える

ちょっと聞かれたので調べてみた。

目的

オリジナル libsqlite3.so.0 を実装したので、これを /module/libsqlite3.so.0 とし、 Python から使えるようにしたい。
Python

import sqlite3

とすると最終的に /usr/lib/python2.7/lib-dynload/_sqlite3.so が読まれるので、

ldd _sqlite3.so

としたときに libsqlite3.so.0 が /module/libsqlite3.so.0 を指すようにすればよい。

普通なら

動的ライブラリなんだから LD_LIBRARY_PATH を設定するのがまずは常識。

LD_LIBRARY_PATH=/module

しかし変わらない。

RPATH

実は _sqlite3.so には RPATH という情報が書かれていて、これが動的ライブラリの場所を示している。
manpage dlopen(3) を読んでもらうとわかるが、動的ライブラリはまずこの RPATH を検索し、ここにあればこれを使う。
LD_LIBRARY_PATH が読まれるのはその次の優先順位なのである。
したがって、この場合 LD_LIBRARY_PATH は機能しない。

RPATH を消す

以下の情報は多分に Frank's Geo-Geeking: RPATH, RUNPATH and LD_LIBRARY_PATH を参考にしている。
RPATH 情報は chrpath(8) により見ることができる。

chrpath /usr/lib/python2.7/lib-dynload/_sqlite3.so 
/usr/lib/python2.7/lib-dynload/_sqlite3.so: RPATH=/usr/lib/i386-linux-gnu

ここで気付いた。
_sqlite3.so の RPATH がないバージョンを作り、これを Python から読むようにすれば?」

cp /usr/lib/python2.7/lib-dynload/_sqlite3.so /module/_sqlite3.so
chrpath --delete /module/_sqlite3.so
export LD_LIBRARY_PATH=/module
ldd /module/_sqlite3.so

これで /module/_sqlite3.so が /module/libsqlite3.so.0 を読むようになってくれた。
もちろん Python から使うときには

export PYTHONPATH=/module

も忘れずに。

ちなみに

libpthread.so.0 ではこんな問題は起きない。というのも、libpthread.so.0 は RPATH で指定されたディレクトリではないところにあるからである。
この問題は SQLite3 モジュールが Python 組込みであることによっているのではないだろうか。

iccをインストールした記録

Intel C コンパイラって買わないと使えないのかと思ってたけど、非商用ならば無料でインストールできることがわかったので実際にやってみた。

ダウンロードまで

これは 2011 年 11 月 18 日の操作記録だが、サイト更新に伴いページ構成が変更されたりリンクが切れる可能性がある。

C Compiler & C++ Compiler Suite from Intel - Intel® Software Network

この辺に IntelC/C++ 系開発用ソフトウェアがまとめられていた。
画面上部の Tools & Downloads から Free Non-Commercial というのを選ぶ。

Non-Commercial Software Development - Intel® Software Network

ここで非商用利用の規約に同意する。

その後いくつかのソフトウェアを選択できる。ここでは Intel® C++ Composer XE 2011 for Linux を選択した。
あとは必要事項を入力すればダウンロードできるようになる。

ライセンス番号もさりげなく画面に表示されるので忘れずに控えておく。

インストー

tarball を落としてきたので、適当に展開し、その中の ./install.sh を実行する。
あとは画面の指示に従ってライセンス番号やインストール先を指定していけばいい。
root 権限があればシステム領域にインストールできるし、今回はユーザ領域($HOME/intel 以下)にインストールした。
RPM 系のディストリビューションではなかった & tarball の中には rpm ファイルもあったが、特に問題なくインストールできた。

lookupDictionary.js を直してみた

とっても便利な Vimperator プラグインの lookupDictionary.js。
どうやら ALC 側で XPath が変更されてしまったらしく結果が帰ってこなくなってしまったため、簡単に修正。

Index: lookupDictionary.js
===================================================================
--- lookupDictionary.js	(リビジョン 37220)
+++ lookupDictionary.js	(作業コピー)
@@ -13,7 +13,7 @@
     names: ['eiji[ro]'],
     url: 'http://eow.alc.co.jp/%s/UTF-8/',
     shortHelp: 'SPACE ALC (\u82F1\u8F9E\u6717 on the Web)',
-    xpath: 'id("resultList")',
+    xpath: 'id("resultsList")',
     dictionary: 'en-US'
 },{
     names: ['goo[dictionary]'],

快適な辞書ライフを!

Python で signal と threading を両立してみた

経緯

最近書いているプログラムで無限ループするワーカースレッドを立てまくるものがあって, それを signal で安全に終了させる手段が知りたかった.

課題プログラム

以下のようなプログラムがあります.

import threading
import time
def loopfunc(event):
    print "Thread Started"
    while not event.isSet():
       time.sleep(1)
    print "Thread End"

def main() :
    threads = []
    e = threading.Event()
    for x in range(10):
        threads.append(threading.Thread(target=loopfunc, args=(e,)))
        threads[x].start()
    for th in threads:
        th.join()

main()

これを起動すると10個のスレッドが作られて, 無限ループします. これに対して SIGINT などを送っても何も起きません. これは Python の thread 全体に対していえることらしいです. たとえば明示的にシグナルハンドラを作って

import threading
import time
import signal
def loopfunc(event):
    print "Thread Started"
    while not event.isSet():
       time.sleep(1)
    print "Thread End"

def sighandler(event, signr, handler):
    event.set()

def main() :
    threads = []
    e = threading.Event()
    signal.signal(signal.SIGINT, (lambda a, b: sighandler(e, a, b)))
    for x in range(10):
        threads.append(threading.Thread(target=loopfunc, args=(e,)))
        threads[x].start()
    for th in threads:
        th.join()

main()

としても, やはり SIGINT は無視されます. これはなぜかというと, join() を呼んでいる最中は signal を受け付けないからです.

解決策

これは絶対に誰か出会った問題だろうと思って調べてみたら(下の参考ページ参照), 開始直後に os.fork() してシグナルハンドラを登録し, スレッドのマスターに SIGKILL を送信することによって解決するという方法が掲載されていました. しかしさすがにこれは乱暴すぎるような気がしたので, もう少し穏やかな終了方法を考えてみました. ふと見るとこれ, 先輩のページだ!

import threading
import time
import signal
def loopfunc(event):
    print "Thread Started"
    while not event.isSet():
       time.sleep(1)
    print "Thread End"

def sighandler(event, signr, handler):
    event.set()

def main() :
    threads = []
    e = threading.Event()
    signal.signal(signal.SIGINT, (lambda a, b: sighandler(e, a, b)))
    for x in range(10):
        threads.append(threading.Thread(target=loopfunc, args=(e,)))
        threads[x].start()
    for th in threads:
        while th.isAlive():
            time.sleep(0.5)
        th.join()

main()

このように 0.5 秒に1回, スレッドの生存を確認することによって join() を呼ばず, シグナルを受け付けられるようにしてみました. この他にも join([timeout]) に引数を渡して isAlive() で調べるループにするという方法もアリだと思います.

これがベストなのかどうかは分かりませんが, やりたいことは実現できたのでメモ.

Embarrassingly Parallel なプログラムを pthread で並列化してみた

やったこと

後輩の授業で Embarrassingly Parallel なプログラムを並列化するという課題が出ていたので、ちょっとやってみました。

実際に使ったコードはgithubで公開中です。公開は課題の締切後に行いました。

課題1 1次元関数の数値積分区間分割による並列化

実関数 $f(x)=sin(x)$ が与えられ、$\int_0^{\pi/2} f(x) dx$ を区分求積法で求めるプログラムがあったとき、区間 $(0, \frac{\pi}{2})$ を N 分割し、 N 個の POSIX thread に割り当てて並列化するという問題です。

この課題は練習用で、プログラムは公開されていました。単に走らせて性能を測ればよいものでした。

課題2.1 素数探索プログラムの区間分割による並列化

2から10000000までの整数のうち、素数がいくつあるかを数えるプログラムが与えられたとき、区間を N 個に分割し、 N 個の POSIX thread で並列化する問題です。課題1のコードを利用すればできる比較的簡単な課題です。

課題2.2 素数探索プログラムの負荷分散を考慮した並列化

素数の分布は一様ではないため、課題2.1のコードでは負荷が偏ってしまいます。そこで区間を M 分割し、 N 個の POSIX thread を利用することで並列化するのが本課題です。

やりかたは単純で、

  1. 区間を M 個に分割し、それぞれをタスクとして作る。
  2. N 個の POSIX thread を作る。

というものです。各 thread は

for j in range(0 .. M-1) :
  if (j 番目のタスクは終わっていない) :
    j 番目のタスクをやる

とふるまいます。これに排他制御を行います。タスクを表す構造体に「手を付けた」というフラグを追加します。

for j in range(0 .. M-1) :
  lock(mutex)
  if (j 番目のタスクは誰も手を付けていない) :
    j 番目のタスクに手を付ける
    unlock(mutex)
    j 番目のタスクをやる
  else :
    unlock(mutex)

このようにすれば OK です。

続きを読む