You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
0.19 deadlocks on JRuby: the websocket handshake write never returns #676
0.19.0/0.19.1 hang on JRuby before the browser is usable — Ferrum::Browser.new alone is enough. The main thread is stuck in the very first write to the CDP socket, the websocket upgrade request (207 bytes), and the call to write_nonblock never returns:
#<Thread:0x...@main run>
ferrum-0.19.1/lib/ferrum/client/web_socket.rb:153:in `write'
websocket-driver-0.8.0-java/lib/websocket/driver/client.rb:55:in `start'
ferrum-0.19.1/lib/ferrum/client/web_socket.rb:54:in `block in initialize'
#<Thread:0x... sleep> # the reader started one line earlier
ferrum-0.19.1/lib/ferrum/client/web_socket.rb:208:in `readpartial'
The change it comes from is 080aadc (#644), which replaced the plain @sock.write in Client::WebSocket#write with a write_nonblock(data, exception: false) + IO.select loop so a stalled browser can't hold the send lock forever. Prepending the 0.18 implementation back onto Ferrum::Client::WebSocket is enough to make 0.19.1 work — same script, same gems, page open in 0.9s. 0.17.1 and 0.18 are fine as released.
The platform difference behind it: on JRuby, a socket that another thread is blocked reading with readpartial takes no writes from other threads, while CRuby writes straight through. ferrum starts its reader thread immediately before the handshake is written, so the new loop meets exactly that state. A reduced TCPSocket-pair script blocks on the plain write too, so I can't claim that's the whole mechanism — but the replacement above is reproducibly the dividing line.
Also on GitHub Actions ubuntu-latest with jruby-9.4: after cuprite 0.19 / ferrum 0.19.1 were released, rails_admin's JRuby jobs stopped finishing — this one ran out the 6-hour default timeout with nothing printed, 4 attempts in a row, while every CRuby job stayed green on the same lockfile. With cuprite pinned to < 0.19 the same job finishes in 8 minutes.
JRuby isn't in your test matrix, so this is a heads-up rather than a complaint. Happy to send a PR if you'd like one — e.g. taking the nonblocking path only when a timeout was actually passed, or falling back to a plain write on JRuby.
@mshibuya thanks for the detailed report, it was spot on. Reproduced it on JRuby 10.1.2: IO#readpartial waits in a NIO select holding the channel's blocking lock, and write_nonblock needs that same lock, so the handshake write never returns.
The fix is on the 0-19-stable branch, which will become 0.19.2. #644 is reverted and frames are now written by a dedicated writer thread with plain blocking writes, so the socket is only read by one thread and written by another, and a stalled browser still can't freeze the client.
Could you give it a try on JRuby as soon as you can?
I'll go ahead and merge this #680
It's next planned 0.20 release that I was going to ship, but this one 0-19-stable is going to be 0.19.2 if all works.
Yep, thanks! I released 0.19.2 so you can start using it, fix is also going to be included in 0.20. Meanwhile I noticed you pass flatten: false... I'll open a draft for it, with explanation
Activity
Keep the browser driver off a version the suite hangs on
Keep the browser driver off a version the suite hangs on
Keep the browser driver off a version the suite hangs on
route commentedon Oct 26, 2026
@mshibuya thanks for the detailed report, it was spot on. Reproduced it on JRuby 10.1.2:
IO#readpartialwaits in a NIO select holding the channel's blocking lock, andwrite_nonblockneeds that same lock, so the handshake write never returns.The fix is on the
0-19-stablebranch, which will become 0.19.2. #644 is reverted and frames are now written by a dedicated writer thread with plain blocking writes, so the socket is only read by one thread and written by another, and a stalled browser still can't freeze the client.Could you give it a try on JRuby as soon as you can?
I'll release 0.19.2 once you confirm it works for you.
route commentedon Oct 26, 2026
I'll go ahead and merge this #680
It's next planned 0.20 release that I was going to ship, but this one
0-19-stableis going to be0.19.2if all works.mshibuya commentedon Oct 26, 2026
Thank you for the quick fix!
Verified that your change works, as you saw in the draft rails_admin PR you opened 👍
route commentedon Oct 26, 2026
Yep, thanks! I released
0.19.2so you can start using it, fix is also going to be included in0.20. Meanwhile I noticed you passflatten: false... I'll open a draft for it, with explanationfix(client): write websocket frames from a writer thread, fixing the …