Actions
Bug #19557
openDeadlock on STDOUT(ERR) lock on signal handler
Description
The following Ruby code produces deadlock; recursive locking (ThreadError)
.
It means some IO operations (puts
, ...) is not used on trap handlers safely.
trap(:USR1){puts 'world'}
Thread.new{loop{Process.kill(:USR1, $$); sleep 0.5}}
loop{puts 'hello'}
...
hello
hello
hello
hello
hello
../../src/clean/test.rb:2:in `write': deadlock; recursive locking (ThreadError)
from ../../src/clean/test.rb:2:in `puts'
from ../../src/clean/test.rb:2:in `puts'
from ../../src/clean/test.rb:2:in `block in <main>'
from ../../src/clean/test.rb:4:in `write'
from ../../src/clean/test.rb:4:in `puts'
from ../../src/clean/test.rb:4:in `puts'
from ../../src/clean/test.rb:4:in `block in <main>'
from <internal:kernel>:187:in `loop'
from ../../src/clean/test.rb:4:in `<main>'
Reason¶
-
puts()
callsrb_io_writev()
- it calls
IO#write
- it calls
io_write_m()
- it calls
io_writev()
- it calls
io_fwritev()
- it calls
io_binwritev()
- it calls
rb_mutex_synchronize()
withio_binwritev_internal()
- STDOUT's
fptr->write_lock
is acquired here
- STDOUT's
-
io_binwritev_internal()
callsrb_writev_internal()
- it calls
rb_thread_io_blocking_region()
withinternal_writev_func()
Here, internal_writev_func()
can be interrupted by signals and if a trap handler is registered, call the trap handler (written in Ruby, puts 'world'
in this case) and call Kernel#puts()
and fptr->write_lock
is already acquired -> deadlock; recursive locking
.
Ideas¶
I'm not sure why fptr->write_lock
is needed, but if there is no internal consistency issue, we can make fptr->write_lock
nil
at least for STDOUT/ERR.
Another idea is, calling trap handlers (or other interruptible Ruby code such as finalizers and so on) after releasing the lock. But I'm not sure it is feasible.
No data to display
Actions
Like0