Class: Phronomy::Concurrency::CancellationToken

Inherits:
Object
  • Object
show all
Defined in:
lib/phronomy/engine/concurrency/cancellation_token.rb

Overview

Cooperative cancellation token for Agent/Tool work.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(monotonic_deadline: nil) ⇒ CancellationToken

Returns a new instance of CancellationToken.

Parameters:

  • monotonic_deadline (Float, nil) (defaults to: nil)

    internal monotonic timestamp.



16
17
18
19
20
21
# File 'lib/phronomy/engine/concurrency/cancellation_token.rb', line 16

def initialize(monotonic_deadline: nil)
  @cancelled = false
  @monotonic_deadline = monotonic_deadline
  @mutex = Mutex.new
  @cancel_callbacks = []
end

Class Method Details

.timeout_after(seconds) ⇒ Object

Creates a token that expires after seconds measured with the monotonic clock.



9
10
11
12
# File 'lib/phronomy/engine/concurrency/cancellation_token.rb', line 9

def self.timeout_after(seconds)
  monotonic_deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + seconds
  new(monotonic_deadline: monotonic_deadline)
end

Instance Method Details

#cancel!self

Explicitly cancels the token and invokes each currently registered callback once. The callback registry is cleared before callbacks run so completed registrations are not retained for the lifetime of a long-lived token.

Cancellation callbacks are isolated from one another: a StandardError raised by one callback is logged and the remaining callbacks are still delivered.

Returns:

  • (self)


66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/phronomy/engine/concurrency/cancellation_token.rb', line 66

def cancel!
  callbacks = @mutex.synchronize do
    return self if @cancelled

    @cancelled = true
    callbacks = @cancel_callbacks
    @cancel_callbacks = []
    callbacks
  end
  callbacks.each { |callback| deliver_cancel_callback(callback) }
  self
end

#cancelled?Boolean

Returns:

  • (Boolean)


80
81
82
83
84
85
# File 'lib/phronomy/engine/concurrency/cancellation_token.rb', line 80

def cancelled?
  return true if @mutex.synchronize { @cancelled }

  !@monotonic_deadline.nil? &&
    Process.clock_gettime(Process::CLOCK_MONOTONIC) >= @monotonic_deadline
end

#on_cancel(&block) ⇒ self

Registers a callback for explicit cancellation.

Deadline expiry by itself only changes #cancelled?. Components that need callback delivery for a monotonic deadline must promote that deadline to cancel! through the Runtime timer queue.

Cancellation callbacks are independent notifications. Failure of one callback is logged and does not suppress delivery to other callbacks.

Returns:

  • (self)

Raises:

  • (ArgumentError)


42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/phronomy/engine/concurrency/cancellation_token.rb', line 42

def on_cancel(&block)
  raise ArgumentError, "on_cancel requires a block" unless block

  already_cancelled = @mutex.synchronize do
    if @cancelled
      true
    else
      @cancel_callbacks << block
      false
    end
  end
  deliver_cancel_callback(block) if already_cancelled
  self
end

#raise_if_cancelled!(message = "invocation cancelled") ⇒ Object



88
89
90
# File 'lib/phronomy/engine/concurrency/cancellation_token.rb', line 88

def raise_if_cancelled!(message = "invocation cancelled")
  raise Phronomy::CancellationError, message if cancelled?
end

#remaining_monotonic_secondsObject



24
25
26
27
28
29
# File 'lib/phronomy/engine/concurrency/cancellation_token.rb', line 24

def remaining_monotonic_seconds
  return nil if @monotonic_deadline.nil?

  remaining = @monotonic_deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
  [remaining, 0.0].max
end