#windows 用の Scm__InternalCondWait ですが,タイムアウトの場合も戻り値を変える以外はタイムアウトでない場合と同じでいい(というか,同じでないといけない)と思うんですが,どうでしょう?
#タイムアウトで起きる人は SignalObjectAndWait(*mutex) から起きてから numWaitersLock をとるまでは condbroadcast が同じく numWaitersLock をとるまでは競合する可能性があると思うのです.
#とすると,CondWait していた人が先にとった場合は問題ないですが,CondBroadcast のほうが先にとると,タイムアウトで起きた人も lastWaiter 候補になりそうで.
#自分が SignalObjectAndWait の挙動を誤解している可能性もありますが
#む、ちょいと込み入ってて私もよくわからないので一歩づつ。まずScm__InternalCondWaitのタイムアウトの場合とそうでない場合の区別ってのはここですね。
#if (r0 != WAIT_OBJECT_0) {
EnterCriticalSection(&cond->numWaitersLock);
cond->numWaiters--;
LeaveCriticalSection(&cond->numWaitersLock);
} else {
/* ok, we're woken up. the flag lastWaiter is TRUE if we're the
last waiter after the broadcast. */
EnterCriticalSection(&cond->numWaitersLock);
cond->numWaiters--;
lastWaiter = cond->broadcast && cond->numWaiters == 0;
LeaveCriticalSection(&cond->numWaitersLock);
if (lastWaiter) {
/* tell the broadcaster that all the waiters have gained
control, and wait to aquire mutex. */
r1 = SignalObjectAndWait(cond->done, *mutex, INFINITE, FALSE);
} else {
/* Aquire mutex */
r1 = WaitForSingleObject(*mutex, INFINITE);
}
}
#そこです
#そこでは *mutex は常にロックされていないと思ってますが,あってます?
#タイムアウトのケースは私が付け加えたんで勘違いしてる可能性はおおいにあります。
#mutexはScm__InternalCondWait, Scm__InternalCondSignal, Scm__InternalCondBroadcastが呼ばれる場合はロックされてる前提です。これは元になったコードもそうなので。(pthreadではそういう要請がないので、そうなっていない呼び出しがあるかもしれません。それは見落としです)
#あ,いえ,そこっていうのはその if (r0 != WAIT_OBJECT_0) の行です.
#SignalObjectAndWait(*mutex, ...) から抜けたときには常に *mutex はロックされてない,ということ.
#あそっちか。SignalObjectAndWaitがmutexをアンロックするので、そうですね。
#そこからnumWaitersを減らす前に、CondBroadcastがnumWaitersを見て待ちがいると思ってしまう、ってこと?
#そうです.
#で、CondSignalが先走った場合は空振りで済むけど、CondBroadcastは待ちに入っちゃうからまずい、というわけか。
#s/Signal/Wiat/ ですね.
#あ、いや、CondSignalもnumWaitersを見ているので。
#あ,じゃあそっちの検討はぜんぜんしてないです.自分のいってるのはまだ CondWait vs CondBroadcast です.
#そもそもタイムアウトでCondWaitから返る場合もmutexは持ってないとならないんだから確かにまずい。でも、タイムアウトした場合、CondBroadcastしている人がいるとは限らないから、lastWaiterの分岐にはいっちゃうとまずい。
#それは cond->broadcast で区別されるんじゃないんですか?
#Signal と Broadcast が同時には実行されないことは外側の mutex が保証しますよね?
#そうか。lastWaiterに入るパスは必ずbroadcastした人がいるわけか。
#わかりました。確かにタイムアウトの場合も同じパスを通らないとだめですね。
#この実装は実は外側の mutex が結構ミソなんだと読んでて思いました
#です。ちゃんとコメントで詳しく書いとかないと危ないですね。
#ということは,実は cond->mutex のほうは要らない?
#あれ、cond->mutexはどうしてつけたんだっけな… 外部のmutexがロックされてないときのための安全弁でつけたんだっけな。でも
#condwaitの方はそれで保護してなくていいんだっけ。
#なんか、外部のmutexの制約を外そうとしてちょっとやって、やっぱり面倒になってやめた覚えがある。
#この実装の場合,外側の mutex とってないと破綻するのであまり意味がないような.
#多分cond->mutexは外側のmutex制約を外そうとした時の名残りなんじゃないかという気がする。
#condwaitが呼ばれた時点でcond->mutexに外部のmutexをセットしておく (違うmutexが既にセットされてたらエラー) ってことにしておいたらいける? signal, broadcastはcond->mutexが未セットだったらnoop。
#セットされた場合はよさそうかな
#あ、いやmutexがrecursiveじゃないとまずいか (signalやbroadcastが内部でcond->mutexをロックする場合)。windowsのmutexってどうだったかな。
#ああ,そうですね.セットされていてまじめに外側でもロックしてたら二度ロックしますね.
#これ読むとrecursiveみたい "A thread can specify a mutex that it already owns in a call to one of the wait functions without blocking its execution. This prevents a thread from deadlocking itself while waiting for a mutex that it already owns. However, to release its ownership, the thread must call ReleaseMutex one time for each time that it obtained ownership (either through CreateMutex or a wait function)."
#未セットの場合は signal や broadcast が空振りに終わる可能性があるけど,それはそもそも外側でロックしてない場合だから自業自得?
#そうみたいですね > recursive
#未セットの場合は待ってる人がいないわけだから空振りでいいと思うんですが、ハザードありますかね。condwaitに入ってセットをするまでの微妙なタイミングってのはあるけど、それはそういう微妙なタイミングに依存してるプログラムの方がまずいような。待ってる人がいないsignal/broadcastってpthreadでも無効でしたよね…確認しよ
#確認した。空振りok。
#NetBSD のマニュアルには if no threads are waiting on cond, neighter function has any effect. てありますね
#broadcast/singal が未セットだと思い,かつ今まさに condwait でセットしようというひとがいるということはすなわち,その broadcast/signal が mutex をとらずに呼ばれている,でいいんですよね?
#です。pthreadでは許されてるので (でも行儀は悪い)
#行儀が悪いというか,まあバグなことがおおいですよね
#これであの解説ページの実装に比べてタイムアウト対応と行儀の悪いプログラムでも破綻しない対応が追加されたんですね.すばらしい.
#いつもいつもレビューありがとうございます。本当に助かります。
#いえいえ,趣味で読んでるだけですので…
#こっちこそ面白いコード読ませてもらえてありがたいです.
#Kimura Fuyukiさんってここ見てるかな。見てたら連絡ください。hadaly.orgのアドレスはバウンスしてしまいました。