Class: Phronomy::CancellationToken

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

Overview

Provides cooperative cancellation for agent invocations.

Pass a token to an agent via +config: { cancellation_token: token }+. The agent checks the token before each LLM call and raises CancellationError when the token is cancelled or the optional deadline has passed.

A token may be shared across multiple agent invocations and across threads; all access to internal state is protected by a Mutex.

Examples:

Explicit cancel from another thread

token = Phronomy::CancellationToken.new
Thread.new { sleep 5; token.cancel! }
result = agent.invoke("...", config: { cancellation_token: token })

Hard deadline via monotonic clock (recommended)

token = Phronomy::CancellationToken.timeout_after(30)
result = agent.invoke("...", config: { cancellation_token: token })

Hard deadline via wall-clock (legacy)

token = Phronomy::CancellationToken.new(deadline: Time.now + 30)
result = agent.invoke("...", config: { cancellation_token: token })

Propagate to parallel workers

token = Phronomy::CancellationToken.new
orchestrator.dispatch_parallel(task1, task2, cancellation_token: token)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(deadline: nil, monotonic_deadline: nil) ⇒ CancellationToken

Returns a new instance of CancellationToken.

Parameters:

  • deadline (Time, nil) (defaults to: nil)

    optional wall-clock deadline; the token reports +cancelled?+ as +true+ once +Time.now >= deadline+. Prefer timeout_after for duration-based cancellation.

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

    internal monotonic timestamp set by timeout_after; prefer that factory method over passing this directly.



50
51
52
53
54
55
56
# File 'lib/phronomy/cancellation_token.rb', line 50

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

Instance Attribute Details

#deadlineTime? (readonly)

Returns the wall-clock deadline passed to #initialize, or +nil+.

Returns:

  • (Time, nil)

    the wall-clock deadline passed to #initialize, or +nil+.



59
60
61
# File 'lib/phronomy/cancellation_token.rb', line 59

def deadline
  @deadline
end

Class Method Details

.timeout_after(seconds) ⇒ CancellationToken

Returns a new token that will expire after +seconds+ seconds, measured with the monotonic clock (+Process::CLOCK_MONOTONIC+). Unlike constructing a token with +deadline: Time.now + seconds+, this factory is immune to NTP adjustments and DST transitions.

Parameters:

  • seconds (Numeric)

    duration in seconds until the token expires.

Returns:



39
40
41
42
# File 'lib/phronomy/cancellation_token.rb', line 39

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

Mark the token as cancelled and fire any registered #on_cancel callbacks. Thread-safe; idempotent — calling multiple times has no additional effect.

Returns:

  • (self)


99
100
101
102
103
104
105
106
107
# File 'lib/phronomy/cancellation_token.rb', line 99

def cancel!
  callbacks = @mutex.synchronize do
    return self if @cancelled
    @cancelled = true
    @cancel_callbacks.dup
  end
  callbacks.each(&:call)
  self
end

#cancelled?Boolean

Returns +true+ when the token has been explicitly cancelled via #cancel!, when the wall-clock deadline has passed, or when the monotonic deadline (set by timeout_after) has elapsed. Thread-safe.

Returns:

  • (Boolean)


114
115
116
117
118
119
# File 'lib/phronomy/cancellation_token.rb', line 114

def cancelled?
  return true if @mutex.synchronize { @cancelled }
  return true if !@deadline.nil? && Time.now >= @deadline
  !@monotonic_deadline.nil? &&
    Process.clock_gettime(Process::CLOCK_MONOTONIC) >= @monotonic_deadline
end

#on_cancel { ... } ⇒ self

Registers a one-shot callback invoked when this token is explicitly cancelled via #cancel!. If the token is already cancelled, the block is called immediately (still within the caller's thread).

Callbacks are NOT fired for deadline-based cancellation (i.e. when #cancelled? returns +true+ due to +@monotonic_deadline+ expiry). Use Runtime#timer_queue to schedule deadline callbacks.

Yields:

  • called with no arguments when (or if) the token is cancelled

Returns:

  • (self)


82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/phronomy/cancellation_token.rb', line 82

def on_cancel(&block)
  already_cancelled = @mutex.synchronize do
    if @cancelled
      true
    else
      @cancel_callbacks << block
      false
    end
  end
  block.call if already_cancelled
  self
end

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

Raises Phronomy::CancellationError if the token is cancelled. A convenience method for cooperative cancellation checks inside tools, RAG loaders, and hooks, replacing the +if cancelled? then raise+ pattern.

Parameters:

  • message (String) (defaults to: "invocation cancelled")

    optional error message

Returns:

  • (nil)

    when the token is not cancelled

Raises:



129
130
131
# File 'lib/phronomy/cancellation_token.rb', line 129

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

#remaining_monotonic_secondsFloat?

Returns the remaining seconds until the monotonic deadline fires, or +nil+ when no monotonic deadline is set. Returns 0.0 if already past.

Returns:

  • (Float, nil)


65
66
67
68
69
# File 'lib/phronomy/cancellation_token.rb', line 65

def remaining_monotonic_seconds
  return nil if @monotonic_deadline.nil?
  remaining = @monotonic_deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
  [remaining, 0.0].max
end