2 # 1. Timer::Timer ---> Timer
3 # 2. timer id is now the object_id of the action
4 # 3. Timer resolution removed, we're always arbitrary precision now
5 # 4. I don't see any obvious races [not that i did see any in old impl, though]
6 # 5. We're tickless now, so no need to jerk start/stop
7 # 6. We should be pretty fast now, wrt old impl
8 # 7. reschedule/remove/block now accept nil as an action id (meaning "current")
9 # 8. repeatability is ignored for 0-period repeatable timers
10 # 9. configure() method superceeds reschedule() [the latter stays as compat]
19 def initialize(options = {}, &block)
28 debug("adding timer #{self} :period => #{opts[:period]}, :repeat => #{opts[:repeat].inspect}")
29 self.configure(opts, &block)
30 debug("added #{self}")
33 def configure(opts = {}, &block)
34 @period = opts[:period] if opts.include? :period
35 @blocked = opts[:blocked] if opts.include? :blocked
36 @repeat = opts[:repeat] if opts.include? :repeat
44 raise 'huh?? blockless action?' unless @block
45 if opts.include? :args
46 @args = Array === opts[:args] ? opts[:args] : [opts[:args]]
49 if opts[:start] and (Time === opts[:start])
50 self.next = opts[:start]
52 self.next = Time.now + (opts[:start] || @period)
56 def reschedule(period, &block)
57 self.configure(:period => period, &block)
72 def run(now = Time.now)
73 raise 'inappropriate time to run()' unless self.next && self.next <= now
78 error "Timer action #{self.inspect}: block #{@block.inspect} failed!"
79 error e.pretty_inspect
82 if @repeat && @period > 0
83 self.next = now + @period
91 self.extend(MonitorMixin)
99 def add(period, opts = {}, &block)
100 a = Action.new({:repeat => true, :period => period}.merge(opts), &block)
102 @actions[a.object_id] = a
108 def add_once(period, opts = {}, &block)
109 self.add(period, {:repeat => false}.merge(opts), &block)
113 debug "blocking #{aid}"
114 self.synchronize { self[aid].block }
118 debug "unblocking #{aid}"
127 @actions.delete(aid) # or raise "nonexistent action #{aid}"
131 alias :delete :remove
133 def configure(aid, opts = {}, &block)
135 self[aid].configure(opts, &block)
140 def reschedule(aid, period, &block)
141 self.configure(aid, :period => period, &block)
147 raise 'double-started timer' if @thread
148 @thread = Thread.new do
150 tmout = self.run_actions
151 self.synchronize { @tick.wait(tmout) }
158 raise "no current action" unless aid
159 raise "nonexistent action #{aid}" unless @actions.include? aid
163 def run_actions(now = Time.now)
165 @actions.keys.each do |k|
167 next if (!a) or a.blocked?
185 nxt = v if v and ((!nxt) or (v < nxt))
190 delta = 0 if delta < 0