socketry / async

An awesome asynchronous event-driven reactor for Ruby.
MIT License
2.05k stars 85 forks source link

Ensure transient tasks are correctly terminated. #245

Closed ioquatix closed 1 year ago

ioquatix commented 1 year ago

Fixes https://github.com/socketry/async/issues/244.

Top level transient tasks are a bit odd. The event loop is considered already finished? and thus won't execute a single iteration. However, in order to terminate the event loop, any and all child tasks need to be stopped.

The following program causes a hang:

Async(transient:true) do
  Async{sleep 1}
end
  1. The top level task is created and executes, starting the child task.
  2. The child task starts sleeping.
  3. The event loop is considered finished because the top level task is transient.
  4. The Scheduler#close method is invoked, which enters the terminate loop.
  5. Task#stop is invoked on the top level task which stops the child task by raising and exception, but itself is not stopped yet. Raising an exception transfers to the child task.
  6. Because run_once considers the scheduler is finished, it does not resume the parent task.
  7. It continues to try and stop the top level task, but won't resume it (even thought it's in the ready list).
  8. Infinite loop is achieved.

The fix, I think, is to avoid checking self.finished? when performing the termination loop. Tests added based on the original report.

Types of Changes

Contribution