24 February '2007 - 19:15 | 技術動向 darwin ruby では exec が使えない(場合がある)
Lingr 常駐用ツールの Lingresident には !restart というコマンドが実装してある。が、これは実際に期待するような動作をしない。
たとえば設定を変えた時とかコードを修正した時などに、Lingresident の起動をサーバ上でし直すのが面倒なのでチャットルームから再起動できたらいいかな、と思ったのだけれど、残念ながらそういう風には動いてくれない。
理由は、どうやって実装すればいいのかが、まだよく分かっていないから。
ひとつ思いついた方法は、組み込み関数 exec で自分自身を実行し直すという方法。ひとつというか、これしか思いついてない。これが動けば問題ない。
しかし、exec が期待したようには動してくれない。正確に言うと、Lingresident を実行している Linux サーバ上では期待通りに動いたけれど、手元の OSX 上では動かない。サーバ上で動くんだから問題ないとも言えるけれど、手元でテストできないコードをサーバ上で動かすのは気持ちが悪い。
いろいろ試してみた結果、動かないのはスレッドを生成した時だけということらしい。
以下、その問題再現コード。
$ cat test.rb
puts $$.to_s
ARGV.empty? || Thread.new { p Thread.list }.join
p Thread.list
sleep 1
exec 'ruby', $0, *$*
やっていることは、一秒寝てから exec で自分が起動されたようにコマンドを実行すること。ただし、コマンドライン引数があったらスレッドを生成する。以下、Linux での実行結果。
$ ruby --version
ruby 1.8.5 (2006-08-25) [i686-linux]
$ ruby test.rb
6471
[#<Thread:0xb7fee7d4 run>]
6471
[#<Thread:0xb7fee7d4 run>]
6471
[#<Thread:0xb7fee7d4 run>]
6471
[#<Thread:0xb7fee7d4 run>]
^Ctest.rb:6:in `sleep': Interrupt
from test.rb:6
$ ruby test.rb foo
6490
[#<Thread:0xb7fee7d4 run>, #<Thread:0xb7fe1bc4 run>]
[#<Thread:0xb7fee7d4 run>]
6490
[#<Thread:0xb7fee7d4 run>, #<Thread:0xb7fe1bc4 run>]
[#<Thread:0xb7fee7d4 run>]
6490
[#<Thread:0xb7fee7d4 run>, #<Thread:0xb7fe1bc4 run>]
[#<Thread:0xb7fee7d4 run>]
6490
[#<Thread:0xb7fee7d4 run>, #<Thread:0xb7fe1bc4 run>]
[#<Thread:0xb7fee7d4 run>]
^Ctest.rb:6:in `sleep': Interrupt
from test.rb:6
特に何も問題なく動いている。
つぎに、OSX 上でのテスト。
$ ruby --version
ruby 1.8.5 (2006-12-25 patchlevel 12) [i686-darwin8.8.1]
$ ruby test.rb
16423
[#<Thread:0x317cc run>]
16423
[#<Thread:0x317cc run>]
16423
[#<Thread:0x317cc run>]
16423
[#<Thread:0x317cc run>]
^Ctest.rb:6:in `sleep': Interrupt
from test.rb:6
$ ruby test.rb foo
16424
[#<Thread:0x317cc run>, #<Thread:0x253f0 run>]
[#<Thread:0x317cc run>]
test.rb:7:in `exec': Operation not supported - ruby (Errno::EOPNOTSUPP)
from test.rb:7
OSX 上ではスレッドが一度でも生成されてしまうと、exec が Errno::EOPNOTSUPP を発生してしまうらしい。理由はよく分からない。ちなみに、exec を system にすると、問題はおきない。
メーリングリストのアーカイブを検索して見たら、昨年十一月に http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/223931 というポストがあった。まったく同じ症状を報告している。ただ、とくに話が続いてないのでどういう扱いになってるかは不明。簡単な再現方法が報告されていないために、誤解されて見過ごされてしまっているのかも知れない。
うーん、どうしようかなあ。
あたりを参考に、
Thread.new {}.join
p Thread.list
sleep 1
fork { exec 'ruby', $0, *$* }
という感じで fork してから exec すると動くようです。
Psychs - 26 February '2007 - 02:10
ていうか、そんな裏技があるとは。まあ、fork しちゃうと PID が変わるのが残念ですけれど、当面それで凌いでおこうと思います。ありがとうございます。
ちなみに、メーリングリストに入るのが面倒くさかったので、とりあえず Apple の ruby 担当者と思しき人にメールしてみたら、「Thanks for the report. You are indeed talking to the right person.」って言われました。ので、そのうち治ると思われ。
ひろしま - 02 March '2007 - 12:15