X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=lib%2Frbot%2Ftimer.rb;h=64b0ee431d4aeb261541e43bda38f31dc9953770;hb=56e4713c5c0498838ed77a409e44fbc3251acde2;hp=1802f9d25502676d7b99dc9f6cd559c57cba5e51;hpb=271fa1315aa0afd0330b61cd4e5071a20fa86c1d;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git diff --git a/lib/rbot/timer.rb b/lib/rbot/timer.rb index 1802f9d2..64b0ee43 100644 --- a/lib/rbot/timer.rb +++ b/lib/rbot/timer.rb @@ -26,19 +26,19 @@ class Timer # Time when the Action should be called next attr_accessor :next - # options are: - # start:: Time when the Action should be run for the first time. - # Repeatable Actions will be repeated after that, see - # :period. One-time Actions will not (obviously) - # Default: Time.now + :period - # period:: How often repeatable Action should be run, in seconds. - # Default: 1 - # blocked:: if true, Action starts as blocked (i.e. will stay dormant - # until unblocked) - # args:: Arguments to pass to the Action callback. Default: [] - # repeat:: Should the Action be called repeatedly? Default: false - # code:: You can specify the Action body using &block, *or* using - # this option. + # Options are: + # start:: Time when the Action should be run for the first time. + # Repeatable Actions will be repeated after that, see + # :period. One-time Actions will not (obviously) + # Default: Time.now + :period + # period:: How often repeatable Action should be run, in seconds. + # Default: 1 + # blocked:: if true, Action starts as blocked (i.e. will stay dormant + # until unblocked) + # args:: Arguments to pass to the Action callback. Default: [] + # repeat:: Should the Action be called repeatedly? Default: false + # code:: You can specify the Action body using &block, *or* using + # this option. def initialize(options = {}, &block) opts = { @@ -62,7 +62,7 @@ class Timer @repeat = opts[:repeat] if opts.include? :repeat if block_given? - @block = block + @block = block elsif opts[:code] @block = opts[:code] end @@ -129,11 +129,12 @@ class Timer self.start end - # creates and installs a new Action, repeatable by default. - # period:: Action period - # opts:: options for Action#new, see there - # block:: Action callback code - # returns the id of the created Action + # Creates and installs a new Action, repeatable by default. + # _period_:: Action period + # _opts_:: options for Action#new, see there + # _block_:: Action callback code + # + # Returns the id of the created Action def add(period, opts = {}, &block) a = Action.new({:repeat => true, :period => period}.merge(opts), &block) self.synchronize do @@ -143,24 +144,25 @@ class Timer return a.object_id end - # creates and installs a new Action, one-time by default. - # period:: Action delay - # opts:: options for Action#new, see there - # block:: Action callback code - # returns the id of the created Action + # Creates and installs a new Action, one-time by default. + # _period_:: Action delay + # _opts_:: options for Action#new, see there + # _block_:: Action callback code + # + # Returns the id of the created Action def add_once(period, opts = {}, &block) self.add(period, {:repeat => false}.merge(opts), &block) end # blocks an existing Action - # aid:: Action id, obtained previously from add() or add_once() + # _aid_:: Action id, obtained previously from add() or add_once() def block(aid) debug "blocking #{aid}" self.synchronize { self[aid].block } end # unblocks an existing blocked Action - # aid:: Action id, obtained previously from add() or add_once() + # _aid_:: Action id, obtained previously from add() or add_once() def unblock(aid) debug "unblocking #{aid}" self.synchronize do @@ -170,7 +172,7 @@ class Timer end # removes an existing blocked Action - # aid:: Action id, obtained previously from add() or add_once() + # _aid_:: Action id, obtained previously from add() or add_once() def remove(aid) self.synchronize do @actions.delete(aid) # or raise "nonexistent action #{aid}" @@ -180,9 +182,9 @@ class Timer alias :delete :remove # Provides for on-the-fly reconfiguration of Actions - # aid:: Action id, obtained previously from add() or add_once() - # opts:: see Action#new - # block:: (optional) new Action callback code + # _aid_:: Action id, obtained previously from add() or add_once() + # _opts_:: see Action#new + # _block_:: (optional) new Action callback code def configure(aid, opts = {}, &block) self.synchronize do self[aid].configure(opts, &block) @@ -191,25 +193,41 @@ class Timer end # changes Action period - # aid:: Action id - # period:: new period - # block:: (optional) new Action callback code + # _aid_:: Action id + # _period_:: new period + # _block_:: (optional) new Action callback code def reschedule(aid, period, &block) self.configure(aid, :period => period, &block) end - protected - def start - raise 'double-started timer' if @thread + raise 'already started' if @thread + @stopping = false + debug "starting timer #{self}" @thread = Thread.new do loop do tmout = self.run_actions + break if tmout and tmout < 0 self.synchronize { @tick.wait(tmout) } end end end + def stop + unless @thread + warning 'trying to stop already stopped timer' + return + end + debug "stopping timer #{self}..." + @stopping = true + self.synchronize { @tick.signal } + @thread.join(60) or @thread.kill + debug "timer #{self} stopped" + @thread = nil + end + + protected + def [](aid) aid ||= @current raise "no current action" unless aid @@ -218,30 +236,23 @@ class Timer end def run_actions(now = Time.now) - nxt = nil @actions.keys.each do |k| - a = @actions[k] - next if (!a) or a.blocked? - - if a.next <= now - begin - @current = k - v = a.run(now) - ensure - @current = nil - end - - unless v - @actions.delete k - next - end - else - v = a.next + return -1 if @stopping + a = @actions[k] or next + next if a.blocked? || a.next > now + + begin + @current = k + a.run(now) + ensure + @current = nil end - nxt = v if v and ((!nxt) or (v < nxt)) + @actions.delete k unless a.next end + nxt = @actions.values.find_all { |v| !v.blocked? }.map{ |v| v.next }.min + if nxt delta = nxt - now delta = 0 if delta < 0