From c8e68b3f2061d6a5dfc220cd60b6a75c646e464b Mon Sep 17 00:00:00 2001 From: Sampo Kuokkanen Date: Fri, 21 Aug 2026 12:42:22 +0900 Subject: [PATCH] Add specs for Fiber.schedule and Fiber.current_scheduler Fiber.schedule and Fiber.current_scheduler had no specs. Cover the RuntimeError raised when no scheduler is set, that the scheduler's #fiber hook is called and its return value is what Fiber.schedule hands back, and that Fiber.schedule works from inside a non-blocking Fiber. Fiber.current_scheduler returns the scheduler only when the current Fiber is non-blocking, which is what separates it from Fiber.scheduler. Cover the root Fiber, blocking and non-blocking Fibers, --- core/fiber/current_scheduler_spec.rb | 67 +++++++++++++++++++++++++++ core/fiber/fixtures/scheduler.rb | 5 ++ core/fiber/schedule_spec.rb | 69 ++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+) create mode 100644 core/fiber/current_scheduler_spec.rb create mode 100644 core/fiber/schedule_spec.rb diff --git a/core/fiber/current_scheduler_spec.rb b/core/fiber/current_scheduler_spec.rb new file mode 100644 index 000000000..fc73c8e6a --- /dev/null +++ b/core/fiber/current_scheduler_spec.rb @@ -0,0 +1,67 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/scheduler' + +describe "Fiber.current_scheduler" do + it "returns nil when no scheduler is set" do + Fiber.scheduler.should == nil + Fiber.current_scheduler.should == nil + end + + describe "when a scheduler is set" do + before :each do + @scheduler = FiberSpecs::LoggingScheduler.new + Fiber.set_scheduler(@scheduler) + end + + after :each do + Fiber.set_scheduler(nil) + end + + it "returns nil on the root Fiber, which is blocking" do + Fiber.current_scheduler.should == nil + end + + it "returns the scheduler inside a non-blocking Fiber" do + seen = nil + Fiber.new(blocking: false) { seen = Fiber.current_scheduler }.resume + seen.should.equal?(@scheduler) + end + + it "returns nil inside a blocking Fiber, where Fiber.scheduler still returns the scheduler" do + seen = nil + Fiber.new(blocking: true) { seen = [Fiber.scheduler, Fiber.current_scheduler] }.resume + seen.should == [@scheduler, nil] + end + + it "returns nil inside a blocking Fiber nested in a non-blocking Fiber" do + seen = nil + Fiber.new(blocking: false) do + Fiber.new(blocking: true) { seen = Fiber.current_scheduler }.resume + end.resume + seen.should == nil + end + + it "returns the scheduler inside a non-blocking Fiber nested in a blocking Fiber" do + seen = nil + Fiber.new(blocking: true) do + Fiber.new(blocking: false) { seen = Fiber.current_scheduler }.resume + end.resume + seen.should.equal?(@scheduler) + end + + it "returns nil inside Fiber.blocking in a non-blocking Fiber" do + seen = nil + Fiber.new(blocking: false) { Fiber.blocking { seen = Fiber.current_scheduler } }.resume + seen.should == nil + end + + it "returns the scheduler again after Fiber.blocking returns" do + seen = nil + Fiber.new(blocking: false) do + Fiber.blocking { } + seen = Fiber.current_scheduler + end.resume + seen.should.equal?(@scheduler) + end + end +end diff --git a/core/fiber/fixtures/scheduler.rb b/core/fiber/fixtures/scheduler.rb index 16bd2f6b4..e29a7bac7 100644 --- a/core/fiber/fixtures/scheduler.rb +++ b/core/fiber/fixtures/scheduler.rb @@ -11,6 +11,11 @@ def block(*args) Fiber.yield end + def fiber(*args, &block) + @events << { event: :fiber, fiber: Fiber.current, args: args } + Fiber.new(blocking: false, &block).tap(&:resume) + end + def io_wait(*args) @events << { event: :io_wait, fiber: Fiber.current, args: args } Fiber.yield diff --git a/core/fiber/schedule_spec.rb b/core/fiber/schedule_spec.rb new file mode 100644 index 000000000..5eb5b5a9a --- /dev/null +++ b/core/fiber/schedule_spec.rb @@ -0,0 +1,69 @@ +require_relative '../../spec_helper' +require_relative 'fixtures/scheduler' + +describe "Fiber.schedule" do + describe "when no scheduler is set" do + it "raises a RuntimeError" do + Fiber.scheduler.should == nil + + -> { + Fiber.schedule { } + }.should.raise(RuntimeError) + end + end + + describe "when a scheduler is set" do + before :each do + @scheduler = FiberSpecs::LoggingScheduler.new + Fiber.set_scheduler(@scheduler) + end + + after :each do + Fiber.set_scheduler(nil) + end + + it "calls the scheduler's #fiber hook" do + Fiber.schedule { } + @scheduler.events.map { |event| event[:event] }.should == [:fiber] + end + + it "returns the Fiber which runs the block" do + scheduled = nil + fiber = Fiber.schedule { scheduled = Fiber.current } + fiber.should.equal?(scheduled) + end + + it "can be called from inside a non-blocking Fiber" do + inner = nil + + outer = Fiber.new(blocking: false) do + inner = Fiber.schedule { } + end + outer.resume + + inner.should.is_a?(Fiber) + end + + it "uses the scheduler of the Thread owning the Fiber it is called from" do + seen = nil + + outer = Fiber.new(blocking: false) do + Fiber.schedule { seen = Fiber.scheduler } + end + outer.resume + + seen.should.equal?(@scheduler) + end + + it "runs the block in a Fiber which sees the scheduler as its current scheduler" do + seen = nil + + outer = Fiber.new(blocking: false) do + Fiber.schedule { seen = Fiber.current_scheduler } + end + outer.resume + + seen.should.equal?(@scheduler) + end + end +end