timers 4.1.1 → 4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 107c4a8a97789c9580117a8eca6cb72af1390d54
4
- data.tar.gz: 6687f8c8b655ce609a3ecf0a4519dd001baf4e2b
2
+ SHA256:
3
+ metadata.gz: 5f9fdee45ad2b102af90e2d529a1384a5f6b975d6ec3112dfafebac2d8574b0f
4
+ data.tar.gz: 5bec94f4fb24de10acaf4fba0e93514aa2912444f19188e6ecd1c6b6e3d25340
5
5
  SHA512:
6
- metadata.gz: 5f838fdcc30e595940ffc40325aea12578b16bf91a279feeafa247eb2d349d2724ec96715ab6d392531efcf7ff9f9e9a44a19b977bfb13a32629ead0a4358bda
7
- data.tar.gz: 1fba4a0bad3f7b284ed4a45e6cc1e156cf66082041ec7a767cad14471e3b82a0c4bf068ea874d5e9f6bb90e3a8a571c73ebb710cb50df0d809dc3d65b470fffb
6
+ metadata.gz: 712db02d6c449fca924459a952df2c9ae21058b7c492ce81d4b86f4989c69aa9ef90cc98ae421ab2f13934da6488d7d91a5718c2f1070714476147302c3d9b6e
7
+ data.tar.gz: c4a358d3bdc7e108afd016e42fdb478b8e717e3c400ae6cf0694e880b91112c9b118e7609d1c31143247d5d87db2577851b1e6002f2731e524a40d7e96c1cc28
@@ -1,5 +1,26 @@
1
+ # frozen_string_literal: true
1
2
 
2
- require 'timers/version'
3
+ # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
3
22
 
4
- require 'timers/group'
5
- require 'timers/wait'
23
+ require_relative "timers/version"
24
+
25
+ require_relative "timers/group"
26
+ require_relative "timers/wait"
@@ -1,116 +1,143 @@
1
+ # frozen_string_literal: true
1
2
 
2
- require 'forwardable'
3
- require 'hitimes'
3
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
4
22
 
5
- require 'timers/timer'
23
+ require_relative "timer"
6
24
 
7
25
  module Timers
