Class: Dommy::Scheduler

Inherits:
Object
  • Object
show all
Defined in:
lib/dommy/scheduler.rb

Overview

Deterministic host-side scheduler for timers, rAF, and microtasks. Time advances only when the host explicitly calls ‘advance_time`.

Defined Under Namespace

Classes: Timer

Constant Summary collapse

FRAME_MS =
16
IDLE_DEADLINE =

requestIdleCallback has no real idle period here; the callback always sees a fixed budget and didTimeout: false.

{"timeRemaining" => 50.0, "didTimeout" => false}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeScheduler

Returns a new instance of Scheduler.



15
16
17
18
19
20
21
# File 'lib/dommy/scheduler.rb', line 15

def initialize
  @now_ms = 0
  @next_id = 1
  @timers = {}
  @microtasks = []
  @native_microtask_scheduler = nil
end

Instance Attribute Details

#native_microtask_schedulerObject

An optional hook (set by a JS runtime) that enqueues a microtask onto the engine’s NATIVE promise-job queue. When present, ‘queue_microtask` routes through it so host-side microtasks (e.g. MutationObserver delivery) interleave FIFO with JS `await`/Promise reactions instead of draining on a separate pass. Absent in vanilla CRuby use (falls back to `@microtasks`).



30
31
32
# File 'lib/dommy/scheduler.rb', line 30

def native_microtask_scheduler
  @native_microtask_scheduler
end

#now_msObject (readonly)

Returns the value of attribute now_ms.



23
24
25
# File 'lib/dommy/scheduler.rb', line 23

def now_ms
  @now_ms
end

Instance Method Details

#advance_time(delta_ms) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/dommy/scheduler.rb', line 88

def advance_time(delta_ms)
  target = @now_ms + [delta_ms.to_i, 0].max
  while next_due_timer_at && next_due_timer_at <= target
    @now_ms = next_due_timer_at
    run_due_timers
    drain_microtasks
  end

  @now_ms = target
  drain_microtasks
  nil
end

#cancel_animation_frame(id) ⇒ Object



56
57
58
# File 'lib/dommy/scheduler.rb', line 56

def cancel_animation_frame(id)
  cancel_timer(id)
end

#cancel_idle_callback(id) ⇒ Object



66
67
68
# File 'lib/dommy/scheduler.rb', line 66

def cancel_idle_callback(id)
  cancel_timer(id)
end

#clear_interval(id) ⇒ Object



45
46
47
# File 'lib/dommy/scheduler.rb', line 45

def clear_interval(id)
  cancel_timer(id)
end

#clear_timeout(id) ⇒ Object



36
37
38
# File 'lib/dommy/scheduler.rb', line 36

def clear_timeout(id)
  cancel_timer(id)
end

#drain_microtasksObject



79
80
81
82
83
84
85
86
# File 'lib/dommy/scheduler.rb', line 79

def drain_microtasks
  until @microtasks.empty?
    callback = @microtasks.shift
    CallableInvoker.invoke(callback, @now_ms)
  end

  nil
end

#drain_timers(advance: 0) ⇒ Object



101
102
103
# File 'lib/dommy/scheduler.rb', line 101

def drain_timers(advance: 0)
  advance_time(advance)
end

#next_due_timer_atObject

Public accessor for eval-time auto-drain: keep advancing the clock until no timers remain (or a safety budget runs out).



107
108
109
# File 'lib/dommy/scheduler.rb', line 107

def next_due_timer_at
  @timers.values.select(&:active).map(&:due_at).min
end

#queue_microtask(callback) ⇒ Object



70
71
72
73
74
75
76
77
# File 'lib/dommy/scheduler.rb', line 70

def queue_microtask(callback)
  if @native_microtask_scheduler
    @native_microtask_scheduler.call(callback)
  else
    @microtasks << callback
  end
  nil
end

#request_animation_frame(callback) ⇒ Object



49
50
51
52
53
54
# File 'lib/dommy/scheduler.rb', line 49

def request_animation_frame(callback)
  frames = ((@now_ms / FRAME_MS) + 1) * FRAME_MS
  id = next_id
  @timers[id] = Timer.new(id, :raf, callback, frames, nil, true)
  id
end

#request_idle_callback(callback, timeout = 0) ⇒ Object

WHATWG requestIdleCallback — modeled as a deferred timer that hands the callback an IdleDeadline-shaped Hash. No real idle period in dommy.



62
63
64
# File 'lib/dommy/scheduler.rb', line 62

def request_idle_callback(callback, timeout = 0)
  register_timer(:idle, callback, timeout.to_i, nil)
end

#set_interval(callback, interval_ms) ⇒ Object



40
41
42
43
# File 'lib/dommy/scheduler.rb', line 40

def set_interval(callback, interval_ms)
  ms = [interval_ms.to_i, 0].max
  register_timer(:interval, callback, ms, ms)
end

#set_timeout(callback, delay_ms) ⇒ Object



32
33
34
# File 'lib/dommy/scheduler.rb', line 32

def set_timeout(callback, delay_ms)
  register_timer(:timeout, callback, delay_ms.to_i, nil)
end