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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: aa56588795590f6b0772c42830b2969d4c18c31b
4
- data.tar.gz: 203a266076597db814652c1b1e8d25fdca6808b2
3
+ metadata.gz: 8fd3554aab4028decec14d741c973f4ffe5d414a
4
+ data.tar.gz: 082bf7c3ec19a1d60df84d1b81a033a888e6b922
5
5
  SHA512:
6
- metadata.gz: cf717b2718394941dc03bd9bd4184a790116fff7e936b07227fe64cec0d7801ff84903ba23efd5332751919d3a9acbbff95c3a1b53f8c150cd67a94c78531763
7
- data.tar.gz: 26d41eddf5f924ac26860a09daba9af79203f32f0f7cefc93cafc5a46ffd95ad959f0281c6dbe619ca3ad8087c63e16c87ef1e860d297f6c30e98da3562c5be0
6
+ metadata.gz: 354c245b317b5cf5127b6bc0ce76d24c0c9c73f7a170afd4e2d7d286a007018a94ee9c628ce86793b799c71946e07a718d83154b3a8e1a25254adaea91e72725
7
+ data.tar.gz: 56d3a3b9d8e54171892347c7ab67dc936216c12bda07f910ddc3b766a99f7ccd8398179e980285ab7307d734a516d91623b93e68db1ae59cc3df686585f78f5f
data/.travis.yml CHANGED
@@ -8,6 +8,7 @@ rvm:
8
8
  - rbx-2
9
9
 
10
10
  matrix:
11
+ fast_finish: true
11
12
  allow_failures:
12
13
  - rvm: ruby-head
13
14
  - rvm: jruby-head
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
- return @sequence.pop(@sequence.size - index)
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)/2
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
- @handle.cancel! if @handle
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
@@ -1,3 +1,3 @@
1
1
  module Timers
2
- VERSION = "4.0.0"
2
+ VERSION = "4.1.0"
3
3
  end
@@ -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
@@ -9,7 +9,7 @@ RSpec.describe Timers::Events do
9
9
  fired = true
10
10
  end
11
11
 
12
- handle = subject.schedule(0.1, callback)
12
+ subject.schedule(0.1, callback)
13
13
 
14
14
  expect(subject.size).to be == 1
15
15
 
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
@@ -60,7 +60,7 @@ RSpec.describe Timers::Group do
60
60
  end
61
61
  end
62
62
 
63
- it "run efficiently" do
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.0.0
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: 2014-07-27 00:00:00.000000000 Z
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.2.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