Bug #968
trap may fail to handle signals
| Status: | Closed | Start date: | 01/02/2009 | |
|---|---|---|---|---|
| Priority: | Normal | Due date: | ||
| Assignee: | % Done: | 100% |
||
| Category: | YARV | |||
| Target version: | 1.9.1 RC2 | |||
| ruby -v: |
Description
遠藤です。
シグナルが連続して送られたとき、シグナルを読み落とすことが
あるようです。具体的には、以下のプログラムが停止しません。
./ruby -e '
n = 0
trap(:USR1) { n += 1 }
30.times { Process.kill(:USR1, $$) }
until n == 30
1000**100000 # wait
p n
end
'
これは仕様でしょうか。
仕様でなければ、以下のようなパッチでどうでしょうか。
Index: thread.c
===================================================================
--- thread.c (revision 21269)
+++ thread.c (working copy)
@@ -1145,6 +1145,10 @@
void
rb_thread_execute_interrupts(rb_thread_t *th)
{
+ if (GET_VM()->main_thread == th) {
+ while (rb_signal_buff_size() && !th->exec_signal) native_thread_yield();
+ }
+
if (th->raised_flag) return;
while (th->interrupt_flag) {
@@ -2438,13 +2442,14 @@
{
rb_vm_t *vm = GET_VM(); /* TODO: fix me for Multi-VM */
int sig;
+ rb_thread_t *mth;
/* for time slice */
RUBY_VM_SET_TIMER_INTERRUPT(vm->running_thread);
/* check signal */
- if ((sig = rb_get_next_signal()) > 0) {
- rb_thread_t *mth = vm->main_thread;
+ mth = vm->main_thread;
+ if (!mth->exec_signal && (sig = rb_get_next_signal()) > 0) {
enum rb_thread_status prev_status = mth->status;
thread_debug("main_thread: %s, sig: %d\n",
thread_status_name(prev_status), sig);
--
Yusuke ENDOH <mame@tsg.ne.jp>
Associated revisions
* thread.c (rb_thread_execute_interrupts): if signal is already
buffered, main thread should wait until timer thread delivers it.
* thread.c (timer_thread_function): should defer delivery of a signal
if main thread does not yet trap a previous one. [ruby-dev:37676]
History
Updated by mame (Yusuke Endoh) over 3 years ago
遠藤です。
補足です。
シグナルが連続して送られたとき、その回数だけシグナルハンドラが呼ばれる
という保障は OS レベルで存在しないと聞きました。
ただ今の Ruby は、せっかくシグナルハンドラが呼ばれた場合でも trap を
呼び損ねる場合があります。
また、種類が異なるシグナルでも trap を呼び損ねる場合があるのですが、
Unix ではこれすら保障されないのでしょうか。
$ ./ruby -e '
n = 0
trap(:USR1) { n += 1 }
trap(:USR2) { n += 1 }
pid = $$
fork do
sleep 0.5
p "send start"
Process.kill(:USR1, pid)
Process.kill(:USR2, pid)
p "send end"
end
p "calc start"
1000**100000 # wait
p "calc end"
p n
'
"calc start"
"send start"
"send end"
"calc end"
1
現在 test-all の TestSignal#test_exit_action が結構な確率で失敗しますが、
これも同じ問題のようで、前述のパッチを当てると通るようになります。
私の環境で高確率で 0F0E になるので、うれしいです (Rinda のテストが
稀に失敗しますが) 。
というわけで、積極的な反対がなければとりあえずコミットしてしまおう
と思います。
--
Yusuke ENDOH <mame@tsg.ne.jp>
Updated by mame (Yusuke Endoh) over 3 years ago
- Status changed from Open to Closed
- % Done changed from 0 to 100
Applied in changeset r21582.