Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lib/async/task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,11 @@ def result
# @parameter later [Boolean] Whether to cancel the task later, or immediately.
# @parameter cause [Exception] The cause of the cancel operation.
def cancel(later = false, cause: $!)
# On affected Ruby versions, `Thread#kill` exposes the internal `TAG_FATAL` value through `$!`, which is not a valid exception cause:
unless cause.is_a?(::Exception)
cause = nil
end

# If no cause is given, we generate one from the current call stack:
unless cause
cause = Cancel::Cause.for("Cancelling task!")
Expand Down
4 changes: 4 additions & 0 deletions releases.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Releases

## Unreleased

- Fixed `Thread#kill` raising a `TypeError` while shutting down a thread with an active `Async::Reactor`.

## v2.45.1

- Fixed `Scheduler#io_wait` returning `nil` instead of `false` when an explicit timeout expired. Native callers such as `Socket#connect` with `connect_timeout:` distinguish a timeout by checking for `false`, so the `nil` caused `TypeError: no implicit conversion from nil to integer` instead of the intended `IO::TimeoutError`.
Expand Down
27 changes: 27 additions & 0 deletions test/async/scheduler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,33 @@
end

with "#close" do
it "can close when the owning thread is killed" do
ready = Thread::Queue.new

thread = Thread.new do
reactor = Async::Reactor.new

reactor.async do
reactor.async do
sleep
end
end

ready << true
reactor.run
end
thread.report_on_exception = false

ready.pop
thread.kill

expect do
thread.join
end.not.to raise_exception

expect(thread).not.to be(:alive?)
end

it "runs the event loop until terminated" do
scheduler = Async::Scheduler.new
Async::Node.new(scheduler)
Expand Down
Loading