#Mac OSX で Gauche-0.9.1 の make test を実行したときに、どこかで止まったようで、中断した覚えがあるのですが、それって使用上の問題になったりするのでしょうか?確か thread だったと思います。
#threadのテストで止まる現象は私も数回見たことがあるのですが、再現できなくて究明していません。Gaucheのバグの可能性と、テストの書き方が悪い可能性があります。
#おはようございます。職場のSolarisもアップデートし、Solaris Expressにしました。で、コンパイルしてみたところ、こちらではgauche.process以外ではmake testも全部通りました。
#(いちおう、もう寝惚けてないつもりです...)
#gauche.processのテストについて若干改良してみました (r7376)。機会があるときに試してみてください>(もと)さん
#r7376試してみました。make test の結果、 0 failed, 0 aborted でした。ありがとうございます。
#よっしゃ。あとはselectのテストの調整かな。
#こちらの環境では3回実行して、3回とも止まりました。取り敢えず気にしないことにします。回答有難うございました。> 再現できなくて究明していません。
#む、そこまで再現性が高いと怪しいですね。どのテストで止まってるかわかりますか? ext/threads以下でmake checkして、中断した後test.logを見てください。
#$ uname -psr
Darwin 8.11.0 powerpc
$ gcc --version
powerpc-apple-darwin8-gcc-4.0.1 (GCC) 4.0.1 (Apple Computer, Inc. build 5370)
Copyright (C) 2005 Free Software Foundation, Inc.
$ LDFLAGS='-L/opt/lib' CPPFLAGS='-I/opt/include' ./configure --enable-multibyte=utf-8 --prefix=/var/tmp/GaucheTest
$ make
$ cd ext/threads
$ make check
GAUCHE_TEST_RECORD_FILE=../../test.record ../../src/gosh -ftest -I. test.scm > test.log
Testing threads ...
$ cat test.log
Testing threads ===============================================================
testing bindings in #<module gauche.threads> ... ok
<basic thread API>-------------------------------------------------------------
test current-thread, expects #t ==> ok
test thread?, expects (#t #f) ==> ok
test make-thread, expects #t ==> ok
test thread-name, expects foo ==> ok
test thread-specific, expects "hello" ==> ok
test thread-start!, expects "hello" ==> ok
test thread-join!, expects 1346269 ==> ok
test thread-status, expects new ==> ok
test thread-status, expects runnable ==> ok
test thread-status, expects stopped ==> ok
test thread-status, expects stopped ==> ok
test thread-status, expects runnable ==> ok
test thread-status, expects terminated ==>
#実行してみました。OS とコンパイラが古いことが原因かもしれません。
#Fink でインストールした GCC 4.4.4 でも同じことになっていたことを思い出しました。OS は同じですが。
#>zzz そこで止まるということはpthread_cancel()がうまく動いてないですね。以前のOSXには確かにpthread_cancel()が特定のシステムコールで無視されるという問題があったはず。
#最近のOSXではどうなんでしょ > Macな方
#気になったので Gauche-0.8.14 で実行してみましたが、特に問題は無いようでした。念のために補足しますと Darwin 8.11.0 powerpc というのは PowerPC 版の 10.4 のことです。
#$ LDFLAGS='-L/opt/lib' CPPFLAGS='-I/opt/include' ./configure --enable-multibyte=utf-8 --prefix=/var/tmp/Gauche && make && cd ext/threads/ && make check
$ cat test.log
Testing threads ===============================================================
testing bindings in #<module gauche.threads> ... ok
<basic thread API>-------------------------------------------------------------
test current-thread, expects #t ==> ok
test thread?, expects (#t #f) ==> ok
test make-thread, expects #t ==> ok
test thread-name, expects foo ==> ok
test thread-specific, expects "hello" ==> ok
test thread-start!, expects "hello" ==> ok
test thread-join!, expects 1346269 ==> ok
<thread and error>-------------------------------------------------------------
test uncaught-exception, expects #t ==> ok
test uncaught-exception, expects #t ==> ok
test uncaught-exception, expects #t ==> ok
<basic mutex API>--------------------------------------------------------------
test make-mutex, expects #t ==> ok
test mutex-name, expects foo ==> ok
test mutex-specific, expects "hoge" ==> ok
test lock and unlock - no blocking, expects #t ==> ok
test mutex-state, expects (not-abandoned #<thread "root" runnable 0x8de10> not-owned not-abandoned) ==> ok
test lock and unlock - blocking (simple spin-lock), expects ((put a) (get a) (put b) (get b) (put c) (get c)) ==> ok
test lock with timeout, expects (#t #f #f #f #f #t #t) ==> ok
test recursive mutex, expects (#<thread "root" runnable 0x8de10> 0 not-abandoned) ==> ok
<condition variables>----------------------------------------------------------
test make-condition-variable, expects #t ==> ok
test condition-varaible-name, expects foo ==> ok
test condition-variable-specific, expects "hello" ==> ok
test condition-variable-signal!, expects ((put a) (get a) (put b) (get b) (put c) (get c)) ==> ok
<port access serialization>----------------------------------------------------
test write to file, buffered, expects #t ==> ok
test write to file, line-buffered, expects #t ==> ok
test write to string, expects #t ==> ok
test
#0.8.14では単にpthread_cancelのテストが入ってないだけです。実際のthread-terminate!まわりのコードは大して変わってないはず。
##もっともpthread_cancel自体が危険な操作ではあるからな… 別手段を用意すべきかもしれない (シグナルひとつ予約してpthread_killでやりとりするとか、Gaucheの中で呼んでるpthread_cond_waitについては常にタイムアウト使うようにしてpthread_testcancelするとか。すぐにできる変更ではないけれど)
#そういうことでしたか。> 0.8.14では単にpthread_cancelのテストが入ってないだけです。
#最初の例では --enable-threads=pthreads を付けていなかったので、このオプションを付けて 0.9.1 でやり直しましたが結果は同じでした。
#うは、Gaucheのthread-terminate!はpthread_cancel()使っているんでしたか。
#SRFIはPOSIXのpthread_cancel()を意識してthread-terminate!をいれたのかもしれないけど、もとのpthread_cancel()がろくに使えない(スレッドがどのタイミングでキャンセルされていいように書くのが難しすぎる)ので、これは使えないなぁと思っていました。
#ポータブルに他スレッドを終わらせるのにそれしかないんで (特に他スレッドがブロックしてるとき)
#pthread_kill使ってthread-terminate!実装するというのがよさそう
#ただ、いきなりpthread_cancel()は確かにキツいんで、もうちょっとgracefulにすることは考えています。もうコードが入ってるかもしれないけど、Gauche内の仕組みとしてVMからVMへ一種のメッセージを送れるので、まずそれを使って「終わってね」とリクエストする。
#で、それに対して応答が無かったら強行手段に出ると。
#Gauche的にはSchemeで書いたコードからVMの内部状態を壊すことはできないというのがポリシーですよね?
#pthread_kill()はシグナルをひとつGauche側で予約しないとならないのが、汎用ライブラリを指向する点でネック。Boehm GCで既に2つ使ってるし。
#そうだとするとシグナルと同じようにVMの安全なポイントまでキャンセルを遅らせるしかなくて、そうするとpthread_cancelではなくてpthread_killを使わざるを得ないような
#VMの大部分はownerしか触れないので、ownerが不在になるterminate以降は、そういうプライベートな部分は壊れても仕方ないです。
#VMのパブリックな部分についてはハザードがあるかも。
#しかしたとえばクラス再定義を実行しているスレッドがpthread_cancelされてしまったら?
#VMの状態は壊れないんじゃないかな。クラス再定義についてはcancel以前に、そもそも再定義中のスレッドがエラーで中断したっておかしなことになります。そういう操作だということです。
#なにかグローバルなロックを獲得するような操作は結構なかったでしたっけ?
#pthread_cond_wait()がシグナルを受けて中断することは保証されてないので、pthread_kill()を使っても、安全な場所までVMを進めることができるとは限らないです。
#クラス再定義についてはグローバルなロックを獲得しますね。Schemeレベルのロックなら、握ったまま死なれた場合、それを獲得に行った別スレッドでabandoned-mutex例外になるので一応ハンドリングは可能 (だけど中途半端なクラス定義状態についてはどっちにせよ回復不可能)
#pthread_cancel()はWindows nativeでもうまくなさそうなので、そのへんは考え直す予定。pthread_cond_waitについては上で書いたように常にtimeoutするようにして状態チェックのコードを入れれば回避可能かなとは思ってます。
#そもそもthread-terminate!、システムコールでブロックしているときにまで中断する必要あるのでしょうか?
#まあシグナルについても、スレッド基本なこの時代にはアプリケーションレベルでそれほど活用される感じではないので、言語ランタイムでひとつ使っちゃってもいいかなって気はする。
#>Rui いや、そもそも、システムコールでブロックしてるようなスレッドを中断させる最終手段がthread-terminate!だと認識しています。通常系で使うコールではない。
#なるほど、たとえばマルチスレッドのサーバーで1リクエストでずっと刺さりっぱなしのスレッドを無理やり終了させるとか、そういうユースケースですかね
#今のGaucheのランタイムだと、thread-stop! というのがあって、これはユーザレベルでVMにメッセージを送るんで、相手のスレッドがVMを実行していれば(ブロックしてなければ)止められます。graceful terminationのシナリオとして、このメカニズムでまず止めることを試みるようにしようかと。