Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: eventmachine/eventmachine
...
head fork: adzap/eventmachine
Checking mergeability… Don’t worry, you can still create the pull request.
  • 2 commits
  • 3 files changed
  • 0 commit comments
  • 1 contributor
Showing with 164 additions and 1 deletion.
  1. +45 −0 lib/em/timers.rb
  2. +36 −1 lib/eventmachine.rb
  3. +83 −0 tests/test_timers.rb
View
45 lib/em/timers.rb
@@ -54,4 +54,49 @@ def fire # :nodoc:
end
end
end
+
+ # Creates a restartable timer
+ #
+ # puts "started timer at #{Time.now}"
+ # timer = EventMachine::RestartableTimer.new(5) do
+ # # should be about 7 seconds later, due to restart at 2 seconds
+ # puts "completed timer at #{Time.now}"
+ # end
+ # EventMachine::Timer.new(2) { timer.restart }
+ #
+ class RestartableTimer
+ def initialize interval, callback=nil, &block
+ @interval = interval
+ @code = callback || block
+ @done = false
+ @work = method(:fire)
+ schedule
+ end
+
+ # Cancel the timer
+ def cancel
+ @done = true
+ EventMachine.send :cancel_timer, @signature
+ end
+
+ # Restart the timer
+ def restart
+ unless @done
+ EventMachine.send :cancel_timer, @signature
+ schedule
+ end
+ end
+
+ def schedule # :nodoc
+ @signature = EventMachine::add_timer(@interval, @work)
+ end
+
+ def fire # :nodoc
+ unless @done
+ @done = true
+ @code.call
+ end
+ end
+ end
+
end
View
37 lib/eventmachine.rb
@@ -349,6 +349,41 @@ def self.add_periodic_timer *args, &block
EventMachine::PeriodicTimer.new(interval, code)
end
+ # EventMachine#add_restartable_timer adds a restartable timer to the event loop.
+ # It takes the same parameters as the one-shot timer method, EventMachine#add_timer.
+ # This method schedules execution of the given block for a one-shot timer, but can
+ # be restarted before firing to begin the timer from the start of the interval again.
+ #
+ # A restartable timer can be restarted as many times as required, as long as the timer
+ # has not completed. It can also be cancelled like a regular timer.
+ #
+ # === Usage example
+ #
+ # The following sample program creates a restartable and then restarts the timer at
+ # some later time with another shorter timer. The time to complete the restartable
+ # will be the original interval plus the amount of time passed before being restarted.
+ #
+ # EventMachine::run {
+ # puts "Starting the run now: #{Time.now}"
+ # timer = EventMachine::add_restartable_timer( 5 ) {
+ # puts "Executing timer event: #{Time.now}"
+ # }
+ # EventMachine::add_timer( 2 ) {
+ # puts "Restarting timer: #{Time.now}"
+ # timer.restart
+ # }
+ # }
+ #
+ #
+ # Also see EventMachine::RestartableTimer
+ #
+ def self.add_restartable_timer *args, &block
+ interval = args.shift
+ code = args.shift || block
+
+ EventMachine::RestartableTimer.new(interval, code)
+ end
+
# Cancel a timer using its signature. You can also use EventMachine::Timer#cancel
#
def self.cancel_timer timer_or_sig
@@ -1543,4 +1578,4 @@ def self.klass_from_handler(klass = Connection, handler = nil, *args)
# Save everyone some typing.
EM = EventMachine
-EM::P = EventMachine::Protocols
+EM::P = EventMachine::Protocols
View
83 tests/test_timers.rb
@@ -119,6 +119,89 @@ def test_periodic_timer_self_cancel
assert( x == 4 )
end
+ def test_restartable_timer
+ x = false
+ EventMachine.run {
+ EventMachine::RestartableTimer.new(0.1) do
+ x = true
+ EventMachine.stop
+ end
+ }
+ assert x
+ end
+
+ def test_add_restartable_timer
+ x = false
+ EventMachine.run {
+ rt = EventMachine.add_restartable_timer(0.1) { x = true }
+ assert rt.respond_to?(:restart)
+ EventMachine.stop
+ }
+ end
+
+ def test_restart_restartable_timer
+ x = false
+ EventMachine.run {
+ EventMachine.add_timer(0.4) { x = 1 }
+ rt = EventMachine::RestartableTimer.new(0.3) do
+ x = true
+ end
+ EventMachine.add_timer(0.2) { rt.restart }
+ EventMachine.add_timer(0.6) { EventMachine.stop }
+ }
+ assert x == true
+ end
+
+ def test_cannot_restart_already_fired_restartable_timer
+ x = false
+ EventMachine.run {
+ rt = EventMachine::RestartableTimer.new(0.1) do
+ x = true
+ end
+ EventMachine.add_timer(0.2) {
+ x = false
+ rt.restart
+ }
+ EventMachine.add_timer(0.4) { EventMachine.stop }
+ }
+ assert !x
+ end
+
+ def test_restartable_timer_cancel
+ x = false
+ EventMachine.run {
+ rt = EventMachine::RestartableTimer.new(0.3) { x = true }
+ rt.cancel
+ EventMachine::Timer.new(0.1) { EventMachine.stop }
+ }
+ assert !x
+ end
+
+ def test_add_restartable_timer_cancel
+ x = false
+ EventMachine.run {
+ rt = EventMachine.add_restartable_timer(0.2) { x = true }
+ EventMachine.cancel_timer(rt)
+ EventMachine.add_timer(0.3) { EventMachine.stop }
+ }
+ assert !x
+ end
+
+ def test_cannot_restart_cancelled_restartable_timer
+ x = false
+ EventMachine.run {
+ rt = EventMachine::RestartableTimer.new(0.2) do
+ x = true
+ end
+ rt.cancel
+ EventMachine.add_timer(0.1) {
+ x = false
+ rt.restart
+ }
+ EventMachine.add_timer(0.4) { EventMachine.stop }
+ }
+ assert !x
+ end
# This test is only applicable to compiled versions of the reactor.
# Pure ruby and java versions have no built-in limit on the number of outstanding timers.

No commit comments for this range

Something went wrong with that request. Please try again.