timers 4.0.0 → 4.1.0
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 +4 -4
- data/.travis.yml +1 -0
- data/CHANGES.md +10 -0
- data/README.md +7 -0
- data/lib/timers/events.rb +3 -2
- data/lib/timers/group.rb +15 -1
- data/lib/timers/timer.rb +22 -11
- data/lib/timers/version.rb +1 -1
- data/spec/cancel_spec.rb +46 -0
- data/spec/events_spec.rb +1 -1
- data/spec/every_spec.rb +15 -0
- data/spec/performance_spec.rb +38 -1
- metadata +11 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8fd3554aab4028decec14d741c973f4ffe5d414a
|
|
4
|
+
data.tar.gz: 082bf7c3ec19a1d60df84d1b81a033a888e6b922
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 354c245b317b5cf5127b6bc0ce76d24c0c9c73f7a170afd4e2d7d286a007018a94ee9c628ce86793b799c71946e07a718d83154b3a8e1a25254adaea91e72725
|
|
7
|
+
data.tar.gz: 56d3a3b9d8e54171892347c7ab67dc936216c12bda07f910ddc3b766a99f7ccd8398179e980285ab7307d734a516d91623b93e68db1ae59cc3df686585f78f5f
|
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
4.1.0 (2015-08-16)
|
|
2
|
+
------------------
|
|
3
|
+
* Addition of `now_and_every` method; fires block immediately, then sets recurring timer.
|
|
4
|
+
* Includes `now_and_after` method; does the same as above for one-shot timers: essentially a "two-shot" timer.
|
|
5
|
+
|
|
6
|
+
4.0.1 (2014-09-10)
|
|
7
|
+
------------------
|
|
8
|
+
* Memory leak fixes
|
|
9
|
+
* mathn fix for those crazy enough to use it
|
|
10
|
+
|
|
1
11
|
4.0.0 (2014-07-27)
|
|
2
12
|
------------------
|
|
3
13
|
* Replace Timers::Timeout with Timers::Wait
|
data/README.md
CHANGED
|
@@ -50,6 +50,13 @@ every_five_seconds = timers.every(5) { puts "Another 5 seconds" }
|
|
|
50
50
|
loop { timers.wait }
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
+
You can also schedule a block to run immediately and periodically with `Timers::Group#now_and_every`:
|
|
54
|
+
```ruby
|
|
55
|
+
now_and_every_five_seconds = timers.now_and_every(5) { puts "Now and in another 5 seconds" }
|
|
56
|
+
|
|
57
|
+
loop { timers.wait }
|
|
58
|
+
```
|
|
59
|
+
|
|
53
60
|
If you'd like another method to do the waiting for you, e.g. `Kernel.select`,
|
|
54
61
|
you can use `Timers::Group#wait_interval` to obtain the amount of time to wait. When
|
|
55
62
|
a timeout is encountered, you can fire all pending timers with `Timers::Group#fire`:
|
data/lib/timers/events.rb
CHANGED
|
@@ -72,6 +72,7 @@ module Timers
|
|
|
72
72
|
return handle
|
|
73
73
|
end
|
|
74
74
|
end
|
|
75
|
+
# @sequence.reverse.find { |handle| !handle.cancelled? }
|
|
75
76
|
end
|
|
76
77
|
|
|
77
78
|
# Returns the number of pending (possibly cancelled) events.
|
|
@@ -93,14 +94,14 @@ module Timers
|
|
|
93
94
|
def pop(time)
|
|
94
95
|
index = bisect_left(@sequence, time)
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
@sequence.pop(@sequence.size - index)
|
|
97
98
|
end
|
|
98
99
|
|
|
99
100
|
# Return the left-most index where to insert item e, in a list a, assuming
|
|
100
101
|
# a is sorted in descending order.
|
|
101
102
|
def bisect_left(a, e, l = 0, u = a.length)
|
|
102
103
|
while l < u
|
|
103
|
-
m = l + (u-l)
|
|
104
|
+
m = l + (u-l).div(2)
|
|
104
105
|
|
|
105
106
|
if a[m] > e
|
|
106
107
|
l = m+1
|
data/lib/timers/group.rb
CHANGED
|
@@ -38,12 +38,26 @@ module Timers
|
|
|
38
38
|
Timer.new(self, interval, false, &block)
|
|
39
39
|
end
|
|
40
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
|
+
|
|
41
48
|
# Call the given block periodically at the given interval. The first
|
|
42
49
|
# argument will be the time at which the group was asked to fire timers for.
|
|
43
50
|
def every(interval, recur = true, &block)
|
|
44
51
|
Timer.new(self, interval, recur, &block)
|
|
45
52
|
end
|
|
46
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
|
+
|
|
47
61
|
# Wait for the next timer and fire it. Can take a block, which should behave
|
|
48
62
|
# like sleep(n), except that n may be nil (sleep forever) or a negative
|
|
49
63
|
# number (fire immediately after return).
|
|
@@ -61,7 +75,7 @@ module Timers
|
|
|
61
75
|
end
|
|
62
76
|
end
|
|
63
77
|
|
|
64
|
-
fire
|
|
78
|
+
return fire
|
|
65
79
|
end
|
|
66
80
|
|
|
67
81
|
# Interval to wait until when the next timer will fire.
|
data/lib/timers/timer.rb
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
|
|
2
2
|
module Timers
|
|
3
|
-
# An individual timer set to fire a given proc at a given time
|
|
3
|
+
# An individual timer set to fire a given proc at a given time. A timer is
|
|
4
|
+
# always connected to a Timer::Group but it would ONLY be in @group.timers
|
|
5
|
+
# if it also has a @handle specified. Otherwise it is either PAUSED or has
|
|
6
|
+
# been FIRED and is not recurring. You can manually enter this state by
|
|
7
|
+
# calling #cancel and resume normal operation by calling #reset.
|
|
4
8
|
class Timer
|
|
5
9
|
include Comparable
|
|
6
10
|
attr_reader :interval, :offset, :recurring
|
|
@@ -8,8 +12,6 @@ module Timers
|
|
|
8
12
|
def initialize(group, interval, recurring = false, offset = nil, &block)
|
|
9
13
|
@group = group
|
|
10
14
|
|
|
11
|
-
@group.timers << self
|
|
12
|
-
|
|
13
15
|
@interval = interval
|
|
14
16
|
@recurring = recurring
|
|
15
17
|
@block = block
|
|
@@ -38,9 +40,9 @@ module Timers
|
|
|
38
40
|
def resume
|
|
39
41
|
return unless paused?
|
|
40
42
|
|
|
41
|
-
@group.timers.add self
|
|
42
43
|
@group.paused_timers.delete self
|
|
43
44
|
|
|
45
|
+
# This will add us back to the group:
|
|
44
46
|
reset
|
|
45
47
|
end
|
|
46
48
|
|
|
@@ -54,27 +56,34 @@ module Timers
|
|
|
54
56
|
|
|
55
57
|
@handle = @group.events.schedule(@offset, self)
|
|
56
58
|
end
|
|
57
|
-
|
|
58
|
-
# Cancel this timer
|
|
59
|
+
|
|
60
|
+
# Cancel this timer. Do not call while paused.
|
|
59
61
|
def cancel
|
|
62
|
+
return unless @handle
|
|
63
|
+
|
|
60
64
|
@handle.cancel! if @handle
|
|
61
65
|
@handle = nil
|
|
62
66
|
|
|
63
67
|
# This timer is no longer valid:
|
|
64
|
-
@group.timers.delete self
|
|
65
|
-
@group = nil
|
|
68
|
+
@group.timers.delete self if @group
|
|
66
69
|
end
|
|
67
70
|
|
|
68
|
-
# Reset this timer
|
|
71
|
+
# Reset this timer. Do not call while paused.
|
|
69
72
|
def reset(offset = @group.current_offset)
|
|
70
|
-
|
|
73
|
+
# This logic allows us to minimise the interaction with @group.timers.
|
|
74
|
+
# A timer with a handle is always registered with the group.
|
|
75
|
+
if @handle
|
|
76
|
+
@handle.cancel!
|
|
77
|
+
else
|
|
78
|
+
@group.timers << self
|
|
79
|
+
end
|
|
71
80
|
|
|
72
81
|
@offset = Float(offset) + @interval
|
|
73
82
|
|
|
74
83
|
@handle = @group.events.schedule(@offset, self)
|
|
75
84
|
end
|
|
76
85
|
|
|
77
|
-
# Fire the block
|
|
86
|
+
# Fire the block.
|
|
78
87
|
def fire(offset = @group.current_offset)
|
|
79
88
|
if recurring == :strict
|
|
80
89
|
# ... make the next interval strictly the last offset + the interval:
|
|
@@ -86,6 +95,8 @@ module Timers
|
|
|
86
95
|
end
|
|
87
96
|
|
|
88
97
|
@block.call(offset)
|
|
98
|
+
|
|
99
|
+
cancel unless recurring
|
|
89
100
|
end
|
|
90
101
|
|
|
91
102
|
alias_method :call, :fire
|
data/lib/timers/version.rb
CHANGED
data/spec/cancel_spec.rb
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
RSpec.describe Timers::Group do
|
|
5
|
+
it "should be able to cancel twice" do
|
|
6
|
+
fired = false
|
|
7
|
+
|
|
8
|
+
timer = subject.after(0.1) { fired = true }
|
|
9
|
+
|
|
10
|
+
2.times do
|
|
11
|
+
timer.cancel
|
|
12
|
+
subject.wait
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
expect(fired).to be false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "should be possble to reset after cancel" do
|
|
19
|
+
fired = false
|
|
20
|
+
|
|
21
|
+
timer = subject.after(0.1) { fired = true }
|
|
22
|
+
timer.cancel
|
|
23
|
+
|
|
24
|
+
subject.wait
|
|
25
|
+
|
|
26
|
+
timer.reset
|
|
27
|
+
|
|
28
|
+
subject.wait
|
|
29
|
+
|
|
30
|
+
expect(fired).to be true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "should cancel and remove one shot timers after they fire" do
|
|
34
|
+
x = 0
|
|
35
|
+
|
|
36
|
+
Timers::Wait.for(2) do |remaining|
|
|
37
|
+
timer = subject.every(0.2) { x += 1 }
|
|
38
|
+
subject.after(0.1) { timer.cancel }
|
|
39
|
+
|
|
40
|
+
subject.wait
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
expect(subject.timers).to be_empty
|
|
44
|
+
expect(x).to be == 0
|
|
45
|
+
end
|
|
46
|
+
end
|
data/spec/events_spec.rb
CHANGED
data/spec/every_spec.rb
CHANGED
|
@@ -16,4 +16,19 @@ RSpec.describe Timers::Group do
|
|
|
16
16
|
|
|
17
17
|
expect(result).to be == [:a, :c, :a, :a, :b, :d]
|
|
18
18
|
end
|
|
19
|
+
|
|
20
|
+
it "should fire immediately and then several times later" do
|
|
21
|
+
result = []
|
|
22
|
+
|
|
23
|
+
subject.every(0.7) { result << :a }
|
|
24
|
+
subject.every(2.3) { result << :b }
|
|
25
|
+
subject.now_and_every(1.3) { result << :c }
|
|
26
|
+
subject.now_and_every(2.4) { result << :d }
|
|
27
|
+
|
|
28
|
+
Timers::Wait.for(2.5) do |remaining|
|
|
29
|
+
subject.wait if subject.wait_interval < remaining
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
expect(result).to be == [:c, :d, :a, :c, :a, :a, :b, :d]
|
|
33
|
+
end
|
|
19
34
|
end
|
data/spec/performance_spec.rb
CHANGED
|
@@ -60,7 +60,7 @@ RSpec.describe Timers::Group do
|
|
|
60
60
|
end
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
it "
|
|
63
|
+
it "runs efficiently" do
|
|
64
64
|
result = []
|
|
65
65
|
range = (1..500)
|
|
66
66
|
duration = 2.0
|
|
@@ -80,4 +80,41 @@ RSpec.describe Timers::Group do
|
|
|
80
80
|
|
|
81
81
|
expect(subject.current_offset).to be_within(TIMER_QUANTUM).of(duration)
|
|
82
82
|
end
|
|
83
|
+
|
|
84
|
+
=begin
|
|
85
|
+
it "runs efficiently at high volume" do
|
|
86
|
+
results = []
|
|
87
|
+
range = (1..300)
|
|
88
|
+
groups = (1..20)
|
|
89
|
+
duration = 101
|
|
90
|
+
|
|
91
|
+
timers = []
|
|
92
|
+
@mutex = Mutex.new
|
|
93
|
+
start = Time.now
|
|
94
|
+
groups.each do |gi|
|
|
95
|
+
timers << Thread.new {
|
|
96
|
+
result = []
|
|
97
|
+
timer = Timers::Group.new
|
|
98
|
+
total = 0
|
|
99
|
+
range.each do |ri|
|
|
100
|
+
offset = ri.to_f / range.max
|
|
101
|
+
total += (duration / offset).floor
|
|
102
|
+
timer.every(ri.to_f / range.max, :strict) { result << ri }
|
|
103
|
+
end
|
|
104
|
+
timer.wait while result.size < total
|
|
105
|
+
@mutex.synchronize { results += result }
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
end
|
|
109
|
+
timers.each { |t| t.join }
|
|
110
|
+
finish = Time.now
|
|
111
|
+
|
|
112
|
+
rate = results.size.to_f / ( runtime = finish - start )
|
|
113
|
+
|
|
114
|
+
puts "Serviced #{results.size} events in #{runtime} seconds, #{rate} e/s; across #{groups.max} timers."
|
|
115
|
+
|
|
116
|
+
expect(runtime).to be_within(TIMER_QUANTUM).of(duration)
|
|
117
|
+
end
|
|
118
|
+
=end
|
|
119
|
+
|
|
83
120
|
end
|
metadata
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: timers
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tony Arcieri
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2015-08-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: hitimes
|
|
15
|
+
type: :runtime
|
|
16
|
+
prerelease: false
|
|
15
17
|
requirement: !ruby/object:Gem::Requirement
|
|
16
18
|
requirements:
|
|
17
19
|
- - ">="
|
|
18
20
|
- !ruby/object:Gem::Version
|
|
19
21
|
version: '0'
|
|
20
|
-
type: :runtime
|
|
21
|
-
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
@@ -26,13 +26,13 @@ dependencies:
|
|
|
26
26
|
version: '0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: rake
|
|
29
|
+
type: :development
|
|
30
|
+
prerelease: false
|
|
29
31
|
requirement: !ruby/object:Gem::Requirement
|
|
30
32
|
requirements:
|
|
31
33
|
- - ">="
|
|
32
34
|
- !ruby/object:Gem::Version
|
|
33
35
|
version: '0'
|
|
34
|
-
type: :development
|
|
35
|
-
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - ">="
|
|
@@ -40,13 +40,13 @@ dependencies:
|
|
|
40
40
|
version: '0'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: rspec
|
|
43
|
+
type: :development
|
|
44
|
+
prerelease: false
|
|
43
45
|
requirement: !ruby/object:Gem::Requirement
|
|
44
46
|
requirements:
|
|
45
47
|
- - "~>"
|
|
46
48
|
- !ruby/object:Gem::Version
|
|
47
49
|
version: 3.0.0
|
|
48
|
-
type: :development
|
|
49
|
-
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
52
|
- - "~>"
|
|
@@ -75,6 +75,7 @@ files:
|
|
|
75
75
|
- lib/timers/timer.rb
|
|
76
76
|
- lib/timers/version.rb
|
|
77
77
|
- lib/timers/wait.rb
|
|
78
|
+
- spec/cancel_spec.rb
|
|
78
79
|
- spec/events_spec.rb
|
|
79
80
|
- spec/every_spec.rb
|
|
80
81
|
- spec/group_spec.rb
|
|
@@ -103,12 +104,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
103
104
|
version: '0'
|
|
104
105
|
requirements: []
|
|
105
106
|
rubyforge_project:
|
|
106
|
-
rubygems_version: 2.
|
|
107
|
+
rubygems_version: 2.4.8
|
|
107
108
|
signing_key:
|
|
108
109
|
specification_version: 4
|
|
109
110
|
summary: Schedule procs to run after a certain time, or at periodic intervals, using
|
|
110
111
|
any API that accepts a timeout
|
|
111
112
|
test_files:
|
|
113
|
+
- spec/cancel_spec.rb
|
|
112
114
|
- spec/events_spec.rb
|
|
113
115
|
- spec/every_spec.rb
|
|
114
116
|
- spec/group_spec.rb
|