Skip to content

Keep a stub WEBrick from hanging the suite on Ruby 4.0 - #669

Merged
tricknotes merged 1 commit into
mainfrom
claude/github-actions-error-investigation-8nkr1o
Sep 5, 2026
Merged

Keep a stub WEBrick from hanging the suite on Ruby 4.0#669
tricknotes merged 1 commit into
mainfrom
claude/github-actions-error-investigation-8nkr1o

Conversation

@tricknotes

Copy link
Copy Markdown
Owner

Problem

CI jobs on Ruby 4.0 intermittently stop producing output after an ember build and run until the 15-minute cap cancels them, e.g. run 33876168453 (build (4.0, 8.1, 7.0.0) and build (4.0, main, 7.0.0)), and before it runs 33875447042, 33871839705, 33869563469 and 33869331315. Every cancelled job in those runs is a Ruby 4.0 job; no Ruby 3.3 / 3.4 job ever hung. The --disable-dev-shm-usage / :js timeout from #668 did not stop it (the run above is the merge of #668).

Cause

Reproduced locally with Ruby 4.0.6 and a watchdog that dumps every thread's backtrace once an example runs for 90 seconds. The main thread was stuck here:

spec/lib/ember_cli/dev_server_spec.rb:246:in 'Thread#join'
spec/lib/ember_cli/dev_server_spec.rb:246:in 'NullServer#shutdown'
spec/lib/ember_cli/dev_server_spec.rb:258:in 'block (2 levels) in <top (required)>'   # after { servers.each(&:shutdown) }

while the WEBrick thread it joins sat in IO.select inside WEBrick::GenericServer#start with status == :Running.

NullServer#listen starts WEBrick with Thread.new { @server.start } and returns at once; the example's after hook then calls GenericServer#shutdown and joins the thread. GenericServer#shutdown only works once #start has set up its shutdown pipe (stop only flips :Running to :Shutdown, and alarm_shutdown_pipe does nothing while @shutdown_pipe is nil). When the thread has not run yet, shutdown is a no-op, the server boots afterwards and loops in IO.select forever, and the join never returns. On Ruby 4.0 a freshly created thread usually has not run by the time the example finishes.

Measured with a standalone loop of the same steps (boot WEBrick in a thread, connect once, shutdown, join with a 2s limit):

Ruby hangs
4.0.6 187 / 300
3.3.6 0 / 300

The :js timeout from #668 cannot catch this: these are plain examples. And the progress dots of the examples that ran after the last ember build never reached the job log, because the Actions runner only flushes complete lines, which is why the jobs looked stuck right after the build output.

Fix

NullServer#listen now waits until WEBrick reports :Running (with a 5-second limit) before returning, so shutdown always reaches a server loop that can be stopped.

Verification

Ruby 4.0.6, Rails 8.1, Ember 7.0.0:

before after
rspec spec/lib/ember_cli/dev_server_spec.rb, 25 runs with a 25s limit 23 hung 0 hung
bin/rake (full suite) 148 examples, 0 failures

Also green on Ruby 3.3.6.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FRn3BbAGhG1oWGbNkSHkvA


Generated by Claude Code

CI jobs on Ruby 4.0 intermittently produced no output after an `ember build`
until the 15-minute cap cancelled them (e.g. run 33876168453, jobs
`build (4.0, 8.1, 7.0.0)` and `build (4.0, main, 7.0.0)`); no job on
Ruby 3.3 or 3.4 ever did.

The hang is in `spec/lib/ember_cli/dev_server_spec.rb`: `NullServer#listen`
starts WEBrick in a new thread and returns immediately, and the example's
`after` hook then calls `GenericServer#shutdown` and joins the thread.
`shutdown` is a no-op until `#start` has set up its shutdown pipe, so when
the thread has not run yet the server boots afterwards and loops in
`IO.select` forever, and the join never returns. On Ruby 4.0 a new thread
usually has not run by then: a standalone loop of the same steps hung
187 of 300 times on Ruby 4.0.6 and 0 of 300 times on Ruby 3.3.6, and the
spec file alone hung 23 of 25 runs on Ruby 4.0.6 before this change and
0 of 25 after it.

The `:js` timeout added in #668 could not catch this because these are
plain examples, and their progress dots never reached the log: the runner
only flushes complete lines, which is why the jobs looked stuck right
after the build output.

Wait until WEBrick reports `:Running` before `listen` returns, so that
`shutdown` always reaches a server loop that can be stopped.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRn3BbAGhG1oWGbNkSHkvA
@tricknotes
tricknotes merged commit dd38857 into main Sep 5, 2026
24 checks passed
@tricknotes
tricknotes deleted the claude/github-actions-error-investigation-8nkr1o branch September 5, 2026 13:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants