Class: Phronomy::Runtime::TimerQueue

Inherits:
Object
  • Object
show all
Defined in:
lib/phronomy/engine/runtime/timer_queue.rb

Overview

Threadless monotonic timer heap driven by EventLoop.

Instance Method Summary collapse

Constructor Details

#initialize(clock: -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) }) ⇒ TimerQueue

Returns a new instance of TimerQueue.



7
8
9
10
11
12
13
# File 'lib/phronomy/engine/runtime/timer_queue.rb', line 7

def initialize(clock: -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) })
  @clock = clock
  @heap = []
  @mutex = Mutex.new
  @stopped = false
  @wake = nil
end

Instance Method Details

#fire_dueObject

Executes all callbacks whose deadline is due. Must be called by EventLoop.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/phronomy/engine/runtime/timer_queue.rb', line 48

def fire_due
  callbacks = @mutex.synchronize do
    return 0 if @stopped

    now = @clock.call
    due_count = @heap.bsearch_index { |(fire_at, _)| fire_at > now } || @heap.length
    @heap.shift(due_count).map(&:last)
  end

  callbacks.each do |callback|
    callback.call
  rescue => error
    Phronomy.configuration.logger&.error do
      "[TimerQueue] callback raised #{error.class}: #{error.message}"
    end
  end
  callbacks.length
end

#pending_countObject



67
68
69
# File 'lib/phronomy/engine/runtime/timer_queue.rb', line 67

def pending_count
  @mutex.synchronize { @heap.size }
end

#schedule(seconds:, &callback) ⇒ Object

Raises:

  • (ArgumentError)


22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/phronomy/engine/runtime/timer_queue.rb', line 22

def schedule(seconds:, &callback)
  raise ArgumentError, "schedule requires a block" unless callback

  fire_at = @clock.call + seconds.to_f
  wake = nil
  @mutex.synchronize do
    raise Phronomy::PoolShutdownError, "TimerQueue has been shut down" if @stopped

    previous_first = @heap.first&.first
    @heap << [fire_at, callback]
    @heap.sort_by!(&:first)
    wake = @wake if previous_first.nil? || fire_at < previous_first
  end
  wake&.call
  self
end

#seconds_until_nextObject

Seconds until the next timer is due, nil when no timers are pending.



40
41
42
43
44
45
# File 'lib/phronomy/engine/runtime/timer_queue.rb', line 40

def seconds_until_next
  @mutex.synchronize do
    return nil if @stopped || @heap.empty?
    [@heap.first.first - @clock.call, 0.0].max
  end
end

#shutdownObject



71
72
73
74
75
76
77
78
79
80
# File 'lib/phronomy/engine/runtime/timer_queue.rb', line 71

def shutdown
  wake = @mutex.synchronize do
    return self if @stopped
    @stopped = true
    @heap.clear
    @wake
  end
  wake&.call
  self
end

#wake_with(&block) ⇒ Object

Installs a lightweight wake callback used when a newly scheduled timer may change EventLoop's current wait deadline.



17
18
19
20
# File 'lib/phronomy/engine/runtime/timer_queue.rb', line 17

def wake_with(&block)
  @mutex.synchronize { @wake = block }
  self
end