diff options
author | Samuel Williams <samuel.williams@oriontransfer.co.nz> | 2022-10-20 13:38:52 +1300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-20 13:38:52 +1300 |
commit | 7f175e564875b011efb43537907867dd08d659e8 (patch) | |
tree | b382a58d414e0c0014285958099a531d35e80086 /test | |
parent | d9d9005a3a31d0df0b5432eba5d6f2b9bd647cb1 (diff) |
Avoid missed wakeup with fiber scheduler and Fiber.blocking. (#6588)
* Ensure that blocked fibers don't prevent valid wakeups.
Notes
Notes:
Merged-By: ioquatix <samuel@codeotaku.com>
Diffstat (limited to 'test')
-rw-r--r-- | test/fiber/scheduler.rb | 11 | ||||
-rw-r--r-- | test/fiber/test_scheduler.rb | 42 |
2 files changed, 53 insertions, 0 deletions
diff --git a/test/fiber/scheduler.rb b/test/fiber/scheduler.rb index 3fd41ef6f1..204a297133 100644 --- a/test/fiber/scheduler.rb +++ b/test/fiber/scheduler.rb @@ -350,3 +350,14 @@ class SleepingUnblockScheduler < Scheduler sleep(0.1) end end + +class SleepingBlockingScheduler < Scheduler + def kernel_sleep(duration = nil) + # Deliberaly sleep in a blocking state which can trigger a deadlock if the implementation is not correct. + Fiber.blocking{sleep 0.0001} + + self.block(:sleep, duration) + + return true + end +end diff --git a/test/fiber/test_scheduler.rb b/test/fiber/test_scheduler.rb index 5a24bff04f..300d30ad63 100644 --- a/test/fiber/test_scheduler.rb +++ b/test/fiber/test_scheduler.rb @@ -138,4 +138,46 @@ class TestFiberScheduler < Test::Unit::TestCase Object.send(:remove_const, :TestFiberSchedulerAutoload) end end + + def test_deadlock + mutex = Thread::Mutex.new + condition = Thread::ConditionVariable.new + q = 0.0001 + + signaller = Thread.new do + loop do + mutex.synchronize do + condition.signal + end + sleep q + end + end + + i = 0 + + thread = Thread.new do + scheduler = SleepingBlockingScheduler.new + Fiber.set_scheduler scheduler + + Fiber.schedule do + 10.times do + mutex.synchronize do + condition.wait(mutex) + sleep q + i += 1 + end + end + end + end + + # Wait for 10 seconds at most... if it doesn't finish, it's deadlocked. + thread.join(10) + + # If it's deadlocked, it will never finish, so this will be 0. + assert_equal 10, i + ensure + # Make sure the threads are dead... + thread.kill + signaller.kill + end end |