前田さん、akr さん
遠藤です。

2010年2月5日14:48 Shugo Maeda <shugo / ruby-lang.org>:
>> Ruby 1.8 のころは timeout 引数が動作していたようなのですが、
>> Ruby 1.9 で timeout 引数がまた動作するようになる予定はあるのでしょうか。
>> メッセージからするとそのうち実装されるような感じがするのですが。
>
> ::ConditionVariable#waitがtimeoutに対応しているようなので、引数を
> そのまま渡すようにしたいと思います。
> ただ、戻り値については1.8と非互換になりますが、timeoutで返って来たのか
> 判定する術がないので、常にtrueを返すことにしたいと思います。
> # ついでにドキュメントを追加します。

[Bug #2629] で ::ConditionVariable#wait の API 設計に race condition が
あることがわかりました。
秒数ではなく絶対時刻で指定すべき (少なくとも、絶対時刻は受け付けるべき)
ようです。また、timeout で帰ってきたかどうかを返り値として返した方が
よいという指摘もあります。

絶対時刻を受け付けるためには thread.c を結構いろいろ変えないといけない
ので、すでに feature freeze 後ということもあり、保守的ですが 1.9.2 では
revert しようと思いますが、いかがでしょうか。


>> とりあえず wait にタイムアウトを入れて、ハングしないで test-all
>> が最後までいくようにしようか、と思ったのですが、
>> MonitorMixin::ConditionVariable#wait の timeout 引数を指定すると
>> timeout is not implemented yet というメッセージで
>> NotImplementedError になってしまいます。

こっちの問題に関しては、タイミングに依存するので多少ださいですが、時間
指定の sleep を使えばもっと簡単になると思います。
test/ruby 以下にはこんなコードがたくさんあります。

ただ、このテストがテストしたいのが imap.idle のブロックからの途中脱出だ
としたら、break するだけでいいかも?

diff --git a/test/net/imap/test_imap.rb b/test/net/imap/test_imap.rb
index 501298c..b1994b1 100644
--- a/test/net/imap/test_imap.rb
+++ b/test/net/imap/test_imap.rb
@@ -217,30 +217,18 @@ class IMAPTest < Test::Unit::TestCase
       begin
         imap = Net::IMAP.new("localhost", :port => port)
         begin
-          th = Thread.current
-          m = Monitor.new
           in_idle = false
-          exception_raised = false
-          c = m.new_cond
-          Thread.start do
-            m.synchronize do
-              until in_idle
-                c.wait(0.1)
-              end
-            end
-            th.raise(Interrupt)
-            exception_raised = true
+          th = Thread.start do
+            Thread.pass until in_idle
+            Thread.main.raise(Interrupt)
           end
           imap.idle do |res|
-            m.synchronize do
-              in_idle = true
-              c.signal
-              until exception_raised
-                c.wait(0.1)
-              end
-            end
+            in_idle = true
+            sleep 1
           end
         rescue Interrupt
+        ensure
+          th.kill if th.alive?
         end
         assert_equal(2, requests.length)
         assert_equal("RUBY0001 IDLE\r\n", requests[0])

-- 
Yusuke Endoh <mame / tsg.ne.jp>