timers 4.1.1 → 4.3.1

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: 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