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
Process.kill(:TERM, Process.pid) kills the process instead of raising SignalException — ~77 examples behind one defect #691
raised SignalException: SIGTERM signo=15 then still alive
0, all three runs
rbgo
(nothing) ×2, NO RAISE - kept running ×1
143, all three runs
Two things are wrong, and the second is the surprising one:
The signal is never turned into a rescuable Ruby exception — the process just
dies with 128+15.
On the one run in three where the raise path was skipped and the program
printed and continued, it still exited 143. So the death is not only "the
raise is missing"; the signal disposition itself terminates regardless of what
Ruby code does afterwards.
It is racy, which is why it has been easy to miss: 1-in-5 on one sweep,
0-in-5 on another. It is also the entire ±1 difference between two otherwise
identical ratchet readings, so it has been showing up as measurement noise rather
than as a defect.
≈77 examples sit behind this one defect
Nine spec files currently report nothing at all — not failures, a silent
exit. Measured as what MRI 4.0.5 reaches through our own shim, so these are
examples our harness can actually score today:
file
MRI through our shim
core/signal/trap_spec.rb
36
core/signal/signal_exception_spec.rb
12
core/kernel/exit_spec.rb
12
core/process/exit_spec.rb
8
core/mutex/lock_spec.rb
4
core/signal/interrupt_spec.rb
3
core/signal/signm_spec.rb
1
core/signal/signo_spec.rb
1
total
≈77
A tenth file, core/string/unpack/carret_spec.rb, yields nothing for MRI under
our shim either — that one is a shim gap, not this defect. Worth stating, because
grouping it in would have inflated the claim.
This is the largest single lever identified today by some margin, and it is one
defect rather than a subsystem: SignalException already exists as a class, and Signal.trap already has a binding.
Owning files: internal/vm/process.go:306 (Process.kill) and internal/vm/signal.go (Signal.trap).
Related, and probably the same boundary
#676 — a deliberate exit 3 also fails to reach the process (rbgo exits 0 where
MRI exits 3), and there the VM half was entirely correct while the front-end
never translated the terminal SystemExit. This is the mirror image: the signal
reaches the process when it should have become an exception. Both live at the
boundary between the Go runtime's process state and the VM's exception
machinery, and it is worth fixing them together rather than one at a time.
Two corrections to figures and paths in this issue, both mine.
1. Four of the file paths are wrong. These are under core/exception/, not core/signal/:
core/exception/signal_exception_spec.rb
core/exception/interrupt_spec.rb
core/exception/signm_spec.rb
core/exception/signo_spec.rb
2. "≈77 examples" was the MRI ceiling, not what this defect withholds. The two
are different quantities and I conflated them. Measured after the fix in #693:
recovered by fixing signals + exit status
58
behind a different defect in internal/vm/thread.go
24
needs Thread#group
1
3. Three of the nine files were never failing — they TIME OUT, before and
after, and still do: core/kernel/exit_spec.rb, core/process/exit_spec.rb, core/mutex/lock_spec.rb. They were only distinguishable because the measuring
tool separates TIMEOUT from a zero result from a missing RBGO_RESULT line. A
file reporting nothing has at least three causes here and they need different
fixes.
Those 24 examples have a single cause, and it is not a mutex defect: an uncaught SystemExit in a non-main thread must reach the main thread and interrupt a
blocking sleep, which rbgo does not do.
ready=falset=Thread.new{Thread.passuntilready;exit42}begin;ready=true;sleeprescueSystemExit;puts"main saw SystemExit";end# MRI prints; rbgo hangs for ever
Recursive and cross-fiber deadlock detection were checked and already match MRI
(both raise ThreadError matching /deadlock/), so core/mutex/lock_spec.rb
belongs to the thread-interruption family rather than to Mutex.
That work is in flight against internal/vm/thread.go, together with the two
defects routed from the shim repair (#692) and the TestServerTLSLoopback hang
(#695) — all four are candidates for one cause: a blocking operation that does not
yield control as MRI's does.
Activity
tannevaled commentedon Sep 26, 2026
Two corrections to figures and paths in this issue, both mine.
1. Four of the file paths are wrong. These are under
core/exception/, notcore/signal/:core/exception/signal_exception_spec.rbcore/exception/interrupt_spec.rbcore/exception/signm_spec.rbcore/exception/signo_spec.rb2. "≈77 examples" was the MRI ceiling, not what this defect withholds. The two
are different quantities and I conflated them. Measured after the fix in #693:
internal/vm/thread.goThread#group3. Three of the nine files were never failing — they TIME OUT, before and
after, and still do:
core/kernel/exit_spec.rb,core/process/exit_spec.rb,core/mutex/lock_spec.rb. They were only distinguishable because the measuringtool separates TIMEOUT from a zero result from a missing
RBGO_RESULTline. Afile reporting nothing has at least three causes here and they need different
fixes.
Those 24 examples have a single cause, and it is not a mutex defect: an uncaught
SystemExitin a non-main thread must reach the main thread and interrupt ablocking
sleep, which rbgo does not do.Recursive and cross-fiber deadlock detection were checked and already match MRI
(both raise
ThreadErrormatching/deadlock/), socore/mutex/lock_spec.rbbelongs to the thread-interruption family rather than to
Mutex.That work is in flight against
internal/vm/thread.go, together with the twodefects routed from the shim repair (#692) and the
TestServerTLSLoopbackhang(#695) — all four are candidates for one cause: a blocking operation that does not
yield control as MRI's does.
signal+exit: a self-directed signal becomes a Ruby exception, and a d…