Class: DiscreteEvent::EventQueue
- Inherits:
-
Object
- Object
- DiscreteEvent::EventQueue
- Defined in:
- lib/discrete_event/event_queue.rb
Overview
Queue of pending events; also keeps track of the clock (the current time).
There are two key terms:
-
action: any Ruby block
-
event: an action to be executed at some specified time in the future
Events are usually created using the #at and #after methods. The methods #at_each, #every and #recur_after make some important special cases more efficient.
See the README for an example.
Direct Known Subclasses
Defined Under Namespace
Classes: Event
Instance Attribute Summary collapse
-
#events ⇒ PQueue
readonly
Event queue.
-
#now ⇒ Number
readonly
Current time (taken from the currently executing event, if any).
Instance Method Summary collapse
-
#after(delay) { ... } ⇒ nil
Schedule
action
(a block) to run after the givendelay
(with respect to #now). -
#at(time) { ... } ⇒ nil
Schedule
action
(a block) to run at the giventime
;time
must not be in the past. -
#at_each(elements, time = nil) {|element| ... } ⇒ nil
Schedule
action
(a block) to run for each element in the given list (possibly at different times). -
#each {|now| ... } ⇒ self
Allow for the creation of a ruby
Enumerator
for the simulation. -
#every(interval, start = 0, &action) ⇒ nil
Schedule
action
(a block) to run periodically. -
#initialize(now = 0.0) ⇒ EventQueue
constructor
A new instance of EventQueue.
-
#next_event_time ⇒ Number?
The time of the next queued event, or
nil
if there are no queued events. -
#recur_after(interval) ⇒ nil
When called from within an action block, repeats the action block after the specified
interval
has elapsed. -
#reset(now = 0.0) ⇒ self
Clear any pending events in the event queue and reset #now.
-
#run_next ⇒ Boolean
Run the action for the next event in the queue.
Constructor Details
#initialize(now = 0.0) ⇒ EventQueue
Returns a new instance of EventQueue.
35 36 37 38 39 |
# File 'lib/discrete_event/event_queue.rb', line 35 def initialize now=0.0 @now = now @events = PQueue.new {|a,b| a.time < b.time} @recur_interval = nil end |
Instance Attribute Details
#events ⇒ PQueue (readonly)
Event queue.
33 34 35 |
# File 'lib/discrete_event/event_queue.rb', line 33 def events @events end |
#now ⇒ Number (readonly)
Current time (taken from the currently executing event, if any). You can use floating point or integer time.
26 27 28 |
# File 'lib/discrete_event/event_queue.rb', line 26 def now @now end |
Instance Method Details
#after(delay) { ... } ⇒ nil
Schedule action
(a block) to run after the given delay
(with respect to #now).
67 68 69 |
# File 'lib/discrete_event/event_queue.rb', line 67 def after delay, &action at(@now + delay, &action) end |
#at(time) { ... } ⇒ nil
Schedule action
(a block) to run at the given time
; time
must not be in the past.
51 52 53 54 55 |
# File 'lib/discrete_event/event_queue.rb', line 51 def at time, &action raise "cannot schedule event in the past" if time < now @events.push(Event.new(time, action)) nil end |
#at_each(elements, time = nil) {|element| ... } ⇒ nil
Schedule action
(a block) to run for each element in the given list (possibly at different times).
This method may be of interest if you have a large number of events that occur at known times. You could use #at to add each one to the event queue at the start of the simulation, but this will make adding other events more expensive. Instead, this method adds them one at a time, so only the next event is stored in the event queue.
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/discrete_event/event_queue.rb', line 106 def at_each elements, time=nil, &action raise ArgumentError, 'no action given' unless block_given? unless elements.empty? element = elements.shift if time.nil? element_time = element.time elsif time.is_a? Proc element_time = time.call(element) elsif time.is_a? Symbol element_time = element.send(time) else raise ArgumentError, "bad time" end at element_time do yield element at_each elements, time, &action end end nil end |
#each {|now| ... } ⇒ self
Allow for the creation of a ruby Enumerator
for the simulation. This yields for each event.
268 269 270 271 |
# File 'lib/discrete_event/event_queue.rb', line 268 def each yield now while run_next self end |
#every(interval, start = 0, &action) ⇒ nil
Schedule action
(a block) to run periodically.
This is useful for statistics collection.
Note that if you specify one or more events of this kind, the simulation will never run out of events.
187 188 189 190 191 192 193 |
# File 'lib/discrete_event/event_queue.rb', line 187 def every interval, start=0, &action at start do yield recur_after interval end nil end |
#next_event_time ⇒ Number?
The time of the next queued event, or nil
if there are no queued events.
If this method is called from within an action block, it returns #now (that is, the current event hasn’t finished yet, so it’s still in some sense the next event).
204 205 206 207 208 209 210 211 |
# File 'lib/discrete_event/event_queue.rb', line 204 def next_event_time event = @events.top if event event.time else nil end end |
#recur_after(interval) ⇒ nil
When called from within an action block, repeats the action block after the specified interval
has elapsed.
Calling this method from outside an action block has no effect. You may call this method at most once in an action block.
Note that you can achieve the same effect using #at and #after and a named method, as in
def demo
at 5 do
puts "now: #{now}"
after 10*rand do
demo
end
end
end
but it is somewhat more efficient to call recur_after
, and, if you do, the named method is not necessary.
159 160 161 162 163 |
# File 'lib/discrete_event/event_queue.rb', line 159 def recur_after interval raise "cannot recur twice" if @recur_interval @recur_interval = interval nil end |
#reset(now = 0.0) ⇒ self
Clear any pending events in the event queue and reset #now.
278 279 280 281 282 |
# File 'lib/discrete_event/event_queue.rb', line 278 def reset now=0.0 @now = now @events.clear self end |
#run_next ⇒ Boolean
Run the action for the next event in the queue.
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/discrete_event/event_queue.rb', line 218 def run_next event = @events.top if event # run the action @now = event.time event.action.call # recurring events get special treatment: can avoid doing a push and a # pop by reusing the Event at the top of the heap, but with a new time # # NB: this assumes that the top element in the heap can't change due to # the event that we just ran, which is the case here, because we don't # allow events to be created in the past, and because of the internals # of the PQueue datastructure if @recur_interval event.time = @now + @recur_interval @events.replace_top(event) @recur_interval = nil else @events.pop end true else false end end |