From f57516011d5d495fd2395ed394c974908ba2ded1 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 11 Feb 2025 01:21:37 +1300 Subject: [PATCH 1/2] Add support for `IO::Event::Profiler`. --- async.gemspec | 2 +- lib/async/scheduler.rb | 22 +++++++++++++++------- test/io.rb | 4 +++- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/async.gemspec b/async.gemspec index 378ae422..6976b653 100644 --- a/async.gemspec +++ b/async.gemspec @@ -27,7 +27,7 @@ Gem::Specification.new do |spec| spec.add_dependency "console", "~> 1.29" spec.add_dependency "fiber-annotation" - spec.add_dependency "io-event", "~> 1.7" + spec.add_dependency "io-event", "~> 1.9" spec.add_dependency "traces", "~> 0.15" spec.add_dependency "metrics", "~> 0.12" end diff --git a/lib/async/scheduler.rb b/lib/async/scheduler.rb index 56c9620b..ed40736b 100644 --- a/lib/async/scheduler.rb +++ b/lib/async/scheduler.rb @@ -42,10 +42,12 @@ def self.supported? # @public Since *Async v1*. # @parameter parent [Node | Nil] The parent node to use for task hierarchy. # @parameter selector [IO::Event::Selector] The selector to use for event handling. - def initialize(parent = nil, selector: nil, worker_pool: DEFAULT_WORKER_POOL) + def initialize(parent = nil, selector: nil, profiler: IO::Event::Profiler.default, worker_pool: DEFAULT_WORKER_POOL) super(parent) @selector = selector || ::IO::Event::Selector.new(Fiber.current) + @profiler = profiler + @interrupted = false @blocked = 0 @@ -492,13 +494,19 @@ def stop def run(...) Kernel.raise ClosedError if @selector.nil? - initial_task = self.async(...) if block_given? - - self.run_loop do - run_once + begin + @profiler&.start + + initial_task = self.async(...) if block_given? + + self.run_loop do + run_once + end + + return initial_task + ensure + @profiler&.stop end - - return initial_task end # Start an asynchronous task within the specified reactor. The task will be executed until the first blocking call, at which point it will yield and and this method will return. diff --git a/test/io.rb b/test/io.rb index da52a4b2..846bb841 100644 --- a/test/io.rb +++ b/test/io.rb @@ -45,12 +45,14 @@ it "can write with timeout" do skip_unless_constant_defined(:TimeoutError, IO) + big = "x" * 1024 * 1024 + input, output = IO.pipe output.timeout = 0.001 expect do while true - output.write("Hello") + output.write(big) end end.to raise_exception(::IO::TimeoutError) end From c3186aad2de268631c3c813e334b6b31b09f37dd Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Thu, 13 Feb 2025 18:35:35 +1300 Subject: [PATCH 2/2] Use `fiber-profiler` gem. --- gems.rb | 1 + lib/async/scheduler.rb | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/gems.rb b/gems.rb index e506ace9..8dca18be 100644 --- a/gems.rb +++ b/gems.rb @@ -10,6 +10,7 @@ gemspec # gem "io-event", git: "https://github.com/socketry/io-event.git" +gem "fiber-profiler" group :maintenance, optional: true do gem "bake-gem" diff --git a/lib/async/scheduler.rb b/lib/async/scheduler.rb index ed40736b..b78ed21c 100644 --- a/lib/async/scheduler.rb +++ b/lib/async/scheduler.rb @@ -15,6 +15,14 @@ require "resolv" module Async + begin + require "fiber/profiler" + Profiler = Fiber::Profiler + rescue LoadError + # Fiber::Profiler is not available. + Profiler = nil + end + # Handles scheduling of fibers. Implements the fiber scheduler interface. class Scheduler < Node DEFAULT_WORKER_POOL = ENV.fetch("ASYNC_SCHEDULER_DEFAULT_WORKER_POOL", nil).then do |value| @@ -42,7 +50,7 @@ def self.supported? # @public Since *Async v1*. # @parameter parent [Node | Nil] The parent node to use for task hierarchy. # @parameter selector [IO::Event::Selector] The selector to use for event handling. - def initialize(parent = nil, selector: nil, profiler: IO::Event::Profiler.default, worker_pool: DEFAULT_WORKER_POOL) + def initialize(parent = nil, selector: nil, profiler: Profiler&.default, worker_pool: DEFAULT_WORKER_POOL) super(parent) @selector = selector || ::IO::Event::Selector.new(Fiber.current)