-
Notifications
You must be signed in to change notification settings - Fork 106
Description
When WEBrick shutdowns, it tries to concurrently write and close a file descriptor, and even tries to close it from multiple threads:
closing it from the main webrick thread:
Line 207 in 841b7da
cleanup_shutdown_pipe(shutdown_pipe) |
closing it from an arbitrary thread:
Line 237 in 841b7da
alarm_shutdown_pipe(&:close) |
writing to it (from an arbitrary thread):
Line 227 in 841b7da
alarm_shutdown_pipe {|f| f.write_nonblock("\0")} |
A hack:
Line 353 in 841b7da
rescue IOError # closed by another thread. |
The problem is if the write_nonblock
which calls write(2) ends up happening once the fd
is close(2)d then it's EBADF, or worse writing to the wrong file descriptor.
This became such an issue that ruby/spec stopped using WEBrick and rewrote to make its own HTTP server to avoid this issue. Also the commit message of ruby/spec@d8ead5d may be interesting.
Only one thread (e.g. the main webrick thread) should close it, and it should wait all sub-threads before closing it so there are concurrent writes to the close.
CRuby has some very complex logic in IO#close which avoids the issue in most cases but it's not clear if it's fully reliable: https://ruby.slack.com/archives/C02A3SL0S/p1636604027275700?thread_ts=1636592668.266300&cid=C02A3SL0S
IIRC I've seen it fail for ruby/spec too on CRuby.
cc @ioquatix