Class: Philiprehberger::TimeoutKit::Deadline

Inherits:
Object
  • Object
show all
Defined in:
lib/philiprehberger/timeout_kit/deadline.rb

Overview

A cooperative deadline that tracks remaining time and supports nesting.

Deadlines do not use Thread.raise. Instead, callers must explicitly call #check! at safe cancellation points.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(seconds, name: nil, grace: nil, on_expire: nil) ⇒ Deadline

Create a new deadline.

Parameters:

  • seconds (Numeric)

    the number of seconds until the deadline expires

  • name (String, nil) (defaults to: nil)

    optional human-readable name for the deadline

  • grace (Numeric, nil) (defaults to: nil)

    optional grace period in seconds after the primary deadline

  • on_expire (Proc, nil) (defaults to: nil)

    optional callback that fires once when expiry is detected



22
23
24
25
26
27
28
29
30
# File 'lib/philiprehberger/timeout_kit/deadline.rb', line 22

def initialize(seconds, name: nil, grace: nil, on_expire: nil)
  @started_at = now
  @expires_at = @started_at + seconds
  @name = name
  @grace_seconds = grace
  @grace_expires_at = @grace_seconds ? @expires_at + @grace_seconds : nil
  @on_expire = on_expire
  @expire_callback_fired = false
end

Instance Attribute Details

#expires_atFloat (readonly)

Returns the absolute monotonic time when the deadline expires.

Returns:

  • (Float)

    the absolute monotonic time when the deadline expires



11
12
13
# File 'lib/philiprehberger/timeout_kit/deadline.rb', line 11

def expires_at
  @expires_at
end

#nameString? (readonly)

Returns the human-readable name for this deadline.

Returns:

  • (String, nil)

    the human-readable name for this deadline



14
15
16
# File 'lib/philiprehberger/timeout_kit/deadline.rb', line 14

def name
  @name
end

Instance Method Details

#check!void

This method returns an undefined value.

Check whether the deadline has expired.

Raises:

  • (DeadlineExceeded)

    if the deadline has passed (and grace period, if any, has also passed)



44
45
46
47
48
49
50
51
52
53
54
# File 'lib/philiprehberger/timeout_kit/deadline.rb', line 44

def check!
  return unless expired?

  fire_expire_callback

  # If we have a grace period and are still within it, don't raise
  return if in_grace?

  message = @name ? "Deadline '#{@name}' exceeded" : 'Deadline exceeded'
  raise DeadlineExceeded, message
end

#elapsedFloat

Return the number of seconds elapsed since the deadline was created. This is a pure wall-clock reading from the monotonic clock and continues to increase past the original budget after expiration. Independent of #expired? and #in_grace?.

Returns:

  • (Float)

    seconds elapsed since creation



75
76
77
# File 'lib/philiprehberger/timeout_kit/deadline.rb', line 75

def elapsed
  now - @started_at
end

#expired?Boolean

Whether the primary deadline has expired.

Returns:

  • (Boolean)


92
93
94
# File 'lib/philiprehberger/timeout_kit/deadline.rb', line 92

def expired?
  now >= @expires_at
end

#grace_remainingFloat

Return the remaining time in the grace period.

Returns:

  • (Float)

    seconds remaining in grace period (0.0 if no grace period or grace expired)



82
83
84
85
86
87
# File 'lib/philiprehberger/timeout_kit/deadline.rb', line 82

def grace_remaining
  return 0.0 unless @grace_expires_at

  r = @grace_expires_at - now
  r.negative? ? 0.0 : r
end

#in_grace?Boolean

Whether the deadline is currently in the grace period. True only when the primary deadline has expired but the grace period has not.

Returns:

  • (Boolean)


100
101
102
103
104
# File 'lib/philiprehberger/timeout_kit/deadline.rb', line 100

def in_grace?
  return false unless @grace_expires_at

  expired? && now < @grace_expires_at
end

#on_expire { ... } ⇒ void

This method returns an undefined value.

Register a callback that fires once when expiry is detected.

Yields:

  • the block to call on expiry



36
37
38
# File 'lib/philiprehberger/timeout_kit/deadline.rb', line 36

def on_expire(&block)
  @on_expire = block
end

#remainingFloat

Return the remaining time in seconds until the primary deadline. Can be negative during the grace period.

Returns:

  • (Float)

    seconds remaining (negative if past primary deadline)



60
61
62
63
64
65
66
67
# File 'lib/philiprehberger/timeout_kit/deadline.rb', line 60

def remaining
  r = @expires_at - now
  if @grace_seconds
    r
  else
    r.negative? ? 0.0 : r
  end
end