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:
Samuel Williams 2021-07-19 19:21:46 +12:00
parent cd49940cff
commit 13f8521c63
Notes: git 2021-07-27 15:23:58 +09:00
4 changed files with 55 additions and 13 deletions

View file

@ -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

View file

@ -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