Feature #3251
closedallow to unlock mutex locked by another thread
Description
=begin
遠藤です。
現状では mutex は lock したスレッドからしか unlock できませんが、
これを許可するようにしませんか。動機は 2 つあります。
- Python の condition variable が mutex を別スレッドから unlock
することで実装されている、のが真似できる - Thread#raise を race condition なしに使えるようになる (気がする)
1 について、Python の condition variable は以下のような感じに実装
されています。
def wait(m1)
m2 = Mutex.new
m2.lock
@waiters << m2
m1.unlock
begin
m2.lock
ensure
m1.lock
end
end
def signal
@waiters.shift.unlock
end
つまり、wait は二重に mutex を lock しようとすることでブロックし、
signal は mutex を別スレッドから unlock することでブロックしている
スレッドを再開します。
今の ConditionVariable の実装には大量の問題点が指摘されているので、
Python の真似をするとよいのではと思います。
権威主義¶
2 について、現状は Thread#raise には以下のような race が存在します。
t1: begin 節を実行している
t2: t1.raise する
t1: rescue/ensure 節の実行を開始する
t3: t1.raise する
t1: rescue/ensure 節が実行されないまま再度例外が発生する
ちなみにこの race はシグナルにも存在します¶
これを、Thread#raise の前に Mutex#lock するというルールにすれば、
race を避けて使うことができるようになります。と思います。
t1: begin 節を実行している
t2: m.lock; t1.raise する
t1: rescue/ensure 節の実行を開始する
t3: m.lock が止まらないので t1.raise できない
t1: 次の例外が投げ込まれる準備ができたら m.unlock する
t3: m.lock が終わって t1.raise する
--
Yusuke Endoh mame@tsg.ne.jp
=end
Updated by ko1 (Koichi Sasada) over 14 years ago
=begin
ささだです.
(2010/05/06 1:24), Yusuke Endoh wrote::
Feature #3251: allow to unlock mutex locked by another thread
http://redmine.ruby-lang.org/issues/show/3251起票者: Yusuke Endoh
ステータス: Open, 優先度: Normal
担当者: Koichi Sasada, カテゴリ: core, Target version: 1.9.x遠藤です。
現状では mutex は lock したスレッドからしか unlock できませんが、
これを許可するようにしませんか。動機は 2 つあります。
- Python の condition variable が mutex を別スレッドから unlock
することで実装されている、のが真似できる- Thread#raise を race condition なしに使えるようになる (気がする)
Python はなぜ Mutex を他から解放できるのか,よくわからないのですが,
http://ja.wikipedia.org/wiki/%E3%83%9F%E3%83%A5%E3%83%BC%E3%83%86%E3%83%83%E3%82%AF%E3%82%B9
狭義には、ミューテックスの場合にそれをロック(P操作)したタスクのみがア
ンロック(V操作)できるのに対して、セマフォではその様な制約はない。
ということで,Mutex という名前だと,他から解放出来ない,というニュアンス
が含まれてるんじゃないかなぁ,という感覚があります.
他のシステムだと,セマフォがあるからいいじゃん,ということなのかもしれ
ず,じゃぁ Ruby にもセマフォを入れないといけない,ということかもしれません.
「感覚」なので,根拠のある話ではないんですが,用語の混乱を避けるために
も,「便利そうだから入れちゃえ」というのはまずいんではないかと愚考する次
第です.Fiber とかてきとーに入れちゃった人間が言うのもなんですが.
そもそも,Mutex は critical section を作るためのものなの
で,lock/unlock は使わないで欲しいなぁ,という気がします.な
ら,CriticalSection クラスでも作るべきなのかな.
--
// SASADA Koichi at atdot dot net
=end
Updated by kosaki (Motohiro KOSAKI) over 14 years ago
=begin
kosakiです
ということで,Mutex という名前だと,他から解放出来ない,というニュアンス
が含まれてるんじゃないかなぁ,という感覚があります.他のシステムだと,セマフォがあるからいいじゃん,ということなのかもしれ
ず,じゃぁ Ruby にもセマフォを入れないといけない,ということかもしれません.「感覚」なので,根拠のある話ではないんですが,用語の混乱を避けるために
も,「便利そうだから入れちゃえ」というのはまずいんではないかと愚考する次
第です.Fiber とかてきとーに入れちゃった人間が言うのもなんですが.
思いつく限り1つだけデメリットがあります。Linuxだとmutexとsemaphoeの両方が
カーネル内にあるわけなんですが、semaphoeはそのセマンティクス上
lockdep(カーネル内ロックバリデータ)非サポートであり、近年では強く非推奨に
近いステータスとなっています。
semaphoeセマンティクスだとABBAデッドロックを検知しても第三者が、
unlockしてくれることで実はちゃんと動くかも知れないので、実用的な
lock validatorの実装がむつかしいと。
このへんは遠藤さんのほうが専門なので、ご意見いただけるとありがたいです。
=end
Updated by mame (Yusuke Endoh) over 14 years ago
=begin
遠藤です。
2010年5月6日1:55 SASADA Koichi ko1@atdot.net:
Python はなぜ Mutex を他から解放できるのか,よくわからないのですが,
snip
ということで,Mutex という名前だと,他から解放出来ない,というニュアンス
が含まれてるんじゃないかなぁ,という感覚があります.
そういえば Python では Lock という名前です。
よくわかりませんが、マニュアルを見る感じ mutex とは明確に区別されて
いるようです。さすが Python 先生。
他のシステムだと,セマフォがあるからいいじゃん,ということなのかもしれ
ず,じゃぁ Ruby にもセマフォを入れないといけない,ということかもしれません.
それなら Semaphore を作りましょうかねえ。
まあ、順序付き Map を Hash と呼ぶ Ruby なら些細な話ではないかという
気もしますが。
2010年5月6日17:09 KOSAKI Motohiro kosaki.motohiro@gmail.com:
semaphoeセマンティクスだとABBAデッドロックを検知しても第三者が、
unlockしてくれることで実はちゃんと動くかも知れないので、実用的な
lock validatorの実装がむつかしいと。
第三者は unlock は呼べませんが、Thread#raise や Thread#kill を呼ぶ
ことができるので、ABBA deadlock から抜け出せる可能性があります。
恐ろしいことに。
なので Ruby の deadlock 検出は、「生きている全スレッドがロック解放
待ち、または無期限 sleep で休止中」という、非常に保守的な判定で実装
されてます。1 つでも実行中のスレッドがあるなら deadlock を報告する
ことはありません。
--
Yusuke Endoh mame@tsg.ne.jp
=end
Updated by mame (Yusuke Endoh) over 14 years ago
=begin
遠藤です。
2010年5月6日20:02 Tanaka Akira akr@fsij.org:
2010年5月6日1:24 Yusuke Endoh redmine@ruby-lang.org:
2 について、現状は Thread#raise には以下のような race が存在します。
t1: begin 節を実行している
t2: t1.raise する
t1: rescue/ensure 節の実行を開始する
t3: t1.raise する
t1: rescue/ensure 節が実行されないまま再度例外が発生するこれを、Thread#raise の前に Mutex#lock するというルールにすれば、
race を避けて使うことができるようになります。と思います。なにか後始末が必要な処理があるとします。
たとえば、open したものは close しないといけないとして、
以下のコードを考えます。begin
f = open(filename)
ensure
f.close
endここで、open が終了した後、f に代入する前にコンテキストスイッチが起こると
どうでしょうか。
コンテキストスイッチの結果、他のスレッドが動いて、上記のコードを動かしている
スレッドを raise したとします。そうすると、ensure 節が実行されるわけですが、close はできません。
なんでかというと、open で生成した IO オブジェクトがどこにも
記録されていないからです。これは上記の Mutex#lock を使っても防げません。
一回しか Thread#raise していないからです。
Thread.raise を受け付けたらまずい期間は mutex をロックしておけ、という
ことで解決できないでしょうか。
Thread 1¶
begin
m.synchronize do
f = open(filename)
end
# ...
ensure
f.close if f
m.unlock
end
Thread 2¶
m.lock
th.raise
ちなみにこの race はシグナルにも存在します¶
POSIX シグナルでは、受け付けたシグナルは signal handler 内でマスクされるので、
handler 内で再度そのシグナルで割り込まれる、という心配はありません。
いえ、そういう心配ではないです。
Ctrl+C を連打した場合、ensure の実行開始直後でもう一回 Interrupt が
発生してしまうと、ensure 節の中身が実行されない可能性があるのでは
ないかという心配です。
ちゃんとは確認してないのですが、ひょっとしたら何か対策されている?¶
SIGINT が Interrupt 例外に自動変換されるのが問題なので、trap(:INT) など
で自力で対策すれば問題なくなると思います。
ところで、Ruby レベルの trap が実行中にシグナルを受け取ったら、別の
trap を実行してしまうことが (ささださんによると) あるらしいのですが、
まずいですかね。
--
Yusuke Endoh mame@tsg.ne.jp
=end
Updated by shyouhei (Shyouhei Urabe) over 14 years ago
- Status changed from Open to Assigned
=begin
=end
Updated by ko1 (Koichi Sasada) over 12 years ago
- Description updated (diff)
これ,どういう話で止まってるんでしたっけ....
なんか,いろんな話が混ざってる気がします.
Updated by kosaki (Motohiro KOSAKI) over 12 years ago
1)まず元々の提案は非同期例外を救うというモチベーションから来ているので、専用構文のほうがベターであろう。よってたぶんmutexの変更は不要
2)やるなら、semaphoeをつくるほうがいいなあ
Updated by ko1 (Koichi Sasada) about 12 years ago
- Status changed from Assigned to Rejected
小崎さん,ありがとうございます.
とりあえず,Mutex になんかするのは reject ということで,
すみませんが,整理して別 ticket としてください>遠藤さん