トップ «前の日記(2006-03-27) 最新 次の日記(2006-03-29)» 編集

日々の破片

著作一覧

2006-03-28

_ Rubyでデッドロック

スクリプトがいきなりデッドロックだと言い出したのでびっくりして(スクリプトではなく)eval.cを眺めたら、なるほど確かにスクリプトのバグだった。

うんと短縮するとこんな感じ。

#!/usr/local/bin/ruby -Ks
require 'thread'
 
q = Queue.new
Thread.new do
  q.deq
end.join

実行すると次のようになる。

test>ruby dl.rb
deadlock 0xf63c8: sleep:-  - /usr/local/lib/ruby/1.8/thread.rb:281
deadlock 0x10d678: sleep:J(0xf63c8) (main) - dl.rb:5
dl.rb:5:in `join': Thread(0x10d678): deadlock (fatal)
	from dl.rb:5

追記:考えてみたら、これはスクリプトのバグを示しているのに、ソースを見なければメッセージの意味がわからんという困ったやつだな。というわけで読み方の説明。(追記の追記:スクリプトのバグじゃない可能性があるかも知れないのでwait_forの内容をまじめに出してるのかも)

deadlockという行はすべてのスレッドの状態を示す。

deadlock 0xf63c8: sleep: -  - /usr/local/……
            A       B    C  D      E
  • A: スレッドの参照
  • B: スレッドの状態
  • C: スレッドの待ち状態理由
    • F(x): ファイルディスクリプタxで待ち
    • S: IOセレクション
    • T(x): タイムアウト待ち(xミリ秒)
    • J(x): スレッドxに対してjoin
    • P: プロセスウェイト
    • -: それ以外
  • D: メインスレッドかそれ以外か
  • E: ソースファイル:行番号

メインスレッドはワーカスレッドの終了を待つ。ワーカスレッドはエンキューを待つ。したがってどのスレッドも動けない。

デッドロックというのはたすき掛けの競合状態をさすと思いこんでいたので、最初、原因がわからなかった(もちろん本当のスクリプトは遙かに複雑な仕組みだ)。

RubyがOSネイティブスレッドを利用するようになっても、こういう細やかな制御はできるのだろうか? すべての割り込みやシグナルやOSネイティブなキューによる実行状態への遷移を見誤ると無意味にデッドロックと判定されて困ることになるし、かといって永遠に待たれるのはバグを見つけるチャンスが相当減ることになる。

ちょっと考えてみたが、OSネイティブスレッドを利用した場合は、いずれにしろ、すさまじく限定された範囲でしかデッドロックは検出できないだろう(dlを利用して直接OSの機能とか呼ばれたらお手上げだ)。

プログラミング言語によるバグ検出というのは、せいぜい静的型チェックとか文法エラーに毛が生えた程度のものを想像していたのだが、現在のRubyの実行時のバグ検出能力は遙かにレベルが高いな、と思ったのだった(言語ではなくスレッドライブラリがうまくできているのだ、という言い方も出来るのでちょっと違うかな?)。

_ すげーよマイクロソフト

知らぬはおればかりなりなのだとは思うけど、良くまあ、このドメインを取得できたものだな。

Windows Live Beta

追記:いかん。つい遊んでしまったが、相当うまくできている(マシンとネットワークが非力だからかいまいちに感じるところもある)。

#Proxy.js

本日のツッコミ(全6件) [ツッコミを入れる]
_ ささだ (2006-03-28 11:16)

デッドロック検出はねー。どうしたもんですかねぇ。ネイティブスレッドを使うと即不可能、というのでは無いんですが、並列実行させようと思うと難しくなる...のかな。<br><br>ちなみに、直接OSの機能を使われたら今のRubyでも簡単にデッドロックするような気がします。

_ arton (2006-03-28 11:23)

>、直接OSの機能<br>あ、それはそうですね。<br>なんとなくネイティブスレッド=API直接叩くみたいに短絡したみたいです。Queue#deqやThread#joinはRubyの世界なのでこれらを利用している限りは制御可能でした。<br>むしろささださんが書かれているとおり、並列実行させる場合のリソース(Threadのステータスとか)の排他制御がとても大変そうですね(と他人事のように書く)。

_ (2006-03-28 20:32)

難しいことはよくわからないけど、今のRubyのスレッドはかなり好き。

_ arton (2006-03-28 22:55)

上のdlのやつだけど違った。現在のRubyでネイティブな待ち状態になるAPIを呼べば、全部が止まる(特定スレッドが止まるわけではない)。でも、ネイティブスレッドが使えると、止まるのはそのスレッドだけになる(だから、比較的自由に待ち状態になるAPIを呼べるようになる)。でも、それはRubyでデッドロックの原因となるか、それとも一時的なもので戻ってくるのかの判断はできない。ということでした。<br>というか、F(X)とかT(X)とかってありえるのだろうか?

_ KM (2006-03-30 09:25)

テクマスクリプト、もとい。素朴に考えると待ちに入る時に原因のほうを辿るような実装なら自分がいるようならアウト、とかできてるのが、ネイティブな待ちにそういう機能がないと駄目、とかそんな感じ?

_ arton (2006-03-30 11:06)

ネイティブな待ちはRubyには見えない(し、見える必要もない)という感じですかね。<br>たとえば、ネイティブAPIでSleep(永遠指定)を呼んだか、Sleep(一時的)を呼んだかの区別はRuby側では判断できませんよね。で、後者がありえるからネイティブAPIを呼んだというところまでは判断できても(dlで呼び出し前後でステータスを変えることは可能)、それをデッドロック判定に利用できないのではないかと思ったということです(スレッドのステータスをRUNNABLEとみなすことになるのではないかということ)。もちろん、そんなことをするスクリプトが悪いわけで、Rubyが提供している機構を使えというだけなんですが。<br>#ネイティブスレッド対応でのVM実装をどうするかの話ではなくて、アプリケーション設計の話です。スクリプトを書く側がスレッドが独立して動けるのを良いことにネイティブAPIを使いまくると、せっかくのRubyのデッドロック判定がきかなくなるな、と思ったというのが始まりです。


2003|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|03|04|05|06|07|08|09|10|11|12|

ジェズイットを見習え