fix potential iteration problem in Timer
[god.git] / lib / god / timer.rb
blob8a3d4ce9fe6712ea88d64d05f39ed0c277ba4484
1 module God
2   
3   class TimerEvent
4     attr_accessor :condition, :at
5     
6     def initialize(condition, interval)
7       self.condition = condition
8       self.at = Time.now.to_i + interval
9     end
10   end
11   
12   class Timer
13     INTERVAL = 0.25
14     
15     attr_reader :events, :timer
16     
17     @@timer = nil
18     
19     def self.get
20       @@timer ||= Timer.new
21     end
22     
23     def self.reset
24       @@timer = nil
25     end
26     
27     # Start the scheduler loop to handle events
28     def initialize
29       @events = []
30       @mutex = Mutex.new
31       
32       @timer = Thread.new do
33         loop do
34           begin
35             # get the current time
36             t = Time.now.to_i
37             
38             # iterate over each event and trigger any that are due
39             @mutex.synchronize do
40               triggered = []
41               
42               @events.each do |event|
43                 if t >= event.at
44                   # trigger the event and mark it for removal
45                   self.trigger(event)
46                   triggered << event
47                 else
48                   # events are ordered, so we can bail on first miss
49                   break
50                 end
51               end
52               
53               # remove all triggered events
54               triggered.each do |event|
55                 @events.delete(event)
56               end
57             end
58           rescue Exception => e
59             message = format("Unhandled exception (%s): %s\n%s",
60                              e.class, e.message, e.backtrace.join("\n"))
61             applog(nil, :fatal, message)
62           ensure
63             # sleep until next check
64             sleep INTERVAL
65           end
66         end
67       end
68     end
69     
70     # Create and register a new TimerEvent with the given parameters
71     def schedule(condition, interval = condition.interval)
72       @mutex.synchronize do
73         @events << TimerEvent.new(condition, interval)
74         @events.sort! { |x, y| x.at <=> y.at }
75       end
76     end
77     
78     # Remove any TimerEvents for the given condition
79     def unschedule(condition)
80       @mutex.synchronize do
81         @events.reject! { |x| x.condition == condition }
82       end
83     end
84     
85     def trigger(event)
86       Hub.trigger(event.condition)
87     end
88     
89     # Join the timer thread
90     def join
91       @timer.join
92     end
93   end
94   
95 end