Class: Dommy::Performance

Inherits:
Object
  • Object
show all
Includes:
Bridge::Methods
Defined in:
lib/dommy/performance.rb

Overview

window.performance — User Timing API (mark / measure) plus a virtual now clock backed by the deterministic scheduler.

Spec:

- User Timing: https://www.w3.org/TR/user-timing/
- HRT (now):   https://www.w3.org/TR/hr-time/

Instance Method Summary collapse

Methods included from Bridge::Methods

included

Constructor Details

#initialize(window) ⇒ Performance

Returns a new instance of Performance.



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/dommy/performance.rb', line 11

def initialize(window)
  @window = window
  @entries = []
  # In browser mode (dommynx sets DOMMY_REAL_TIME_PERFORMANCE) `now` tracks
  # REAL wall-clock, not the deterministic scheduler's virtual clock. Virtual
  # time only advances between tasks (in advance_time), so it is FROZEN inside
  # a long synchronous task — and a concurrent renderer (React's scheduler)
  # decides when to yield by watching performance.now() cross a ~5ms frame
  # budget. With a frozen clock it never crosses, so React renders the entire
  # fiber tree in ONE eval that overruns the eval timeout ("InternalError:
  # interrupted"). A real clock lets it slice the work and yield (via
  # MessageChannel), so each eval stays short. Off by default → tests keep the
  # deterministic virtual clock.
  @real_time = !ENV["DOMMY_REAL_TIME_PERFORMANCE"].to_s.empty?
  @real_origin = nil
end

Instance Method Details

#__js_call__(method, args) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/dommy/performance.rb', line 118

def __js_call__(method, args)
  case method
  when "now"
    now
  when "mark"
    mark(args[0], args[1])
  when "measure"
    measure(args[0], args[1], args[2])
  when "clearMarks"
    clear_marks(args[0])
  when "clearMeasures"
    clear_measures(args[0])
  when "getEntries"
    get_entries
  when "getEntriesByName"
    get_entries_by_name(args[0], args[1])
  when "getEntriesByType"
    get_entries_by_type(args[0])
  end
end

#__js_get__(key) ⇒ Object



103
104
105
106
107
108
109
110
111
112
# File 'lib/dommy/performance.rb', line 103

def __js_get__(key)
  case key
  when "now"
    now
  when "timeOrigin"
    0.0
  else
    Bridge::ABSENT
  end
end

#clear_marks(name = nil) ⇒ Object Also known as: clearMarks



71
72
73
74
# File 'lib/dommy/performance.rb', line 71

def clear_marks(name = nil)
  @entries.reject! { |e| e.entry_type == "mark" && (name.nil? || e.name == name.to_s) }
  nil
end

#clear_measures(name = nil) ⇒ Object Also known as: clearMeasures



78
79
80
81
# File 'lib/dommy/performance.rb', line 78

def clear_measures(name = nil)
  @entries.reject! { |e| e.entry_type == "measure" && (name.nil? || e.name == name.to_s) }
  nil
end

#get_entriesObject Also known as: getEntries



85
86
87
# File 'lib/dommy/performance.rb', line 85

def get_entries
  @entries.dup
end

#get_entries_by_name(name, entry_type = nil) ⇒ Object Also known as: getEntriesByName



91
92
93
# File 'lib/dommy/performance.rb', line 91

def get_entries_by_name(name, entry_type = nil)
  @entries.select { |e| e.name == name.to_s && (entry_type.nil? || e.entry_type == entry_type.to_s) }
end

#get_entries_by_type(entry_type) ⇒ Object Also known as: getEntriesByType



97
98
99
# File 'lib/dommy/performance.rb', line 97

def get_entries_by_type(entry_type)
  @entries.select { |e| e.entry_type == entry_type.to_s }
end

#mark(name, options = nil) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/dommy/performance.rb', line 36

def mark(name, options = nil)
  start_time = options.is_a?(Hash) && options.key?("startTime") ? options["startTime"].to_f : now
  detail = options.is_a?(Hash) ? options["detail"] : nil
  entry = PerformanceEntry.new(
    name: name.to_s,
    entry_type: "mark",
    start_time: start_time,
    duration: 0.0,
    detail: detail
  )
  @entries << entry
  entry
end

#measure(name, start_or_options = nil, end_mark = nil) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/dommy/performance.rb', line 50

def measure(name, start_or_options = nil, end_mark = nil)
  if start_or_options.is_a?(Hash)
    start = resolve_time(start_or_options["start"])
    finish = resolve_time(start_or_options["end"])
  else
    start = resolve_time(start_or_options)
    finish = resolve_time(end_mark)
  end

  start ||= 0.0
  finish ||= now
  entry = PerformanceEntry.new(
    name: name.to_s,
    entry_type: "measure",
    start_time: start,
    duration: finish - start
  )
  @entries << entry
  entry
end

#nowObject



28
29
30
31
32
33
34
# File 'lib/dommy/performance.rb', line 28

def now
  return @window.scheduler.now_ms.to_f unless @real_time

  current = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
  @real_origin ||= current
  current - @real_origin
end