原です。
mutex.rb があるにもかかわらず、現在の thread.rb に入っている CV
のコードを元にして、正常に動作するものに書き直そうと思ったので
すが、、、
-----------------------^ cv1.rb
class ConditionVariable
def initialize
@waiters = []
end
def wait(mutex)
mutex.unlock
Thread.critical = true
@waiters.push(Thread.current)
Thread.stop
mutex.lock
end
def signal
Thread.critical = true
t = @waiters.shift
Thread.critical = false
t.run if t
end
def broadcast
Thread.critical = true
waiters0 = @waitors.dup
@waiters.clear
Thread.critical = false
for t in waiters0
t.run
end
end
end
-----------------------$ cv1.rb
となりました。少し解説すると、現在の thread.rb では
def wait(mutex)
mutex.unlock
@waiters_mutex.synchronize {
@waiters.push(Thread.current)
}
Thread.stop
mutex.lock
end
ですが、これは例えば咳さんの TinyQueue でいえば、Thread.stop の前
に Thread.pass を書いただけで、dead lock したりします。これの理由
は前田さんが [ruby-list:14504] で言っているように stop してないう
ちに run されてしまうからです。で、Thread.critical = true をそれ
以前に書かなければならないわけですが、synchronize にも unlock にも
Thread.critical = false というコードが含まれますから、このままで
は効果がありません。そこで cv1.rb の様になったのです。
ところが、cv1.rb でもやはり wait の mutex.unlock の後に「すき間」
があるので、ここに Thread.pass を書いたりすると dead lock を起こ
したりします。そこで次のように改良します。
-----------------------^ cv2.rb
def Thread.exclusive
begin
Thread.critical = true
r = yield
ensure
Thread.critical = false
end
r
end
class Mutex
def exclusive_unlock
return unless @locked
Thread.exclusive do
t = @waiting.shift
@locked = false
t.wakeup if t
yield
end
self
end
end
class ConditionVariable
def initialize
@waiters = []
end
def wait(mutex)
mutex.exclusive_unlock do
@waiters.push(Thread.current)
Thread.stop
end
mutex.lock
end
def signal
t = @waiters.shift
t.run if t
end
def broadcast
waitors0 = nil
Thread.exclusive do
waiters0 = @waitors.dup
@waiters.clear
end
for t in waiters0
t.run
end
end
end
-----------------------$ cv2.rb
これでうまく動くと思います。
#同じ事が monitor.rb でも問題になると思いますが、大丈夫なのかな。
signal の後、条件が変わってしまって、、、という [ruby-list:6735]
以降のスレッドで議論された問題は run を使う限りないと思うのです
がどうでしょうか。