8
- # Maintains an ordered list of events, which can be cancelled.
9
- class Events
10
- # Represents a cancellable handle for a specific timer event.
11
- class Handle
12
- def initialize(time, callback)
13
- @time = time
14
- @callback = callback
15
- end
16
-
17
- # The absolute time that the handle should be fired at.
18
- attr :time
19
-
20
- # Cancel this timer, O(1).
21
- def cancel!
22
- # The simplest way to keep track of cancelled status is to nullify the
23
- # callback. This should also be optimal for garbage collection.
24
- @callback = nil
25
- end
26
-
27
- # Has this timer been cancelled? Cancelled timer's don't fire.
28
- def cancelled?
29
- @callback.nil?
30
- end
31
-
32
- def > other
33
- @time > other.to_f
34
- end
35
-
36
- def to_f
37
- @time
38
- end
39
-
40
- # Fire the callback if not cancelled with the given time parameter.
41
- def fire(time)
42
- if @callback
43
- @callback.call(time)
44
- end
45
- end
46
- end
47
-
48
- def initialize
49
- # A sequence of handles, maintained in sorted order, future to present.
50
- # @sequence.last is the next event to be fired.
51
- @sequence = []
52
- end
53
-
54
- # Add an event at the given time.
55
- def schedule(time, callback)
56
- handle = Handle.new(time.to_f, callback)
57
-
58
- index = bisect_left(@sequence, handle)
59
-
60
- # Maintain sorted order, O(logN) insertion time.
61
- @sequence.insert(index, handle)
62
-
63
- return handle
64
- end
65
-
66
- # Returns the first non-cancelled handle.
67
- def first
68
- while handle = @sequence.last
69
- if handle.cancelled?
70
- @sequence.pop
71
- else
72
- return handle
73
- end
74
- end
75
- # @sequence.reverse.find { |handle| !handle.cancelled? }
76
- end
77
-
78
- # Returns the number of pending (possibly cancelled) events.
79
- def size
80
- @sequence.size
81
- end
82
-
83
- # Fire all handles for which Handle#time is less than the given time.
84
- def fire(time)
85
- pop(time).reverse_each do |handle|
86
- handle.fire(time)
87
- end
88
- end
89
-
90
- private
91
-
92
- # Efficiently take k handles for which Handle#time is less than the given
93
- # time.
94
- def pop(time)
95
- index = bisect_left(@sequence, time)
96
-
97
- @sequence.pop(@sequence.size - index)
98
- end
99
-
100
- # Return the left-most index where to insert item e, in a list a, assuming
101
- # a is sorted in descending order.
102
- def bisect_left(a, e, l = 0, u = a.length)
103
- while l < u
104
- m = l + (u-l).div(2)
105
-
106
- if a[m] > e
107
- l = m+1
108
- else
109
- u = m
110
- end
111
- end
112
-
113
- return l
114
- end
115
- end
26
+ # Maintains an ordered list of events, which can be cancelled.
27
+ class Events
28
+ # Represents a cancellable handle for a specific timer event.
29
+ class Handle
30
+ def initialize(time, callback)
31
+ @time = time
32
+ @callback = callback
33
+ end
34
+
35
+ # The absolute time that the handle should be fired at.
36
+ attr_reader :time
37
+
38
+ # Cancel this timer, O(1).
39
+ def cancel!
40
+ # The simplest way to keep track of cancelled status is to nullify the
41
+ # callback. This should also be optimal for garbage collection.
42
+ @callback = nil
43
+ end
44
+
45
+ # Has this timer been cancelled? Cancelled timer's don't fire.
46
+ def cancelled?
47
+ @callback.nil?
48
+ end
49
+
50
+ def > other
51
+ @time > other.to_f
52
+ end
53
+
54
+ def >= other
55
+ @time >= other.to_f
56
+ end
57
+
58
+ def to_f
59
+ @time
60
+ end
61
+
62
+ # Fire the callback if not cancelled with the given time parameter.
63
+ def fire(time)
64
+ @callback.call(time) if @callback
65
+ end
66
+ end
67
+
68
+ def initialize
69
+ # A sequence of handles, maintained in sorted order, future to present.
70
+ # @sequence.last is the next event to be fired.
71
+ @sequence = []
72
+ @queue = []
73
+ end
74
+
75
+ # Add an event at the given time.
76
+ def schedule(time, callback)
77
+ handle = Handle.new(time.to_f, callback)
78
+
79
+ @queue << handle
80
+
81
+ return handle
82
+ end
83
+
84
+ # Returns the first non-cancelled handle.
85
+ def first
86
+ merge!
87
+
88
+ while (handle = @sequence.last)
89
+ return handle unless handle.cancelled?
90
+ @sequence.pop
91
+ end
92
+ end
93
+
94
+ # Returns the number of pending (possibly cancelled) events.
95
+ def size
96
+ @sequence.size + @queue.size
97
+ end
98
+
99
+ # Fire all handles for which Handle#time is less than the given time.
100
+ def fire(time)
101
+ merge!
102
+
103
+ while handle = @sequence.last and handle.time <= time
104
+ @sequence.pop
105
+ handle.fire(time)
106
+ end
107
+ end
108
+
109
+ private
110
+
111
+ def merge!
112
+ while handle = @queue.pop
113
+ next if handle.cancelled?
114
+
115
+ index = bisect_right(@sequence, handle)
116
+
117
+ if current_handle = @sequence[index] and current_handle.cancelled?
118
+ # puts "Replacing handle at index: #{index} due to cancellation in array containing #{@sequence.size} item(s)."
119
+ @sequence[index] = handle
120
+ else
121
+ # puts "Inserting handle at index: #{index} in array containing #{@sequence.size} item(s)."
122
+ @sequence.insert(index, handle)
123
+ end
124
+ end
125
+ end
126
+
127
+ # Return the right-most index where to insert item e, in a list a, assuming
128
+ # a is sorted in descending order.
129
+ def bisect_right(a, e, l = 0, u = a.length)
130
+ while l < u
131
+ m = l + (u - l).div(2)
132
+
133
+ if a[m] >= e
134
+ l = m + 1
135
+ else
136
+ u = m
137
+ end
138
+ end
139
+
140
+ l
141
+ end
142
+ end
116
143
  end
@@ -1,132 +1,147 @@
1
-
2
- require 'set'
3
- require 'forwardable'
4
- require 'hitimes'
5
-
6
- require 'timers/timer'
7
- require 'timers/events'
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require "set"
24
+ require "forwardable"
25
+
26
+ require_relative "interval"
27
+ require_relative "timer"
28
+ require_relative "events"
8
29
 
9
30
  module Timers
10
- class Group
11
- include Enumerable
12
-
13
- extend Forwardable
14
- def_delegators :@timers, :each, :empty?
15
-
16
- def initialize
17
- @events = Events.new
18
-
19
- @timers = Set.new
20
- @paused_timers = Set.new
21
-
22
- @interval = Hitimes::Interval.new
23
- @interval.start
24
- end
25
-
26
- # Scheduled events:
27
- attr :events
28
-
29
- # Active timers:
30
- attr :timers
31
-
32
- # Paused timers:
33
- attr :paused_timers
34
-
35
- # Call the given block after the given interval. The first argument will be
36
- # the time at which the group was asked to fire timers for.
37
- def after(interval, &block)
38
- Timer.new(self, interval, false, &block)
39
- end
40
-
41
- # Call the given block immediately, and then after the given interval. The first
42
- # argument will be the time at which the group was asked to fire timers for.
43
- def now_and_after(interval, &block)
44
- block.call
45
- after(interval, &block)
46
- end
47
-
48
- # Call the given block periodically at the given interval. The first
49
- # argument will be the time at which the group was asked to fire timers for.
50
- def every(interval, recur = true, &block)
51
- Timer.new(self, interval, recur, &block)
52
- end
53
-
54
- # Call the given block immediately, and then periodically at the given interval. The first
55
- # argument will be the time at which the group was asked to fire timers for.
56
- def now_and_every(interval, recur = true, &block)
57
- block.call
58
- every(interval, recur, &block)
59
- end
60
-
61
- # Wait for the next timer and fire it. Can take a block, which should behave
62
- # like sleep(n), except that n may be nil (sleep forever) or a negative
63
- # number (fire immediately after return).
64
- def wait(&block)
65
- if block_given?
66
- yield wait_interval
67
-
68
- while interval = wait_interval and interval > 0
69
- yield interval
70
- end
71
- else
72
- while interval = wait_interval and interval > 0
73
- # We cannot assume that sleep will wait for the specified time, it might be +/- a bit.
74
- sleep interval
75
- end
76
- end
77
-
78
- return fire
79
- end
80
-
81
- # Interval to wait until when the next timer will fire.
82
- # - nil: no timers
83
- # - -ve: timers expired already
84
- # - 0: timers ready to fire
85
- # - +ve: timers waiting to fire
86
- def wait_interval(offset = self.current_offset)
87
- if handle = @events.first
88
- return handle.time - Float(offset)
89
- end
90
- end
91
-
92
- # Fire all timers that are ready.
93
- def fire(offset = self.current_offset)
94
- @events.fire(offset)
95
- end
96
-
97
- # Pause all timers.
98
- def pause
99
- @timers.dup.each do |timer|
100
- timer.pause
101
- end
102
- end
103
-
104
- # Resume all timers.
105
- def resume
106
- @paused_timers.dup.each do |timer|
107
- timer.resume
108
- end
109
- end
110
-
111
- alias_method :continue, :resume
112
-
113
- # Delay all timers.
114
- def delay(seconds)
115
- @timers.each do |timer|
116
- timer.delay(seconds)
117
- end
118
- end
119
-
120
- # Cancel all timers.
121
- def cancel
122
- @timers.dup.each do |timer|
123
- timer.cancel
124
- end
125
- end
126
-
127
- # The group's current time.
128
- def current_offset
129
- @interval.to_f
130
- end
131
- end
31
+ # A collection of timers which may fire at different times
32
+ class Group
33
+ include Enumerable
34
+
35
+ extend Forwardable
36
+ def_delegators :@timers, :each, :empty?
37
+
38
+ def initialize
39
+ @events = Events.new
40
+
41
+ @timers = Set.new
42
+ @paused_timers = Set.new
43
+
44
+ @interval = Interval.new
45
+ @interval.start
46
+ end
47
+
48
+ # Scheduled events:
49
+ attr_reader :events
50
+
51
+ # Active timers:
52
+ attr_reader :timers
53
+
54
+ # Paused timers:
55
+ attr_reader :paused_timers
56
+
57
+ # Call the given block after the given interval. The first argument will be
58
+ # the time at which the group was asked to fire timers for.
59
+ def after(interval, &block)
60
+ Timer.new(self, interval, false, &block)
61
+ end
62
+
63
+ # Call the given block immediately, and then after the given interval. The first
64
+ # argument will be the time at which the group was asked to fire timers for.
65
+ def now_and_after(interval, &block)
66
+ yield
67
+ after(interval, &block)
68
+ end
69
+
70
+ # Call the given block periodically at the given interval. The first
71
+ # argument will be the time at which the group was asked to fire timers for.
72
+ def every(interval, recur = true, &block)
73
+ Timer.new(self, interval, recur, &block)
74
+ end
75
+
76
+ # Call the given block immediately, and then periodically at the given interval. The first
77
+ # argument will be the time at which the group was asked to fire timers for.
78
+ def now_and_every(interval, recur = true, &block)
79
+ yield
80
+ every(interval, recur, &block)
81
+ end
82
+
83
+ # Wait for the next timer and fire it. Can take a block, which should behave
84
+ # like sleep(n), except that n may be nil (sleep forever) or a negative
85
+ # number (fire immediately after return).
86
+ def wait
87
+ if block_given?
88
+ yield wait_interval
89
+
90
+ while (interval = wait_interval) && interval > 0
91
+ yield interval
92
+ end
93
+ else
94
+ while (interval = wait_interval) && interval > 0
95
+ # We cannot assume that sleep will wait for the specified time, it might be +/- a bit.
96
+ sleep interval
97
+ end
98
+ end
99
+
100
+ fire
101
+ end
102
+
103
+ # Interval to wait until when the next timer will fire.
104
+ # - nil: no timers
105
+ # - -ve: timers expired already
106
+ # - 0: timers ready to fire
107
+ # - +ve: timers waiting to fire
108
+ def wait_interval(offset = current_offset)
109
+ handle = @events.first
110
+ handle.time - Float(offset) if handle
111
+ end
112
+
113
+ # Fire all timers that are ready.
114
+ def fire(offset = current_offset)
115
+ @events.fire(offset)
116
+ end
117
+
118
+ # Pause all timers.
119
+ def pause
120
+ @timers.dup.each(&:pause)
121
+ end
122
+
123
+ # Resume all timers.
124
+ def resume
125
+ @paused_timers.dup.each(&:resume)
126
+ end
127
+
128
+ alias continue resume
129
+
130
+ # Delay all timers.
131
+ def delay(seconds)
132
+ @timers.each do |timer|
133
+ timer.delay(seconds)
134
+ end
135
+ end
136
+
137
+ # Cancel all timers.
138
+ def cancel
139
+ @timers.dup.each(&:cancel)
140
+ end
141
+
142
+ # The group's current time.
143
+ def current_offset
144
+ @interval.to_f
145
+ end
146
+ end
132
147
  end