redo timer mutexing
authortom <tom@taco.desk.hq.powerset.com>
Mon, 7 Jan 2008 23:25:08 +0000 (7 15:25 -0800)
committertom <tom@taco.desk.hq.powerset.com>
Mon, 7 Jan 2008 23:25:08 +0000 (7 15:25 -0800)
History.txt
lib/god/timer.rb
test/test_timer.rb

index 650d34d..13b05ed 100644 (file)
@@ -1,3 +1,7 @@
+== 0.6.6 / 2008-01-07
+  * Bug Fixes
+    * Redo Timer mutexing to reduce synchronization needs
+
 == 0.6.5 / 2008-01-04
   * Bug Fixes
     * Fix Timer descheduling deadlock issue
index 67713e8..658e453 100644 (file)
@@ -20,7 +20,7 @@ module God
   class Timer
     INTERVAL = 0.25
     
-    attr_reader :events, :conditions, :timer
+    attr_reader :events, :pending_events, :conditions, :timer
     
     @@timer = nil
     
@@ -44,38 +44,45 @@ module God
     # Returns Timer
     def initialize
       @events = []
+      @pending_events = []
       @conditions = []
-      @mutex = Monitor.new
+      @pending_mutex = Mutex.new
       
       @timer = Thread.new do
         loop do
           # applog(nil, :debug, "timer main loop, #{@events.size} events pending")
           
           begin
+            # pull in pending events
+            @pending_mutex.synchronize do
+              @pending_events.each { |e| @events << e }
+              @pending_events.clear
+            end
+            
+            @events.sort! { |x, y| x.at <=> y.at }
+            
             # get the current time
             t = Time.now.to_i
             
             # iterate over each event and trigger any that are due
-            @mutex.synchronize do
-              triggered = []
-              
-              @events.each do |event|
-                if t >= event.at
-                  # trigger the event and mark it for removal
-                  self.trigger(event)
-                  triggered << event
-                else
-                  # events are ordered, so we can bail on first miss
-                  break
-                end
-              end
-              
-              # remove all triggered events
-              triggered.each do |event|
-                @conditions.delete(event.condition)
-                @events.delete(event)
+            triggered = []
+            
+            @events.each do |event|
+              if t >= event.at
+                # trigger the event and mark it for removal
+                self.trigger(event)
+                triggered << event
+              else
+                # events are ordered, so we can bail on first miss
+                break
               end
             end
+            
+            # remove all triggered events
+            triggered.each do |event|
+              @conditions.delete(event.condition)
+              @events.delete(event)
+            end
           rescue Exception => e
             message = format("Unhandled exception (%s): %s\n%s",
                              e.class, e.message, e.backtrace.join("\n"))
@@ -95,12 +102,11 @@ module God
     # Returns nothing
     def schedule(condition, delay = condition.interval)
       applog(nil, :debug, "timer schedule #{condition} in #{delay} seconds")
-      @mutex.synchronize do
-        unless @conditions.include?(condition)
-          @events << TimerEvent.new(condition, delay)
-          @conditions << condition
-          @events.sort! { |x, y| x.at <=> y.at }
+      unless @conditions.include?(condition)
+        @pending_mutex.synchronize do
+          @pending_events << TimerEvent.new(condition, delay)
         end
+        @conditions << condition
       end
     end
     
index 4ff88b4..9f9f876 100644 (file)
@@ -13,7 +13,7 @@ class TestTimer < Test::Unit::TestCase
   def test_schedule_should_queue_event
     w = Watch.new
     @t.schedule(stub(:interval => 20, :watch => w))
-    
+    sleep(0.3)
     assert_equal 1, @t.events.size
   end