Skip to content

Commit

Permalink
Allow Interrupt to retry the run loop after issuing #stop. (#297)
Browse files Browse the repository at this point in the history
* Add Ruby v3.1 to coverage workflow.
  • Loading branch information
ioquatix authored Jan 3, 2024
1 parent d4409ff commit 2fcb2ca
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 9 deletions.
1 change: 1 addition & 0 deletions .github/workflows/coverage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
- ubuntu

ruby:
- "3.1"
- "3.2"
- "ruby-head"

Expand Down
23 changes: 14 additions & 9 deletions lib/async/scheduler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -306,17 +306,22 @@ def run(...)

initial_task = self.async(...) if block_given?

# In theory, we could use Exception here to be a little bit safer, but we've only shown the case for SignalException to be a problem, so let's not over-engineer this.
Thread.handle_interrupt(SignalException => :never) do
while true
# If we are interrupted, we need to exit:
break if self.interrupted?

# If we are finished, we need to exit:
break unless self.run_once
begin
# In theory, we could use Exception here to be a little bit safer, but we've only shown the case for SignalException to be a problem, so let's not over-engineer this.
Thread.handle_interrupt(SignalException => :never) do
while true
# If we are interrupted, we need to exit:
break if self.interrupted?

# If we are finished, we need to exit:
break unless self.run_once
end
end
rescue Interrupt
self.stop
retry
end

return initial_task
ensure
Console.logger.debug(self) {"Exiting run-loop because #{$! ? $! : 'finished'}."}
Expand Down
37 changes: 37 additions & 0 deletions test/async/scheduler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,43 @@
scheduler.close
scheduler.interrupt
end

it "can interrupt a scheduler from a different thread" do
finished = false
sleeping = Thread::Queue.new

thread = Thread.new do
scheduler = Async::Scheduler.new
Fiber.set_scheduler(scheduler)

scheduler.run do |task|
sleeping.push(true)
sleep
ensure
begin
sleeping.push(true)
sleep
ensure
finished = true
end
end
# rescue Interrupt
# # Ignore.
end

expect(sleeping.pop).to be == true
expect(finished).to be == false

thread.raise(Interrupt)

expect(sleeping.pop).to be == true
expect(finished).to be == false

thread.raise(Interrupt)
thread.join

expect(finished).to be == true
end
end

with '#block' do
Expand Down

0 comments on commit 2fcb2ca

Please sign in to comment.