summaryrefslogtreecommitdiff
path: root/test/fiber
diff options
context:
space:
mode:
authorSamuel Williams <samuel.williams@oriontransfer.co.nz>2022-10-20 13:38:52 +1300
committerGitHub <noreply@github.com>2022-10-20 13:38:52 +1300
commit7f175e564875b011efb43537907867dd08d659e8 (patch)
treeb382a58d414e0c0014285958099a531d35e80086 /test/fiber
parentd9d9005a3a31d0df0b5432eba5d6f2b9bd647cb1 (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/fiber')
-rw-r--r--test/fiber/scheduler.rb11
-rw-r--r--test/fiber/test_scheduler.rb42
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