diff options
| author | Luke Gruber <luke.gruber@shopify.com> | 2025-12-04 16:51:11 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-12-04 16:51:11 -0500 |
| commit | 8d8159e7d87e4fd1594ce2fad3d2653e47fb1026 (patch) | |
| tree | c11cf0064acddc8b0b3f3f4332565e3e649dee1e /thread_pthread.h | |
| parent | 7d9558f99b5bb8279c138860670b56735a28a3e4 (diff) | |
Fix thread scheduler issue with thread_sched_wait_events (#15392)
Fix race between timer thread dequeuing waiting thread and thread
skipping sleeping due to being dequeued. We now use `th->event_serial` which
is protected by `thread_sched_lock`. When a thread is put on timer thread's waiting
list, the event serial is saved on the item. The timer thread checks
that the saved serial is the same as current thread's serial before
calling `thread_sched_to_ready`.
The following script (taken from a test in `test_thread.rb` used to crash on
scheduler debug assertions. It would likely crash in non-debug mode as well.
```ruby
def assert_nil(val)
if val != nil
raise "Expected #{val} to be nil"
end
end
def assert_equal(expected, actual)
if expected != actual
raise "Expected #{expected} to be #{actual}"
end
end
def test_join2
ok = false
t1 = Thread.new { ok = true; sleep }
Thread.pass until ok
Thread.pass until t1.stop?
t2 = Thread.new do
Thread.pass while ok
t1.join(0.01)
end
t3 = Thread.new do
ok = false
t1.join
end
assert_nil(t2.value)
t1.wakeup
assert_equal(t1, t3.value)
ensure
t1&.kill&.join
t2&.kill&.join
t3&.kill&.join
end
rs = 30.times.map do
Ractor.new do
test_join2
end
end
rs.each(&:join)
```
Diffstat (limited to 'thread_pthread.h')
| -rw-r--r-- | thread_pthread.h | 1 |
1 files changed, 1 insertions, 0 deletions
diff --git a/thread_pthread.h b/thread_pthread.h index d635948c4b..f579e53781 100644 --- a/thread_pthread.h +++ b/thread_pthread.h @@ -39,6 +39,7 @@ struct rb_thread_sched_waiting { #else uint64_t timeout; #endif + uint32_t event_serial; int fd; // -1 for timeout only int result; } data; |
