timers 4.3.0 → 4.3.5

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.
data/spec/spec_helper.rb DELETED
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
- #
3
- # This file is part of the "timers" project and released under the MIT license.
4
- #
5
- # Copyright, 2018, by Samuel Williams. All rights reserved.
6
- #
7
-
8
- # Level of accuracy enforced by tests (50ms)
9
- TIMER_QUANTUM = 0.05
10
-
11
- if ENV['COVERAGE'] || ENV['TRAVIS']
12
- begin
13
- require 'simplecov'
14
-
15
- SimpleCov.start do
16
- add_filter "/spec/"
17
- end
18
-
19
- if ENV['TRAVIS']
20
- require 'coveralls'
21
- Coveralls.wear!
22
- end
23
- rescue LoadError
24
- warn "Could not load simplecov: #{$!}"
25
- end
26
- end
27
-
28
- require 'bundler/setup'
29
- Bundler.require(:test)
30
-
31
- require 'timers'
32
-
33
- RSpec.configure do |config|
34
- # Enable flags like --only-failures and --next-failure
35
- config.example_status_persistence_file_path = ".rspec_status"
36
-
37
- config.expect_with :rspec do |c|
38
- c.syntax = :expect
39
- end
40
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
- #
3
- # This file is part of the "timers" project and released under the MIT license.
4
- #
5
- # Copyright, 2018, by Samuel Williams. All rights reserved.
6
- #
7
-
8
- RSpec.describe Timers::Group do
9
- it "should be able to cancel twice" do
10
- fired = false
11
-
12
- timer = subject.after(0.1) { fired = true }
13
-
14
- 2.times do
15
- timer.cancel
16
- subject.wait
17
- end
18
-
19
- expect(fired).to be false
20
- end
21
-
22
- it "should be possble to reset after cancel" do
23
- fired = false
24
-
25
- timer = subject.after(0.1) { fired = true }
26
- timer.cancel
27
-
28
- subject.wait
29
-
30
- timer.reset
31
-
32
- subject.wait
33
-
34
- expect(fired).to be true
35
- end
36
-
37
- it "should cancel and remove one shot timers after they fire" do
38
- x = 0
39
-
40
- Timers::Wait.for(2) do |_remaining|
41
- timer = subject.every(0.2) { x += 1 }
42
- subject.after(0.1) { timer.cancel }
43
-
44
- subject.wait
45
- end
46
-
47
- expect(subject.timers).to be_empty
48
- expect(x).to be == 0
49
- end
50
- end
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
- #
3
- # This file is part of the "timers" project and released under the MIT license.
4
- #
5
- # Copyright, 2018, by Samuel Williams. All rights reserved.
6
- #
7
-
8
- RSpec.describe Timers::Events do
9
- it "should register an event" do
10
- fired = false
11
-
12
- callback = proc do |_time|
13
- fired = true
14
- end
15
-
16
- subject.schedule(0.1, callback)
17
-
18
- expect(subject.size).to be == 1
19
-
20
- subject.fire(0.15)
21
-
22
- expect(subject.size).to be == 0
23
-
24
- expect(fired).to be true
25
- end
26
-
27
- it "should register events in order" do
28
- fired = []
29
-
30
- times = [0.95, 0.1, 0.3, 0.5, 0.4, 0.2, 0.01, 0.9]
31
-
32
- times.each do |requested_time|
33
- callback = proc do |_time|
34
- fired << requested_time
35
- end
36
-
37
- subject.schedule(requested_time, callback)
38
- end
39
-
40
- subject.fire(0.5)
41
- expect(fired).to be == times.sort.first(6)
42
-
43
- subject.fire(1.0)
44
- expect(fired).to be == times.sort
45
- end
46
-
47
- it "should fire events with the time they were fired at" do
48
- fired_at = :not_fired
49
-
50
- callback = proc do |time|
51
- # The time we actually were fired at:
52
- fired_at = time
53
- end
54
-
55
- subject.schedule(0.5, callback)
56
-
57
- subject.fire(1.0)
58
-
59
- expect(fired_at).to be == 1.0
60
- end
61
- end
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
- #
3
- # This file is part of the "timers" project and released under the MIT license.
4
- #
5
- # Copyright, 2018, by Samuel Williams. All rights reserved.
6
- #
7
-
8
- RSpec.describe Timers::Group do
9
- it "should fire several times" do
10
- result = []
11
-
12
- subject.every(0.7) { result << :a }
13
- subject.every(2.3) { result << :b }
14
- subject.every(1.3) { result << :c }
15
- subject.every(2.4) { result << :d }
16
-
17
- Timers::Wait.for(2.5) do |remaining|
18
- subject.wait if subject.wait_interval < remaining
19
- end
20
-
21
- expect(result).to be == [:a, :c, :a, :a, :b, :d]
22
- end
23
-
24
- it "should fire immediately and then several times later" do
25
- result = []
26
-
27
- subject.every(0.7) { result << :a }
28
- subject.every(2.3) { result << :b }
29
- subject.now_and_every(1.3) { result << :c }
30
- subject.now_and_every(2.4) { result << :d }
31
-
32
- Timers::Wait.for(2.5) do |remaining|
33
- subject.wait if subject.wait_interval < remaining
34
- end
35
-
36
- expect(result).to be == [:c, :d, :a, :c, :a, :a, :b, :d]
37
- end
38
- end
@@ -1,260 +0,0 @@
1
- # frozen_string_literal: true
2
- #
3
- # This file is part of the "timers" project and released under the MIT license.
4
- #
5
- # Copyright, 2018, by Samuel Williams. All rights reserved.
6
- #
7
-
8
- RSpec.describe Timers::Group do
9
- describe "#wait" do
10
- it "calls the wait block with nil" do
11
- called = false
12
-
13
- subject.wait do |interval|
14
- expect(interval).to be_nil
15
- called = true
16
- end
17
-
18
- expect(called).to be true
19
- end
20
-
21
- it "calls the wait block with an interval" do
22
- called = false
23
- fired = false
24
-
25
- subject.after(0.1) { fired = true }
26
-
27
- subject.wait do |interval|
28
- expect(interval).to be_within(TIMER_QUANTUM).of(0.1)
29
- called = true
30
- sleep 0.2
31
- end
32
-
33
- expect(called).to be true
34
- expect(fired).to be true
35
- end
36
- end
37
-
38
- it "sleeps until the next timer" do
39
- interval = TIMER_QUANTUM * 2
40
- started_at = Time.now
41
-
42
- fired = false
43
- subject.after(interval) { fired = true }
44
- subject.wait
45
-
46
- expect(fired).to be true
47
- expect(Time.now - started_at).to be_within(TIMER_QUANTUM).of interval
48
- end
49
-
50
- it "fires instantly when next timer is in the past" do
51
- fired = false
52
- subject.after(TIMER_QUANTUM) { fired = true }
53
- sleep(TIMER_QUANTUM * 2)
54
- subject.wait
55
-
56
- expect(fired).to be true
57
- end
58
-
59
- it "calculates the interval until the next timer should fire" do
60
- interval = 0.1
61
-
62
- subject.after(interval)
63
- expect(subject.wait_interval).to be_within(TIMER_QUANTUM).of interval
64
-
65
- sleep(interval)
66
- expect(subject.wait_interval).to be <= 0
67
- end
68
-
69
- it "fires timers in the correct order" do
70
- result = []
71
-
72
- subject.after(TIMER_QUANTUM * 2) { result << :two }
73
- subject.after(TIMER_QUANTUM * 3) { result << :three }
74
- subject.after(TIMER_QUANTUM * 1) { result << :one }
75
-
76
- sleep TIMER_QUANTUM * 4
77
- subject.fire
78
-
79
- expect(result).to eq [:one, :two, :three]
80
- end
81
-
82
- it "raises TypeError if given an invalid time" do
83
- expect do
84
- subject.after(nil) { nil }
85
- end.to raise_exception(TypeError)
86
- end
87
-
88
- describe "recurring timers" do
89
- it "continues to fire the timers at each interval" do
90
- result = []
91
-
92
- subject.every(TIMER_QUANTUM * 2) { result << :foo }
93
-
94
- sleep TIMER_QUANTUM * 3
95
- subject.fire
96
- expect(result).to eq [:foo]
97
-
98
- sleep TIMER_QUANTUM * 5
99
- subject.fire
100
- expect(result).to eq [:foo, :foo]
101
- end
102
- end
103
-
104
- it "calculates the proper interval to wait until firing" do
105
- interval_ms = 25
106
-
107
- subject.after(interval_ms / 1000.0)
108
-
109
- expect(subject.wait_interval).to be_within(TIMER_QUANTUM).of(interval_ms / 1000.0)
110
- end
111
-
112
- describe "pause and continue timers" do
113
- before(:each) do
114
- @interval = TIMER_QUANTUM * 2
115
-
116
- @fired = false
117
- @timer = subject.after(@interval) { @fired = true }
118
- @fired2 = false
119
- @timer2 = subject.after(@interval) { @fired2 = true }
120
- end
121
-
122
- it "does not fire when paused" do
123
- @timer.pause
124
- subject.wait
125
- expect(@fired).to be false
126
- end
127
-
128
- it "fires when continued after pause" do
129
- @timer.pause
130
- subject.wait
131
- @timer.resume
132
-
133
- sleep @timer.interval
134
- subject.wait
135
-
136
- expect(@fired).to be true
137
- end
138
-
139
- it "can pause all timers at once" do
140
- subject.pause
141
- subject.wait
142
- expect(@fired).to be false
143
- expect(@fired2).to be false
144
- end
145
-
146
- it "can continue all timers at once" do
147
- subject.pause
148
- subject.wait
149
- subject.resume
150
-
151
- # We need to wait until we are sure both timers will fire, otherwise highly accurate clocks
152
- # (e.g. JVM)may only fire the first timer, but not the second, because they are actually
153
- # schedueled at different times.
154
- sleep TIMER_QUANTUM * 2
155
- subject.wait
156
-
157
- expect(@fired).to be true
158
- expect(@fired2).to be true
159
- end
160
-
161
- it "can fire the timer directly" do
162
- fired = false
163
- timer = subject.after(TIMER_QUANTUM * 1) { fired = true }
164
- timer.pause
165
- subject.wait
166
- expect(fired).not_to be true
167
- timer.resume
168
- expect(fired).not_to be true
169
- timer.fire
170
- expect(fired).to be true
171
- end
172
- end
173
-
174
- describe "delay timer" do
175
- it "adds appropriate amount of time to timer" do
176
- timer = subject.after(10)
177
- timer.delay(5)
178
- expect(timer.offset - subject.current_offset).to be_within(TIMER_QUANTUM).of(15)
179
- end
180
- end
181
-
182
- describe "delay timer collection" do
183
- it "delay on set adds appropriate amount of time to all timers" do
184
- timer = subject.after(10)
185
- timer2 = subject.after(20)
186
- subject.delay(5)
187
- expect(timer.offset - subject.current_offset).to be_within(TIMER_QUANTUM).of(15)
188
- expect(timer2.offset - subject.current_offset).to be_within(TIMER_QUANTUM).of(25)
189
- end
190
- end
191
-
192
- describe "on delaying a timer" do
193
- it "fires timers in the correct order" do
194
- result = []
195
-
196
- subject.after(TIMER_QUANTUM * 2) { result << :two }
197
- subject.after(TIMER_QUANTUM * 3) { result << :three }
198
- first = subject.after(TIMER_QUANTUM * 1) { result << :one }
199
- first.delay(TIMER_QUANTUM * 3)
200
-
201
- sleep TIMER_QUANTUM * 5
202
- subject.fire
203
-
204
- expect(result).to eq [:two, :three, :one]
205
- end
206
- end
207
-
208
- describe "#inspect" do
209
- it "before firing" do
210
- fired = false
211
- timer = subject.after(TIMER_QUANTUM * 5) { fired = true }
212
- timer.pause
213
- expect(fired).not_to be true
214
- expect(timer.inspect).to match(/\A#<Timers::Timer:0x[\da-f]+ fires in [-\.\de]+ seconds>\Z/)
215
- end
216
-
217
- it "after firing" do
218
- fired = false
219
- timer = subject.after(TIMER_QUANTUM) { fired = true }
220
-
221
- subject.wait
222
-
223
- expect(fired).to be true
224
- expect(timer.inspect).to match(/\A#<Timers::Timer:0x[\da-f]+ fired [-\.\de]+ seconds ago>\Z/)
225
- end
226
-
227
- it "recurring firing" do
228
- result = []
229
- timer = subject.every(TIMER_QUANTUM) { result << :foo }
230
-
231
- subject.wait
232
- expect(result).not_to be_empty
233
- regex = /\A#<Timers::Timer:0x[\da-f]+ fires in [-\.\de]+ seconds, recurs every #{format("%0.2f", TIMER_QUANTUM)}>\Z/
234
- expect(timer.inspect).to match(regex)
235
- end
236
- end
237
-
238
- describe "#fires_in" do
239
- let(:interval) { TIMER_QUANTUM * 2 }
240
-
241
- it "calculates the interval until the next fire if it's recurring" do
242
- timer = subject.every(interval) { true }
243
- expect(timer.fires_in).to be_within(TIMER_QUANTUM).of(interval)
244
- end
245
-
246
- context "when timer is not recurring" do
247
- let!(:timer) { subject.after(interval) { true } }
248
-
249
- it "calculates the interval until the next fire if it hasn't already fired" do
250
- expect(timer.fires_in).to be_within(TIMER_QUANTUM).of(interval)
251
- end
252
-
253
- it "calculates the interval since last fire if already fired" do
254
- subject.wait
255
- sleep(interval)
256
- expect(timer.fires_in).to be_within(TIMER_QUANTUM).of(0 - interval)
257
- end
258
- end
259
- end
260
- end
@@ -1,124 +0,0 @@
1
- # frozen_string_literal: true
2
- #
3
- # This file is part of the "timers" project and released under the MIT license.
4
- #
5
- # Copyright, 2018, by Samuel Williams. All rights reserved.
6
- #
7
-
8
- require "spec_helper"
9
-
10
- # Event based timers:
11
-
12
- # Serviced 31812 events in 2.39075272 seconds, 13306.320832794887 e/s.
13
- # Thread ID: 7336700
14
- # Fiber ID: 30106340
15
- # Total: 2.384043
16
- # Sort by: self_time
17
-
18
- # %self total self wait child calls name
19
- # 13.48 0.510 0.321 0.000 0.189 369133 Timers::Events::Handle#<=>
20
- # 8.12 0.194 0.194 0.000 0.000 427278 Timers::Events::Handle#to_f
21
- # 4.55 0.109 0.109 0.000 0.000 427278 Float#<=>
22
- # 4.40 1.857 0.105 0.000 1.752 466376 *Timers::Events#bsearch
23
- # 4.30 0.103 0.103 0.000 0.000 402945 Float#to_f
24
- # 2.65 0.063 0.063 0.000 0.000 33812 Array#insert
25
- # 2.64 1.850 0.063 0.000 1.787 33812 Timers::Events#schedule
26
- # 2.40 1.930 0.057 0.000 1.873 33812 Timers::Timer#reset
27
- # 1.89 1.894 0.045 0.000 1.849 31812 Timers::Timer#fire
28
- # 1.69 1.966 0.040 0.000 1.926 31812 Timers::Events::Handle#fire
29
- # 1.35 0.040 0.032 0.000 0.008 33812 Timers::Events::Handle#initialize
30
- # 1.29 0.044 0.031 0.000 0.013 44451 Timers::Group#current_offset
31
-
32
- # SortedSet based timers:
33
-
34
- # Serviced 32516 events in 66.753277275 seconds, 487.1072288781219 e/s.
35
- # Thread ID: 15995640
36
- # Fiber ID: 38731780
37
- # Total: 66.716394
38
- # Sort by: self_time
39
-
40
- # %self total self wait child calls name
41
- # 54.73 49.718 36.513 0.000 13.205 57084873 Timers::Timer#<=>
42
- # 23.74 65.559 15.841 0.000 49.718 32534 Array#sort!
43
- # 19.79 13.205 13.205 0.000 0.000 57084873 Float#<=>
44
-
45
- # Max out events performance (on my computer):
46
- # Serviced 1142649 events in 11.194903921 seconds, 102068.70405115146 e/s.
47
-
48
- RSpec.describe Timers::Group do
49
- context "with profiler" do
50
- if defined? RubyProf
51
- before(:each) do
52
- # Running RubyProf makes the code slightly slower.
53
- RubyProf.start
54
- puts "*** Running with RubyProf reduces performance ***"
55
- end
56
-
57
- after(:each) do
58
- if RubyProf.running?
59
- # file = arg.metadata[:description].gsub(/\s+/, '-')
60
-
61
- result = RubyProf.stop
62
-
63
- printer = RubyProf::FlatPrinter.new(result)
64
- printer.print($stderr, min_percent: 1.0)
65
- end
66
- end
67
- end
68
-
69
- it "runs efficiently" do
70
- result = []
71
- range = (1..500)
72
- duration = 2.0
73
-
74
- total = 0
75
- range.each do |index|
76
- offset = index.to_f / range.max
77
- total += (duration / offset).floor
78
-
79
- subject.every(index.to_f / range.max, :strict) { result << index }
80
- end
81
-
82
- subject.wait while result.size < total
83
-
84
- rate = result.size.to_f / subject.current_offset
85
- puts "Serviced #{result.size} events in #{subject.current_offset} seconds, #{rate} e/s."
86
-
87
- expect(subject.current_offset).to be_within(20).percent_of(duration)
88
- end
89
- end
90
-
91
- it "runs efficiently at high volume" do
92
- results = []
93
- range = (1..300)
94
- groups = (1..20)
95
- duration = 11
96
-
97
- timers = []
98
- @mutex = Mutex.new
99
- start = Time.now
100
- groups.each do |gi|
101
- timers << Thread.new {
102
- result = []
103
- timer = Timers::Group.new
104
- total = 0
105
- range.each do |ri|
106
- offset = ri.to_f / range.max
107
- total += (duration / offset).floor
108
- timer.every(ri.to_f / range.max, :strict) { result << ri }
109
- end
110
- timer.wait while result.size < total
111
- @mutex.synchronize { results += result }
112
-
113
- }
114
- end
115
- timers.each { |t| t.join }
116
- finish = Time.now
117
-
118
- rate = results.size.to_f / ( runtime = finish - start )
119
-
120
- puts "Serviced #{results.size} events in #{runtime} seconds, #{rate} e/s; across #{groups.max} timers."
121
-
122
- expect(runtime).to be_within(20).percent_of(duration)
123
- end
124
- end
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
- #
3
- # This file is part of the "timers" project and released under the MIT license.
4
- #
5
- # Copyright, 2018, by Samuel Williams. All rights reserved.
6
- #
7
-
8
- RSpec.describe Timers::Group do
9
- it "should not diverge too much" do
10
- fired = :not_fired_yet
11
- count = 0
12
- quantum = 0.01
13
-
14
- start_offset = subject.current_offset
15
- Timers::Timer.new(subject, quantum, :strict, start_offset) do |offset|
16
- fired = offset
17
- count += 1
18
- end
19
-
20
- iterations = 1000
21
- subject.wait while count < iterations
22
-
23
- # In my testing on the JVM, without the :strict recurring, I noticed 60ms of error here.
24
- expect(fired - start_offset).to be_within(quantum).of(iterations * quantum)
25
- end
26
-
27
- it "should only fire 0-interval timer once per iteration" do
28
- count = 0
29
-
30
- start_offset = subject.current_offset
31
- Timers::Timer.new(subject, 0, :strict, start_offset) do |offset, timer|
32
- count += 1
33
- end
34
-
35
- subject.wait
36
-
37
- expect(count).to be == 1
38
- end
39
- end
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
- #
3
- # This file is part of the "timers" project and released under the MIT license.
4
- #
5
- # Copyright, 2018, by Samuel Williams. All rights reserved.
6
- #
7
-
8
- require "spec_helper"
9
- require "timers/wait"
10
-
11
- RSpec.describe Timers::Wait do
12
- it "repeats until timeout expired" do
13
- timeout = Timers::Wait.new(5)
14
- count = 0
15
-
16
- timeout.while_time_remaining do |remaining|
17
- expect(remaining).to be_within(TIMER_QUANTUM).of(timeout.duration - count)
18
-
19
- count += 1
20
- sleep 1
21
- end
22
-
23
- expect(count).to eq(5)
24
- end
25
-
26
- it "yields results as soon as possible" do
27
- timeout = Timers::Wait.new(5)
28
-
29
- result = timeout.while_time_remaining do |_remaining|
30
- break :done
31
- end
32
-
33
- expect(result).to eq(:done)
34
- end
35
- end
data/timers.gemspec DELETED
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
- #
3
- # This file is part of the "timers" project and released under the MIT license.
4
- #
5
- # Copyright, 2018, by Samuel Williams. All rights reserved.
6
- #
7
-
8
- require_relative 'lib/timers/version'
9
-
10
- Gem::Specification.new do |spec|
11
- spec.name = "timers"
12
- spec.version = Timers::VERSION
13
- spec.authors = ["Samuel Williams", "Tony Arcieri"]
14
- spec.email = ["samuel@codeotaku.com", "bascule@gmail.com"]
15
- spec.licenses = ["MIT"]
16
- spec.homepage = "https://github.com/socketry/timers"
17
- spec.summary = "Pure Ruby one-shot and periodic timers"
18
- spec.description = <<-DESCRIPTION.strip.gsub(/\s+/, " ")
19
- Schedule procs to run after a certain time, or at periodic intervals,
20
- using any API that accepts a timeout.
21
- DESCRIPTION
22
-
23
- spec.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
24
- spec.executables = spec.files.grep(%r{^bin/}).map { |f| File.basename(f) }
25
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
26
- spec.require_paths = ["lib"]
27
-
28
- spec.required_ruby_version = '>= 2.2.1'
29
-
30
- spec.add_development_dependency "bundler"
31
- spec.add_development_dependency "rspec", "~> 3.6"
32
- spec.add_development_dependency "rake"
33
- end