Closed kudojp closed 2 years ago
I do grep search for places where timeout_after
is used now.
I take a look at each of these.
# @deprecated Replaced by {Scheduler#timeout_after}.
def with_timeout(timeout, exception = TimeoutError, message = "execution expired", &block)
Fiber.scheduler.timeout_after(timeout, exception, message, &block)
end
👉 When calling Task#with_timeout, the block is passed to the method call of Scheduler#with_timeout. This part crashes when Task#with_timeout
is called with a block and the block argument is used. It should be a timeout, but now it is a timer. This has to be investigated more.
alias with_timeout timeout_after
👉 Reactor class inherits Scheduler class and #timeout_after
is the one defined as Scheduler#timeout_after
. Thus, if Reactor#with_timeout
or Reactor#timeout_after
is called with a block and the block argument is used. It should be a timeout, but now it is a timer. This has to be investigated more.
✅ This is exactly the method I update in this PR.
Both of 1 and 2 above are on #with_timeout
method. I do grep search for with_timeout
to see where they are used.
In the result above, spec/async/task_spec.rb L368
is the only problem.
I take a look.
describe '#with_timeout' do
it "can extend timeout" do
reactor.async do |task|
task.with_timeout(0.2) do |timer|
task.sleep(0.1)
expect(timer.fires_in).to be_within(10 * Q).percent_of(0.1)
timer.reset
expect(timer.fires_in).to be_within(10 * Q).percent_of(0.2)
end
end
reactor.run
end
👉 I found from above that the timer of the block argument is used to reset/reschedule the timer associated with the task scheduled by Task#with_timeout
. Since Task#with_timeout
is now deprecated, it is acceptable to drop the capability to reset/reschedule these tasks.
Mmm,,,
Now I noticed that if this PR is merged, resetting/rescheduling timers of the tasks which are scheduled by Scheduler#timeout_after
becomes impossible too (even though it is not tested in any of the specs now).
Hi @ioquatix, nice to meet you! I would like to know your idea!
In this PR, I updated Async::Scheduler#timeout_after
so that Async::Scheduler
becomes compatible with Timeout.timeout
executed in Fiber.schedule
block. However, with this change, the interface to reset/reschedule the tasks which are scheduled with Timeout.timeout
would be lost.
I would like to know if you think this is acceptable or not. (I have come up with solutions below and thought the solution2 is the best.)
I personally think solution 2 is the best. If the capability of resetting/rescheduling tasks is necessary, I think updating the interface of FiberScheduler should come first. If the Ruby maintainers (including you) make a conclusion that reseting/rescheduling tasks is useful, the interface should also be decided there. After that, this gem should be updated in that way, I think.
Thanks for all the details and notes. I will implement option 3 or some variant of it as the interface should be compatible between Async 1 and 2. I have a locally working branch I'll push it later this week.
Thanks I have merged a fix for this.
Thanx!
I will try to release this soon after I test the changes.
Description
Why
In the current implementation of
Async::Scheduler#timeout_after
, it executes the given block with the timer as the block argument as below.(from. https://github.com/socketry/async/blob/v2.0.0/lib/async/scheduler.rb#L294)
However, in ruby 3.1, the scheduler which is set to the thread is triggered as below.
(from. https://github.com/ruby/ruby/blob/ruby_3_1/lib/timeout.rb#L88-L90)
When both
Timeout#timeout
andTimeout.timeout
is called, they take the block argument as timeout(second), not as a Timer object. This is also described in the official doc as below.This should be fixed.
What
Async::Scheduler#timeout_after
executes the given block with passing timeout as the argument.Types of Changes
Testing