]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blobdiff - lib/rbot/timer.rb
Better handling of akills and other form of disconnections
[user/henk/code/ruby/rbot.git] / lib / rbot / timer.rb
index 1802f9d25502676d7b99dc9f6cd559c57cba5e51..64b0ee431d4aeb261541e43bda38f31dc9953770 100644 (file)
@@ -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