mirror of
https://github.com/ruby/ruby.git
synced 2025-09-22 03:53:58 +02:00
Fix potential hang when joining threads.
If the thread termination invokes user code after `th->status` becomes `THREAD_KILLED`, and the user unblock function causes that `th->status` to become something else (e.g. `THREAD_RUNNING`), threads waiting in `thread_join_sleep` will hang forever. We move the unblock function call to before the thread status is updated, and allow threads to join as soon as `th->value` becomes defined.
This commit is contained in:
parent
cd49940cff
commit
13f8521c63
Notes:
git
2021-07-27 15:23:58 +09:00
4 changed files with 55 additions and 13 deletions
|
@ -112,8 +112,10 @@ class Scheduler
|
|||
|
||||
self.run
|
||||
ensure
|
||||
@urgent.each(&:close)
|
||||
@urgent = nil
|
||||
if @urgent
|
||||
@urgent.each(&:close)
|
||||
@urgent = nil
|
||||
end
|
||||
|
||||
@closed = true
|
||||
|
||||
|
@ -240,3 +242,13 @@ class BrokenUnblockScheduler < Scheduler
|
|||
raise "Broken unblock!"
|
||||
end
|
||||
end
|
||||
|
||||
class SleepingUnblockScheduler < Scheduler
|
||||
# This method is invoked when the thread is exiting.
|
||||
def unblock(blocker, fiber)
|
||||
super
|
||||
|
||||
# This changes the current thread state to `THREAD_RUNNING` which causes `thread_join_sleep` to hang.
|
||||
sleep(0.1)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -66,4 +66,18 @@ class TestFiberThread < Test::Unit::TestCase
|
|||
thread.join
|
||||
end
|
||||
end
|
||||
|
||||
def test_thread_join_hang
|
||||
thread = Thread.new do
|
||||
scheduler = SleepingUnblockScheduler.new
|
||||
|
||||
Fiber.set_scheduler scheduler
|
||||
|
||||
Fiber.schedule do
|
||||
Thread.new{sleep(0.01)}.value
|
||||
end
|
||||
end
|
||||
|
||||
thread.join
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue