timers 4.1.2 → 4.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 97c81b8ed305099faee9ecec3ee32f6960b70d54
4
- data.tar.gz: 90d94a796bfe60e78963b0053648396aefdb19b6
2
+ SHA256:
3
+ metadata.gz: 52ef1e26aab6a7bbbf5c9d687028bdc36f52810dd7ebc0c76873b6bef9f30ca2
4
+ data.tar.gz: 26105fe04a6d2bfcdf9efb46ebd83d9562a08af3c2f113892a8be57713be6112
5
5
  SHA512:
6
- metadata.gz: dbea5311bcd5fb7bfe7c98191ac15146647b91ab5c994309f3bc14fe394ac97832361afa6ff7f257d844a802265125a5bafa051026fa1eed979f8310d8a21f87
7
- data.tar.gz: 2097c9e9d6e838550fa3c4735db828173852dd5e598670a376a04de132274df6464687cfe0e72992c2469d5d355932808fe1147e44ec5f26e2162046267c37ad
6
+ metadata.gz: 2a3ae0e0d9644627fef3aeffdfde86456d74c28ab89194486743fee2a25e8832c5aafb05f88174f6ee27d4f9402e05b4f80776c49cc34d70da527e63f9097685
7
+ data.tar.gz: 1f30b97aa11f6f42b3ce1786ae42f06334a86064306601635774b8d84cc88df765e8312a455798984520d44e3db35f8094fb23ec35cb736eb2191bfa27dece0c
@@ -1,6 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- 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.
4
22
 
5
- require "timers/group"
6
- require "timers/wait"
23
+ require_relative "timers/version"
24
+
25
+ require_relative "timers/group"
26
+ require_relative "timers/wait"
@@ -1,111 +1,143 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "forwardable"
4
- require "hitimes"
5
-
6
- require "timers/timer"
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_relative "timer"
7
24
 
8
25
  module Timers
9
- # Maintains an ordered list of events, which can be cancelled.
10
- class Events
11
- # Represents a cancellable handle for a specific timer event.
12
- class Handle
13
- def initialize(time, callback)
14
- @time = time
15
- @callback = callback
16
- end
17
-
18
- # The absolute time that the handle should be fired at.
19
- attr_reader :time
20
-
21
- # Cancel this timer, O(1).
22
- def cancel!
23
- # The simplest way to keep track of cancelled status is to nullify the
24
- # callback. This should also be optimal for garbage collection.
25
- @callback = nil
26
- end
27
-
28
- # Has this timer been cancelled? Cancelled timer's don't fire.
29
- def cancelled?
30
- @callback.nil?
31
- end
32
-
33
- def >(other)
34
- @time > other.to_f
35
- end
36
-
37
- def to_f
38
- @time
39
- end
40
-
41
- # Fire the callback if not cancelled with the given time parameter.
42
- def fire(time)
43
- @callback.call(time) if @callback
44
- end
45
- end
46
-
47
- def initialize
48
- # A sequence of handles, maintained in sorted order, future to present.
49
- # @sequence.last is the next event to be fired.
50
- @sequence = []
51
- end
52
-
53
- # Add an event at the given time.
54
- def schedule(time, callback)
55
- handle = Handle.new(time.to_f, callback)
56
-
57
- index = bisect_left(@sequence, handle)
58
-
59
- # Maintain sorted order, O(logN) insertion time.
60
- @sequence.insert(index, handle)
61
-
62
- handle
63
- end
64
-
65
- # Returns the first non-cancelled handle.
66
- def first
67
- while (handle = @sequence.last)
68
- return handle unless handle.cancelled?
69
- @sequence.pop
70
- end
71
- end
72
-
73
- # Returns the number of pending (possibly cancelled) events.
74
- def size
75
- @sequence.size
76
- end
77
-
78
- # Fire all handles for which Handle#time is less than the given time.
79
- def fire(time)
80
- pop(time).reverse_each do |handle|
81
- handle.fire(time)
82
- end
83
- end
84
-
85
- private
86
-
87
- # Efficiently take k handles for which Handle#time is less than the given
88
- # time.
89
- def pop(time)
90
- index = bisect_left(@sequence, time)
91
-
92
- @sequence.pop(@sequence.size - index)
93
- end
94
-
95
- # Return the left-most index where to insert item e, in a list a, assuming
96
- # a is sorted in descending order.
97
- def bisect_left(a, e, l = 0, u = a.length)
98
- while l < u
99
- m = l + (u - l).div(2)
100
-
101
- if a[m] > e
102
- l = m + 1
103
- else
104
- u = m
105
- end
106
- end
107
-
108
- l
109
- end
110
- 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
111
143
  end
@@ -1,127 +1,147 @@
1
1
  # frozen_string_literal: true
2
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
+
3
23
  require "set"
4
24
  require "forwardable"
5
- require "hitimes"
6
25
 
7
- require "timers/timer"
8
- require "timers/events"
26
+ require_relative "interval"
27
+ require_relative "timer"
28
+ require_relative "events"
9
29
 
10
30
  module Timers
11
- # A collection of timers which may fire at different times
12
- class Group
13
- include Enumerable
14
-
15
- extend Forwardable
16
- def_delegators :@timers, :each, :empty?
17
-
18
- def initialize
19
- @events = Events.new
20
-
21
- @timers = Set.new
22
- @paused_timers = Set.new
23
-
24
- @interval = Hitimes::Interval.new
25
- @interval.start
26
- end
27
-
28
- # Scheduled events:
29
- attr_reader :events
30
-
31
- # Active timers:
32
- attr_reader :timers
33
-
34
- # Paused timers:
35
- attr_reader :paused_timers
36
-
37
- # Call the given block after the given interval. The first argument will be
38
- # the time at which the group was asked to fire timers for.
39
- def after(interval, &block)
40
- Timer.new(self, interval, false, &block)
41
- end
42
-
43
- # Call the given block immediately, and then after the given interval. The first
44
- # argument will be the time at which the group was asked to fire timers for.
45
- def now_and_after(interval, &block)
46
- yield
47
- after(interval, &block)
48
- end
49
-
50
- # Call the given block periodically at the given interval. The first
51
- # argument will be the time at which the group was asked to fire timers for.
52
- def every(interval, recur = true, &block)
53
- Timer.new(self, interval, recur, &block)
54
- end
55
-
56
- # Call the given block immediately, and then periodically at the given interval. The first
57
- # argument will be the time at which the group was asked to fire timers for.
58
- def now_and_every(interval, recur = true, &block)
59
- yield
60
- every(interval, recur, &block)
61
- end
62
-
63
- # Wait for the next timer and fire it. Can take a block, which should behave
64
- # like sleep(n), except that n may be nil (sleep forever) or a negative
65
- # number (fire immediately after return).
66
- def wait
67
- if block_given?
68
- yield wait_interval
69
-
70
- while (interval = wait_interval) && interval > 0
71
- yield interval
72
- end
73
- else
74
- while (interval = wait_interval) && interval > 0
75
- # We cannot assume that sleep will wait for the specified time, it might be +/- a bit.
76
- sleep interval
77
- end
78
- end
79
-
80
- fire
81
- end
82
-
83
- # Interval to wait until when the next timer will fire.
84
- # - nil: no timers
85
- # - -ve: timers expired already
86
- # - 0: timers ready to fire
87
- # - +ve: timers waiting to fire
88
- def wait_interval(offset = current_offset)
89
- handle = @events.first
90
- handle.time - Float(offset) if handle
91
- end
92
-
93
- # Fire all timers that are ready.
94
- def fire(offset = current_offset)
95
- @events.fire(offset)
96
- end
97
-
98
- # Pause all timers.
99
- def pause
100
- @timers.dup.each(&:pause)
101
- end
102
-
103
- # Resume all timers.
104
- def resume
105
- @paused_timers.dup.each(&:resume)
106
- end
107
-
108
- alias continue resume
109
-
110
- # Delay all timers.
111
- def delay(seconds)
112
- @timers.each do |timer|
113
- timer.delay(seconds)
114
- end
115
- end
116
-
117
- # Cancel all timers.
118
- def cancel
119
- @timers.dup.each(&:cancel)
120
- end
121
-
122
- # The group's current time.
123
- def current_offset
124
- @interval.to_f
125
- end
126
- 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
127
147
  end