lib/timers.rb in timers-1.1.0 vs lib/timers.rb in timers-2.0.0
- old
+ new
@@ -1,24 +1,31 @@
require 'set'
require 'forwardable'
require 'timers/version'
+require 'hitimes'
-# Low precision timers implemented in pure Ruby
+# Workaround for thread safety issues in SortedSet initialization
+# See: https://github.com/celluloid/timers/issues/20
+SortedSet.new
+
class Timers
include Enumerable
extend Forwardable
def_delegators :@timers, :delete, :each, :empty?
def initialize
@timers = SortedSet.new
+ @paused_timers = SortedSet.new
+ @interval = Hitimes::Interval.new
+ @interval.start
end
# Call the given block after the given interval
def after(interval, &block)
Timer.new(self, interval, false, &block)
end
-
+
# Call the given block after the given interval has expired. +interval+
# is measured in milliseconds.
#
# Timer.new.after_milliseconds(25) { puts "fired!" }
#
@@ -38,78 +45,125 @@
sleep i if i
fire
end
# Interval to wait until when the next timer will fire
- def wait_interval(now = Time.now)
+ def wait_interval(offset = self.current_offset)
timer = @timers.first
return unless timer
- interval = timer.time - now
+ interval = timer.offset - Float(offset)
interval > 0 ? interval : 0
end
# Fire all timers that are ready
- def fire(now = Time.now)
- time = now + 0.001 # Fudge 1ms in case of clock imprecision
- while (timer = @timers.first) && (time >= timer.time)
+ def fire(offset = self.current_offset)
+ time = Float(offset) + 0.001 # Fudge 1ms in case of clock imprecision
+ while (timer = @timers.first) && (time >= timer.offset)
@timers.delete timer
- timer.fire(now)
+ timer.fire(offset)
end
end
def add(timer)
raise TypeError, "not a Timers::Timer" unless timer.is_a? Timers::Timer
@timers.add(timer)
end
+ def pause(timer = nil)
+ return pause_all if timer.nil?
+ raise TypeError, "not a Timers::Timer" unless timer.is_a? Timers::Timer
+ @timers.delete timer
+ @paused_timers.add timer
+ end
+
+ def pause_all
+ @timers.each {|timer| timer.pause}
+ end
+
+ def continue(timer = nil)
+ return continue_all if timer.nil?
+ raise TypeError, "not a Timers::Timer" unless timer.is_a? Timers::Timer
+ @paused_timers.delete timer
+ @timers.add timer
+ end
+
+ def continue_all
+ @paused_timers.each {|timer| timer.continue}
+ end
+
+ def delay(seconds)
+ @timers.each {|timer| timer.delay(seconds)}
+ end
+
alias_method :cancel, :delete
+ def current_offset
+ @interval.to_f
+ end
+
# An individual timer set to fire a given proc at a given time
class Timer
include Comparable
- attr_reader :interval, :time, :recurring
+ attr_reader :interval, :offset, :recurring
def initialize(timers, interval, recurring = false, &block)
@timers, @interval, @recurring = timers, interval, recurring
- @block = block
- @time = nil
+ @block = block
+ @offset = nil
reset
end
def <=>(other)
- @time <=> other.time
+ @offset <=> other.offset
end
# Cancel this timer
def cancel
@timers.cancel self
end
+ # Extend this timer
+ def delay(seconds)
+ @timers.delete self
+ @offset += seconds
+ @timers.add self
+ end
+
# Reset this timer
- def reset(now = Time.now)
+ def reset(offset = @timers.current_offset)
@timers.cancel self if @time
- @time = now + @interval
+ @offset = Float(offset) + @interval
@timers.add self
end
# Fire the block
- def fire(now = Time.now)
- reset(now) if recurring
+ def fire(offset = @timers.current_offset)
+ reset(offset) if recurring
@block.call
end
alias_method :call, :fire
+ # Pause this timer
+ def pause
+ @timers.pause self
+ end
+
+ # Continue this timer
+ def continue
+ @timers.continue self
+ end
+
# Inspect a timer
def inspect
str = "#<Timers::Timer:#{object_id.to_s(16)} "
- now = Time.now
+ offset = @timers.current_offset
- if @time
- if @time >= now
- str << "fires in #{@time - now} seconds"
+ if @offset
+ if @offset >= offset
+ str << "fires in #{@offset - offset} seconds"
else
- str << "fired #{now - @time} seconds ago"
+ str << "fired #{offset - @offset} seconds ago"
end
str << ", recurs every #{interval}" if recurring
else
str << "dead"