uv-rays 2.3.0 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,409 +1,409 @@
1
- # frozen_string_literal: true
2
-
3
- module UV
4
-
5
- class ScheduledEvent < ::Libuv::Q::DeferredPromise
6
- # Note:: Comparable should not effect Hashes
7
- # it will however effect arrays
8
- include Comparable
9
-
10
- attr_reader :created
11
- attr_reader :last_scheduled
12
- attr_reader :next_scheduled
13
- attr_reader :trigger_count
14
-
15
- def initialize(scheduler)
16
- # Create a dummy deferrable
17
- reactor = scheduler.reactor
18
- defer = reactor.defer
19
-
20
- # Record a backtrace of where the schedule was created
21
- @trace = caller
22
-
23
- # Setup common event variables
24
- @scheduler = scheduler
25
- @created = reactor.now
26
- @last_scheduled = @created
27
- @trigger_count = 0
28
-
29
- # init the promise
30
- super(reactor, defer)
31
- end
32
-
33
- # Provide relevant inspect information
34
- def inspect
35
- insp = String.new("#<#{self.class}:#{"0x00%x" % (self.__id__ << 1)} ")
36
- insp << "trigger_count=#{@trigger_count} "
37
- insp << "config=#{info} " if self.respond_to?(:info, true)
38
- insp << "next_scheduled=#{to_time(@next_scheduled)} "
39
- insp << "last_scheduled=#{to_time(@last_scheduled)} created=#{to_time(@created)}>"
40
- insp
41
- end
42
- alias_method :to_s, :inspect
43
-
44
- def to_time(internal_time)
45
- if internal_time
46
- ((internal_time + @scheduler.time_diff) / 1000).to_i
47
- else
48
- internal_time
49
- end
50
- end
51
-
52
-
53
- # required for comparable
54
- def <=>(anOther)
55
- @next_scheduled <=> anOther.next_scheduled
56
- end
57
-
58
- # reject the promise
59
- def cancel
60
- @defer.reject(:cancelled)
61
- end
62
-
63
- # notify listeners of the event
64
- def trigger
65
- @trigger_count += 1
66
- @defer.notify(@reactor.now, self)
67
- end
68
- end
69
-
70
- class OneShot < ScheduledEvent
71
- def initialize(scheduler, at)
72
- super(scheduler)
73
-
74
- @next_scheduled = at
75
- end
76
-
77
- # Updates the scheduled time
78
- def update(time)
79
- @last_scheduled = @reactor.now
80
-
81
- parsed_time = Scheduler.parse_in(time, :quiet)
82
- if parsed_time.nil?
83
- # Parse at will throw an error if time is invalid
84
- parsed_time = Scheduler.parse_at(time) - @scheduler.time_diff
85
- else
86
- parsed_time += @last_scheduled
87
- end
88
-
89
- @next_scheduled = parsed_time
90
- @scheduler.reschedule(self)
91
- end
92
-
93
- # Runs the event and cancels the schedule
94
- def trigger
95
- super()
96
- @defer.resolve(:triggered)
97
- end
98
- end
99
-
100
- class Repeat < ScheduledEvent
101
- def initialize(scheduler, every)
102
- super(scheduler)
103
-
104
- @every = every
105
- next_time
106
- end
107
-
108
- # Update the time period of the repeating event
109
- #
110
- # @param schedule [String] a standard CRON job line or a human readable string representing a time period.
111
- def update(every, timezone: nil)
112
- time = Scheduler.parse_in(every, :quiet) || Scheduler.parse_cron(every, :quiet, timezone: timezone)
113
- raise ArgumentError.new("couldn't parse \"#{o}\"") if time.nil?
114
-
115
- @every = time
116
- reschedule
117
- end
118
-
119
- # removes the event from the schedule
120
- def pause
121
- @paused = true
122
- @scheduler.unschedule(self)
123
- end
124
-
125
- # reschedules the event to the next time period
126
- # can be used to reset a repeating timer
127
- def resume
128
- @paused = false
129
- @last_scheduled = @reactor.now
130
- reschedule
131
- end
132
-
133
- # Runs the event and reschedules
134
- def trigger
135
- super()
136
- @reactor.next_tick do
137
- # Do this next tick to avoid needless scheduling
138
- # if the event is stopped in the callback
139
- reschedule
140
- end
141
- end
142
-
143
-
144
- protected
145
-
146
-
147
- def next_time
148
- @last_scheduled = @reactor.now
149
- if @every.is_a? Integer
150
- @next_scheduled = @last_scheduled + @every
151
- else
152
- # must be a cron
153
- @next_scheduled = (@every.next.to_f * 1000).to_i - @scheduler.time_diff
154
- end
155
- end
156
-
157
- def reschedule
158
- unless @paused
159
- next_time
160
- @scheduler.reschedule(self)
161
- end
162
- end
163
-
164
- def info
165
- "repeat:#{@every.inspect}"
166
- end
167
- end
168
-
169
-
170
- class Scheduler
171
- attr_reader :reactor
172
- attr_reader :time_diff
173
- attr_reader :next
174
-
175
-
176
- def initialize(reactor)
177
- @reactor = reactor
178
- @schedules = Set.new
179
- @scheduled = []
180
- @next = nil # Next schedule time
181
- @timer = nil # Reference to the timer
182
- @timer_callback = method(:on_timer)
183
-
184
- # Not really required when used correctly
185
- @critical = Mutex.new
186
-
187
- # Every hour we should re-calibrate this (just in case)
188
- calibrate_time
189
-
190
- @calibrate = @reactor.timer do
191
- calibrate_time
192
- @calibrate.start(3600000)
193
- end
194
- @calibrate.start(3600000)
195
- @calibrate.unref
196
- end
197
-
198
-
199
- # As the libuv time is taken from an arbitrary point in time we
200
- # need to roughly synchronize between it and ruby's Time.now
201
- def calibrate_time
202
- @reactor.update_time
203
- @time_diff = (Time.now.to_f * 1000).to_i - @reactor.now
204
- end
205
-
206
- # Create a repeating event that occurs each time period
207
- #
208
- # @param time [String] a human readable string representing the time period. 3w2d4h1m2s for example.
209
- # @param callback [Proc] a block or method to execute when the event triggers
210
- # @return [::UV::Repeat]
211
- def every(time, callback = nil, &block)
212
- callback ||= block
213
- ms = Scheduler.parse_in(time)
214
- event = Repeat.new(self, ms)
215
-
216
- if callback.respond_to? :call
217
- event.progress callback
218
- end
219
- schedule(event)
220
- event
221
- end
222
-
223
- # Create a one off event that occurs after the time period
224
- #
225
- # @param time [String] a human readable string representing the time period. 3w2d4h1m2s for example.
226
- # @param callback [Proc] a block or method to execute when the event triggers
227
- # @return [::UV::OneShot]
228
- def in(time, callback = nil, &block)
229
- callback ||= block
230
- ms = @reactor.now + Scheduler.parse_in(time)
231
- event = OneShot.new(self, ms)
232
-
233
- if callback.respond_to? :call
234
- event.progress callback
235
- end
236
- schedule(event)
237
- event
238
- end
239
-
240
- # Create a one off event that occurs at a particular date and time
241
- #
242
- # @param time [String, Time] a representation of a date and time that can be parsed
243
- # @param callback [Proc] a block or method to execute when the event triggers
244
- # @return [::UV::OneShot]
245
- def at(time, callback = nil, &block)
246
- callback ||= block
247
- ms = Scheduler.parse_at(time) - @time_diff
248
- event = OneShot.new(self, ms)
249
-
250
- if callback.respond_to? :call
251
- event.progress callback
252
- end
253
- schedule(event)
254
- event
255
- end
256
-
257
- # Create a repeating event that uses a CRON line to determine the trigger time
258
- #
259
- # @param schedule [String] a standard CRON job line.
260
- # @param callback [Proc] a block or method to execute when the event triggers
261
- # @return [::UV::Repeat]
262
- def cron(schedule, callback = nil, timezone: nil, &block)
263
- callback ||= block
264
- ms = Scheduler.parse_cron(schedule, timezone: timezone)
265
- event = Repeat.new(self, ms)
266
-
267
- if callback.respond_to? :call
268
- event.progress callback
269
- end
270
- schedule(event)
271
- event
272
- end
273
-
274
- # Schedules an event for execution
275
- #
276
- # @param event [ScheduledEvent]
277
- def reschedule(event)
278
- # Check promise is not resolved
279
- return if event.resolved?
280
-
281
- @critical.synchronize {
282
- # Remove the event from the scheduled list and ensure it is in the schedules set
283
- if @schedules.include?(event)
284
- remove(event)
285
- else
286
- @schedules << event
287
- end
288
-
289
- # optimal algorithm for inserting into an already sorted list
290
- Bisect.insort(@scheduled, event)
291
-
292
- # Update the timer
293
- check_timer
294
- }
295
- end
296
-
297
- # Removes an event from the schedule
298
- #
299
- # @param event [ScheduledEvent]
300
- def unschedule(event)
301
- @critical.synchronize {
302
- # Only call delete and update the timer when required
303
- if @schedules.include?(event)
304
- @schedules.delete(event)
305
- remove(event)
306
- check_timer
307
- end
308
- }
309
- end
310
-
311
-
312
- private
313
-
314
-
315
- # Remove an element from the array
316
- def remove(obj)
317
- position = nil
318
-
319
- @scheduled.each_index do |i|
320
- # object level comparison
321
- if obj.equal? @scheduled[i]
322
- position = i
323
- break
324
- end
325
- end
326
-
327
- @scheduled.slice!(position) unless position.nil?
328
- end
329
-
330
- # First time schedule we want to bind to the promise
331
- def schedule(event)
332
- reschedule(event)
333
-
334
- event.finally do
335
- unschedule event
336
- end
337
- end
338
-
339
- # Ensures the current timer, if any, is still
340
- # accurate by checking the head of the schedule
341
- def check_timer
342
- @reactor.update_time
343
-
344
- existing = @next
345
- schedule = @scheduled.first
346
- @next = schedule.nil? ? nil : schedule.next_scheduled
347
-
348
- if existing != @next
349
- # lazy load the timer
350
- if @timer.nil?
351
- new_timer
352
- else
353
- @timer.stop
354
- end
355
-
356
- if not @next.nil?
357
- in_time = @next - @reactor.now
358
-
359
- # Ensure there are never negative start times
360
- if in_time > 3
361
- @timer.start(in_time)
362
- else
363
- # Effectively next tick
364
- @timer.start(0)
365
- end
366
- end
367
- end
368
- end
369
-
370
- # Is called when the libuv timer fires
371
- def on_timer
372
- @critical.synchronize {
373
- schedule = @scheduled.shift
374
- @schedules.delete(schedule)
375
- schedule.trigger
376
-
377
- # execute schedules that are within 3ms of this event
378
- # Basic timer coalescing..
379
- now = @reactor.now + 3
380
- while @scheduled.first && @scheduled.first.next_scheduled <= now
381
- schedule = @scheduled.shift
382
- @schedules.delete(schedule)
383
- schedule.trigger
384
- end
385
- check_timer
386
- }
387
- end
388
-
389
- # Provide some assurances on timer failure
390
- def new_timer
391
- @timer = @reactor.timer @timer_callback
392
- @timer.finally do
393
- new_timer
394
- unless @next.nil?
395
- @timer.start(@next)
396
- end
397
- end
398
- end
399
- end
400
- end
401
-
402
- module Libuv
403
- class Reactor
404
- def scheduler
405
- @scheduler ||= UV::Scheduler.new(@reactor)
406
- @scheduler
407
- end
408
- end
409
- end
1
+ # frozen_string_literal: true
2
+
3
+ module UV
4
+
5
+ class ScheduledEvent < ::Libuv::Q::DeferredPromise
6
+ # Note:: Comparable should not effect Hashes
7
+ # it will however effect arrays
8
+ include Comparable
9
+
10
+ attr_reader :created
11
+ attr_reader :last_scheduled
12
+ attr_reader :next_scheduled
13
+ attr_reader :trigger_count
14
+
15
+ def initialize(scheduler)
16
+ # Create a dummy deferrable
17
+ reactor = scheduler.reactor
18
+ defer = reactor.defer
19
+
20
+ # Record a backtrace of where the schedule was created
21
+ @trace = caller
22
+
23
+ # Setup common event variables
24
+ @scheduler = scheduler
25
+ @created = reactor.now
26
+ @last_scheduled = @created
27
+ @trigger_count = 0
28
+
29
+ # init the promise
30
+ super(reactor, defer)
31
+ end
32
+
33
+ # Provide relevant inspect information
34
+ def inspect
35
+ insp = String.new("#<#{self.class}:#{"0x00%x" % (self.__id__ << 1)} ")
36
+ insp << "trigger_count=#{@trigger_count} "
37
+ insp << "config=#{info} " if self.respond_to?(:info, true)
38
+ insp << "next_scheduled=#{to_time(@next_scheduled)} "
39
+ insp << "last_scheduled=#{to_time(@last_scheduled)} created=#{to_time(@created)}>"
40
+ insp
41
+ end
42
+ alias_method :to_s, :inspect
43
+
44
+ def to_time(internal_time)
45
+ if internal_time
46
+ ((internal_time + @scheduler.time_diff) / 1000).to_i
47
+ else
48
+ internal_time
49
+ end
50
+ end
51
+
52
+
53
+ # required for comparable
54
+ def <=>(anOther)
55
+ @next_scheduled <=> anOther.next_scheduled
56
+ end
57
+
58
+ # reject the promise
59
+ def cancel
60
+ @defer.reject(:cancelled)
61
+ end
62
+
63
+ # notify listeners of the event
64
+ def trigger
65
+ @trigger_count += 1
66
+ @defer.notify(@reactor.now, self)
67
+ end
68
+ end
69
+
70
+ class OneShot < ScheduledEvent
71
+ def initialize(scheduler, at)
72
+ super(scheduler)
73
+
74
+ @next_scheduled = at
75
+ end
76
+
77
+ # Updates the scheduled time
78
+ def update(time)
79
+ @last_scheduled = @reactor.now
80
+
81
+ parsed_time = Scheduler.parse_in(time, :quiet)
82
+ if parsed_time.nil?
83
+ # Parse at will throw an error if time is invalid
84
+ parsed_time = Scheduler.parse_at(time) - @scheduler.time_diff
85
+ else
86
+ parsed_time += @last_scheduled
87
+ end
88
+
89
+ @next_scheduled = parsed_time
90
+ @scheduler.reschedule(self)
91
+ end
92
+
93
+ # Runs the event and cancels the schedule
94
+ def trigger
95
+ super()
96
+ @defer.resolve(:triggered)
97
+ end
98
+ end
99
+
100
+ class Repeat < ScheduledEvent
101
+ def initialize(scheduler, every)
102
+ super(scheduler)
103
+
104
+ @every = every
105
+ next_time
106
+ end
107
+
108
+ # Update the time period of the repeating event
109
+ #
110
+ # @param schedule [String] a standard CRON job line or a human readable string representing a time period.
111
+ def update(every, timezone: nil)
112
+ time = Scheduler.parse_in(every, :quiet) || Scheduler.parse_cron(every, :quiet, timezone: timezone)
113
+ raise ArgumentError.new("couldn't parse \"#{o}\"") if time.nil?
114
+
115
+ @every = time
116
+ reschedule
117
+ end
118
+
119
+ # removes the event from the schedule
120
+ def pause
121
+ @paused = true
122
+ @scheduler.unschedule(self)
123
+ end
124
+
125
+ # reschedules the event to the next time period
126
+ # can be used to reset a repeating timer
127
+ def resume
128
+ @paused = false
129
+ @last_scheduled = @reactor.now
130
+ reschedule
131
+ end
132
+
133
+ # Runs the event and reschedules
134
+ def trigger
135
+ super()
136
+ @reactor.next_tick do
137
+ # Do this next tick to avoid needless scheduling
138
+ # if the event is stopped in the callback
139
+ reschedule
140
+ end
141
+ end
142
+
143
+
144
+ protected
145
+
146
+
147
+ def next_time
148
+ @last_scheduled = @reactor.now
149
+ if @every.is_a? Integer
150
+ @next_scheduled = @last_scheduled + @every
151
+ else
152
+ # must be a cron
153
+ @next_scheduled = (@every.next.to_f * 1000).to_i - @scheduler.time_diff
154
+ end
155
+ end
156
+
157
+ def reschedule
158
+ unless @paused
159
+ next_time
160
+ @scheduler.reschedule(self)
161
+ end
162
+ end
163
+
164
+ def info
165
+ "repeat:#{@every.inspect}"
166
+ end
167
+ end
168
+
169
+
170
+ class Scheduler
171
+ attr_reader :reactor
172
+ attr_reader :time_diff
173
+ attr_reader :next
174
+
175
+
176
+ def initialize(reactor)
177
+ @reactor = reactor
178
+ @schedules = Set.new
179
+ @scheduled = []
180
+ @next = nil # Next schedule time
181
+ @timer = nil # Reference to the timer
182
+ @timer_callback = method(:on_timer)
183
+
184
+ # Not really required when used correctly
185
+ @critical = Mutex.new
186
+
187
+ # Every hour we should re-calibrate this (just in case)
188
+ calibrate_time
189
+
190
+ @calibrate = @reactor.timer do
191
+ calibrate_time
192
+ @calibrate.start(3600000)
193
+ end
194
+ @calibrate.start(3600000)
195
+ @calibrate.unref
196
+ end
197
+
198
+
199
+ # As the libuv time is taken from an arbitrary point in time we
200
+ # need to roughly synchronize between it and ruby's Time.now
201
+ def calibrate_time
202
+ @reactor.update_time
203
+ @time_diff = (Time.now.to_f * 1000).to_i - @reactor.now
204
+ end
205
+
206
+ # Create a repeating event that occurs each time period
207
+ #
208
+ # @param time [String] a human readable string representing the time period. 3w2d4h1m2s for example.
209
+ # @param callback [Proc] a block or method to execute when the event triggers
210
+ # @return [::UV::Repeat]
211
+ def every(time, callback = nil, &block)
212
+ callback ||= block
213
+ ms = Scheduler.parse_in(time)
214
+ event = Repeat.new(self, ms)
215
+
216
+ if callback.respond_to? :call
217
+ event.progress callback
218
+ end
219
+ schedule(event)
220
+ event
221
+ end
222
+
223
+ # Create a one off event that occurs after the time period
224
+ #
225
+ # @param time [String] a human readable string representing the time period. 3w2d4h1m2s for example.
226
+ # @param callback [Proc] a block or method to execute when the event triggers
227
+ # @return [::UV::OneShot]
228
+ def in(time, callback = nil, &block)
229
+ callback ||= block
230
+ ms = @reactor.now + Scheduler.parse_in(time)
231
+ event = OneShot.new(self, ms)
232
+
233
+ if callback.respond_to? :call
234
+ event.progress callback
235
+ end
236
+ schedule(event)
237
+ event
238
+ end
239
+
240
+ # Create a one off event that occurs at a particular date and time
241
+ #
242
+ # @param time [String, Time] a representation of a date and time that can be parsed
243
+ # @param callback [Proc] a block or method to execute when the event triggers
244
+ # @return [::UV::OneShot]
245
+ def at(time, callback = nil, &block)
246
+ callback ||= block
247
+ ms = Scheduler.parse_at(time) - @time_diff
248
+ event = OneShot.new(self, ms)
249
+
250
+ if callback.respond_to? :call
251
+ event.progress callback
252
+ end
253
+ schedule(event)
254
+ event
255
+ end
256
+
257
+ # Create a repeating event that uses a CRON line to determine the trigger time
258
+ #
259
+ # @param schedule [String] a standard CRON job line.
260
+ # @param callback [Proc] a block or method to execute when the event triggers
261
+ # @return [::UV::Repeat]
262
+ def cron(schedule, callback = nil, timezone: nil, &block)
263
+ callback ||= block
264
+ ms = Scheduler.parse_cron(schedule, timezone: timezone)
265
+ event = Repeat.new(self, ms)
266
+
267
+ if callback.respond_to? :call
268
+ event.progress callback
269
+ end
270
+ schedule(event)
271
+ event
272
+ end
273
+
274
+ # Schedules an event for execution
275
+ #
276
+ # @param event [ScheduledEvent]
277
+ def reschedule(event)
278
+ # Check promise is not resolved
279
+ return if event.resolved?
280
+
281
+ @critical.synchronize {
282
+ # Remove the event from the scheduled list and ensure it is in the schedules set
283
+ if @schedules.include?(event)
284
+ remove(event)
285
+ else
286
+ @schedules << event
287
+ end
288
+
289
+ # optimal algorithm for inserting into an already sorted list
290
+ Bisect.insort(@scheduled, event)
291
+
292
+ # Update the timer
293
+ check_timer
294
+ }
295
+ end
296
+
297
+ # Removes an event from the schedule
298
+ #
299
+ # @param event [ScheduledEvent]
300
+ def unschedule(event)
301
+ @critical.synchronize {
302
+ # Only call delete and update the timer when required
303
+ if @schedules.include?(event)
304
+ @schedules.delete(event)
305
+ remove(event)
306
+ check_timer
307
+ end
308
+ }
309
+ end
310
+
311
+
312
+ private
313
+
314
+
315
+ # Remove an element from the array
316
+ def remove(obj)
317
+ position = nil
318
+
319
+ @scheduled.each_index do |i|
320
+ # object level comparison
321
+ if obj.equal? @scheduled[i]
322
+ position = i
323
+ break
324
+ end
325
+ end
326
+
327
+ @scheduled.slice!(position) unless position.nil?
328
+ end
329
+
330
+ # First time schedule we want to bind to the promise
331
+ def schedule(event)
332
+ reschedule(event)
333
+
334
+ event.finally do
335
+ unschedule event
336
+ end
337
+ end
338
+
339
+ # Ensures the current timer, if any, is still
340
+ # accurate by checking the head of the schedule
341
+ def check_timer
342
+ @reactor.update_time
343
+
344
+ existing = @next
345
+ schedule = @scheduled.first
346
+ @next = schedule.nil? ? nil : schedule.next_scheduled
347
+
348
+ if existing != @next
349
+ # lazy load the timer
350
+ if @timer.nil?
351
+ new_timer
352
+ else
353
+ @timer.stop
354
+ end
355
+
356
+ if not @next.nil?
357
+ in_time = @next - @reactor.now
358
+
359
+ # Ensure there are never negative start times
360
+ if in_time > 3
361
+ @timer.start(in_time)
362
+ else
363
+ # Effectively next tick
364
+ @timer.start(0)
365
+ end
366
+ end
367
+ end
368
+ end
369
+
370
+ # Is called when the libuv timer fires
371
+ def on_timer
372
+ @critical.synchronize {
373
+ schedule = @scheduled.shift
374
+ @schedules.delete(schedule)
375
+ schedule.trigger
376
+
377
+ # execute schedules that are within 3ms of this event
378
+ # Basic timer coalescing..
379
+ now = @reactor.now + 3
380
+ while @scheduled.first && @scheduled.first.next_scheduled <= now
381
+ schedule = @scheduled.shift
382
+ @schedules.delete(schedule)
383
+ schedule.trigger
384
+ end
385
+ check_timer
386
+ }
387
+ end
388
+
389
+ # Provide some assurances on timer failure
390
+ def new_timer
391
+ @timer = @reactor.timer @timer_callback
392
+ @timer.finally do
393
+ new_timer
394
+ unless @next.nil?
395
+ @timer.start(@next)
396
+ end
397
+ end
398
+ end
399
+ end
400
+ end
401
+
402
+ module Libuv
403
+ class Reactor
404
+ def scheduler
405
+ @scheduler ||= UV::Scheduler.new(@reactor)
406
+ @scheduler
407
+ end
408
+ end
409
+ end