Class: Phronomy::Task
- Inherits:
-
Object
- Object
- Phronomy::Task
- Defined in:
- lib/phronomy/engine/task.rb
Overview
A thread-free asynchronous completion handle.
Task does not execute work. Execution belongs to EventLoop/FSMSession or OffloadPool. Task represents completion, failure, cancellation, callbacks, and a blocking wait for callers outside EventLoop.
Framework components own Task settlement. Application code should observe a Task through #wait_result, #on_complete, #map, and state readers rather than calling #complete, #fail, or #cancel!. Operation-wide cancellation is supplied through the CancellationToken accepted by the API that created the Task.
Constant Summary collapse
- STATES =
%i[pending completed failed cancelled].freeze
Instance Attribute Summary collapse
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#parent ⇒ Object
readonly
Returns the value of attribute parent.
Class Method Summary collapse
-
.deferred(name: nil, parent: nil) ⇒ Object
private
Creates an unsettled completion handle for framework-owned execution.
Instance Method Summary collapse
-
#alive? ⇒ Boolean
Whether the Task has not yet reached a terminal state.
-
#cancel!(error = Phronomy::CancellationError.new("Task cancelled")) ⇒ Object
private
Settles this Task as cancelled.
-
#complete(value = nil) ⇒ Object
private
Settles this Task successfully.
-
#done? ⇒ Boolean
Whether the Task has reached a terminal state.
-
#fail(error) ⇒ Object
private
Settles this Task with a failure.
-
#initialize(name: nil, parent: nil) ⇒ Task
constructor
private
A new instance of Task.
-
#join(limit = nil) ⇒ Object
private
Compatibility wait that does not re-raise the Task error.
-
#map(&block) ⇒ Object
Creates a derived Task by transforming this Task's successful result.
-
#on_complete {|value, error| ... } ⇒ self
Registers an independent completion notification.
-
#status ⇒ Symbol
:pending, :completed, :failed, or :cancelled.
-
#wait_result(timeout: nil) ⇒ Object
Blocks the calling thread until settlement.
Constructor Details
#initialize(name: nil, parent: nil) ⇒ Task
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns a new instance of Task.
29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/phronomy/engine/task.rb', line 29 def initialize(name: nil, parent: nil) @name = name @parent = parent @status = :pending @value = nil @error = nil @mutex = Mutex.new @cond = ConditionVariable.new @children = [] @on_complete_callbacks = [] parent&.register_child(self) end |
Instance Attribute Details
#name ⇒ Object (readonly)
Returns the value of attribute name.
26 27 28 |
# File 'lib/phronomy/engine/task.rb', line 26 def name @name end |
#parent ⇒ Object (readonly)
Returns the value of attribute parent.
26 27 28 |
# File 'lib/phronomy/engine/task.rb', line 26 def parent @parent end |
Class Method Details
.deferred(name: nil, parent: nil) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Creates an unsettled completion handle for framework-owned execution.
22 23 24 |
# File 'lib/phronomy/engine/task.rb', line 22 def self.deferred(name: nil, parent: nil) new(name: name, parent: parent) end |
Instance Method Details
#alive? ⇒ Boolean
Returns whether the Task has not yet reached a terminal state.
56 57 58 |
# File 'lib/phronomy/engine/task.rb', line 56 def alive? !done? end |
#cancel!(error = Phronomy::CancellationError.new("Task cancelled")) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Settles this Task as cancelled. Framework-owned settlement API.
This method does not propagate backwards into a CancellationToken that may have been used to create the Task. Tokens can be shared across operations; operation-wide cancellation is owned by the creating API.
167 168 169 170 171 172 173 174 |
# File 'lib/phronomy/engine/task.rb', line 167 def cancel!(error = Phronomy::CancellationError.new("Task cancelled")) changed = settle!(:cancelled, error: error) if changed children = @mutex.synchronize { @children.dup } children.each(&:cancel!) end self end |
#complete(value = nil) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Settles this Task successfully. Framework-owned settlement API.
150 151 152 |
# File 'lib/phronomy/engine/task.rb', line 150 def complete(value = nil) settle!(:completed, value: value) end |
#done? ⇒ Boolean
Returns whether the Task has reached a terminal state.
50 51 52 |
# File 'lib/phronomy/engine/task.rb', line 50 def done? @mutex.synchronize { TERMINAL_STATES.include?(@status) } end |
#fail(error) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Settles this Task with a failure. Framework-owned settlement API.
156 157 158 159 |
# File 'lib/phronomy/engine/task.rb', line 156 def fail(error) raise ArgumentError, "error is required" unless error settle!(:failed, error: error) end |
#join(limit = nil) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Compatibility wait that does not re-raise the Task error. Returns self when settled, nil on timeout.
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/phronomy/engine/task.rb', line 101 def join(limit = nil) if Phronomy::Runtime.in_event_loop_context? && !done? raise Phronomy::EventLoopReentrancyError, "Task#join cannot block the EventLoop thread; continue via an event" end deadline = limit && monotonic_now + limit.to_f @mutex.synchronize do until TERMINAL_STATES.include?(@status) if deadline remaining = deadline - monotonic_now return nil if remaining <= 0 @cond.wait(@mutex, remaining) else @cond.wait(@mutex) end end end self end |
#map(&block) ⇒ Object
Creates a derived Task by transforming this Task's successful result.
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/phronomy/engine/task.rb', line 178 def map(&block) raise ArgumentError, "map requires a block" unless block mapped = self.class.deferred(name: "#{@name}-mapped", parent: @parent) on_complete do |value, error| if error mapped.fail(error) next end begin mapped.complete(block.call(value)) rescue => mapped_error mapped.fail(mapped_error) end end mapped end |
#on_complete {|value, error| ... } ⇒ self
Registers an independent completion notification.
The callback execution thread is not guaranteed. It may be the caller that registers after settlement, an OffloadPool worker, or a framework control thread. Callbacks must therefore be thread-safe and should complete quickly. A callback failure is logged and does not suppress delivery to other completion callbacks or change the Task's already-settled result.
133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/phronomy/engine/task.rb', line 133 def on_complete(&callback) raise ArgumentError, "on_complete requires a block" unless callback fire_args = nil @mutex.synchronize do if TERMINAL_STATES.include?(@status) fire_args = [@value, @error] else @on_complete_callbacks << callback end end deliver_completion_callback(callback, *fire_args) if fire_args self end |
#status ⇒ Symbol
Returns :pending, :completed, :failed, or :cancelled.
44 45 46 |
# File 'lib/phronomy/engine/task.rb', line 44 def status @mutex.synchronize { @status } end |
#wait_result(timeout: nil) ⇒ Object
Blocks the calling thread until settlement.
EventLoop is never allowed to wait for a Task; framework continuation must proceed through explicit events. The optional timeout is waiter-local: it does not settle or cancel the Task.
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/phronomy/engine/task.rb', line 71 def wait_result(timeout: nil) if Phronomy::Runtime.in_event_loop_context? && !done? raise Phronomy::EventLoopReentrancyError, "Task#wait_result cannot block the EventLoop thread; continue via an event" end deadline = timeout && monotonic_now + timeout.to_f value, error = @mutex.synchronize do until TERMINAL_STATES.include?(@status) if deadline remaining = deadline - monotonic_now if remaining <= 0 raise Phronomy::TimeoutError, "timed out waiting for Task #{@name || "(unnamed)"}" end @cond.wait(@mutex, remaining) else @cond.wait(@mutex) end end [@value, @error] end raise error if error value end |