lib/async/task.rb in async-2.19.0 vs lib/async/task.rb in async-2.20.0

- old
+ new

@@ -38,21 +38,21 @@ end end end # Raised if a timeout occurs on a specific Fiber. Handled gracefully by `Task`. - # @public Since `stable-v1`. + # @public Since *Async v1*. class TimeoutError < StandardError # Create a new timeout error. # # @parameter message [String] The error message. def initialize(message = "execution expired") super end end - # @public Since `stable-v1`. + # @public Since *Async v1*. class Task < Node # Raised when a child task is created within a task that has finished execution. class FinishedError < RuntimeError # Create a new finished error. # @@ -196,11 +196,11 @@ schedule do @block.call(self, *arguments) rescue => error # I'm not completely happy with this overhead, but the alternative is to not log anything which makes debugging extremely difficult. Maybe we can introduce a debug wrapper which adds extra logging. if @finished.nil? - Console.warn(self, "Task may have ended with unhandled exception.", exception: error) + warn(self, "Task may have ended with unhandled exception.", exception: error) end raise end else @@ -208,17 +208,28 @@ end end # Run an asynchronous task as a child of the current task. # + # @public Since *Async v1*. + # @asynchronous May context switch immediately to the new task. + # + # @yields {|task| ...} in the context of the new task. # @raises [FinishedError] If the task has already finished. # @returns [Task] The child task. def async(*arguments, **options, &block) raise FinishedError if self.finished? task = Task.new(self, **options, &block) + # When calling an async block, we deterministically execute it until the first blocking operation. We don't *have* to do this - we could schedule it for later execution, but it's useful to: + # + # - Fail at the point of the method call where possible. + # - Execute determinstically where possible. + # - Avoid scheduler overhead if no blocking operation is performed. + # + # There are different strategies (greedy vs non-greedy). We are currently using a greedy strategy. task.run(*arguments) return task end @@ -300,11 +311,11 @@ # You can nest calls to defer_stop, but the stop will only be deferred until the outermost block exits. # # If stop is invoked a second time, it will be immediately executed. # # @yields {} The block of code to execute. - # @public Since `stable-v1`. + # @public Since *Async v1*. def defer_stop # Tri-state variable for controlling stop: # - nil: defer_stop has not been called. # - false: defer_stop has been called and we are not stopping. # - true: defer_stop has been called and we will stop when exiting the block. @@ -357,9 +368,13 @@ def current? Fiber.current.equal?(@fiber) end private + + def warn(...) + Console.warn(...) + end # Finish the current task, moving any children to the parent. def finish! # Don't hold references to the fiber or block after the task has finished: @fiber = nil