原です。

In message "[ruby-list:14813] Re: ConditionVariable (again)"
    on 99/06/04, Shugo Maeda <shugo / netlab.co.jp> writes:
|
|前田です。

|> signal の後、条件が変わってしまって、、、という [ruby-list:6735]
|> 以降のスレッドで議論された問題は run を使う限りないと思うのです
|> がどうでしょうか。
|
|たしかスケジューリングの話ですよね。
|具体的にどういう内容かはすっかり忘れました(^_^;)が、monitor.rbで
|は効率を犠牲にして安全側に倒した覚えがあります。

ふーむ。monitor.rb は私にとって幾つか謎があります。

例えば Thread#run が一つも無くて、Thread#wakeup のみが使われている
こと。これも、スケジューリング問題の調整のためでしょうか。あとロッ
クの owner という概念があって、そこかしこに、owner がずれてると例外
を発生させる様になっているところとか。owner はカウントつきロックの
ためにだけあるのでは無いようですね。


|> mutex.rb があるにもかかわらず、現在の thread.rb に入っている CV

あ、またまたすいません。monitor.rb の書き間違いです。(^^;


|> #同じ事が monitor.rb でも問題になると思いますが、大丈夫なのかな。
|
|monitor.rbだとwaitの中のmutex.unlock相当の処理をConditionVariable
|の方で直接制御しているので、大丈夫だと思います。(たぶん)

monitor.rb は全部自前でバンバンしているんですね。

私はスレッドに関するプログラムがちゃんと動くかどうか検証するのに
あちこちに(critical である所とか、キューに回って Thread.stop した
直後とかは避けて)Thread.pass を入れて強制的にスレッドの切替を起こ
してみるのですが、monitor.rb で問題になりそうな所でやってみました。

まず monitor.rb コピー mon-test.rb を取ります。:-)
そして、253行目あたり、クリティカルセクションが終った所で pass さ
せます。

----------------------------------
      Thread.critical = false
MON_EXIT
Thread.pass
----------------------------------

そして、下の(せきさんの TinyQueue でインプットを2つにした)
スクリプトを動かします。

するとやはり dead lock が起こります。(Linux です。)
詳しい動作はわからないのですが、、、(直前の Thread.critical
 = false を削除しても直らない)。


-----------------^ test1.rb
#!/usr/bin/ruby
require "mon-test.rb"

Thread.abort_on_exception = true

class TinyQueue
  def initialize(max=2)
    @max = max
    @mutex = Monitor.new
    @full = @mutex.new_cond
    @empty = @mutex.new_cond
    @q = []
  end
  
  def count
    @q.size
  end
  
  def enq(v)
    @mutex.synchronize {
      @full.wait if count == @max
      @q.push v
      @empty.signal if count == 1 
    }
  end
  
  def deq
    @mutex.synchronize {
      @empty.wait if count == 0
      v = @q.shift
      @full.signal if count == (@max - 1)
      v
    }
  end
  
  alias send enq
  alias recv deq
end

q = TinyQueue.new(1)

foods = 'Apple Banana1 Strawberry Udon Rice Milk'.split

l = []

sender_size = 2

sender_size.times do
  th = Thread.new {
    for obj in foods
      q.send(obj) 
      print "sent ", obj, "\n"
    end
    q.send nil
  }
  l.push th
end


th = Thread.new {
  c = 0
  loop do
    obj = q.recv
    c += 1 if obj == nil
    print "  recv ", obj, "\n"
    break if c >= sender_size
  end
}
l.push th

l.each do |t|
  Thread.join t
end
-----------------$ test1.rb