Class: Polyphony::Timer
Overview
Implements a common timer for running multiple timeouts. This class may be
used to reduce the timer granularity in case a large number of timeouts is
used concurrently. This class basically provides the same methods as global
methods concerned with timeouts, such as #cancel_after
, #every
etc.
Instance Method Summary collapse
-
#after(interval, &block) ⇒ Fiber
Spins up a fiber that will run the given block after sleeping for the given delay.
-
#cancel_after(interval, with_exception: Polyphony::Cancel) ⇒ Object
Runs the given block after setting up a cancellation timer for cancellation.
-
#every(interval) ⇒ Object
Runs the given block in an infinite loop with a regular interval between consecutive iterations.
-
#initialize(tag = nil, resolution:) ⇒ Timer
constructor
Initializes a new timer with the given resolution.
-
#move_on_after(interval, with_value: nil) ⇒ Object
Runs the given block after setting up a cancellation timer for cancellation.
-
#reset ⇒ Object
Resets the timeout for the current fiber.
-
#sleep(duration) ⇒ Object
Sleeps for the given duration.
-
#stop ⇒ Polyphony::Timer
Stops the timer's associated fiber.
Constructor Details
#initialize(tag = nil, resolution:) ⇒ Timer
Initializes a new timer with the given resolution.
13 14 15 16 |
# File 'lib/polyphony/core/timer.rb', line 13 def initialize(tag = nil, resolution:) @fiber = spin_loop(tag, interval: resolution) { update } @timeouts = {} end |
Instance Method Details
#after(interval, &block) ⇒ Fiber
Spins up a fiber that will run the given block after sleeping for the given delay.
45 46 47 48 49 50 |
# File 'lib/polyphony/core/timer.rb', line 45 def after(interval, &block) spin do self.sleep interval block.() end end |
#cancel_after(interval) {|Fiber| ... } ⇒ any #cancel_after(interval, with_exception: exception) {|Fiber| ... } ⇒ any #cancel_after(interval, with_exception: [klass, message]) {|Fiber| ... } ⇒ any
Runs the given block after setting up a cancellation timer for
cancellation. If the cancellation timer elapses, the execution will be
interrupted with an exception defaulting to Polyphony::Cancel
.
This method should be used when a timeout should cause an exception to be propagated down the call stack or up the fiber tree.
Example of normal use:
def read_from_io_with_timeout(io) timer.cancel_after(10) { io.read } rescue Polyphony::Cancel nil end
The timeout period can be reset using Timer#reset
, as shown in the
following example:
timer.cancel_after(10) do loop do msg = socket.gets timer.reset handle_msg(msg) end end
111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/polyphony/core/timer.rb', line 111 def cancel_after(interval, with_exception: Polyphony::Cancel) fiber = Fiber.current @timeouts[fiber] = { interval:, target_stamp: now + interval, exception: with_exception } yield ensure @timeouts.delete(fiber) end |
#every(interval) ⇒ Object
Runs the given block in an infinite loop with a regular interval between consecutive iterations.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/polyphony/core/timer.rb', line 56 def every(interval) fiber = Fiber.current @timeouts[fiber] = { interval:, target_stamp: now + interval, recurring: true } while true Polyphony.backend_wait_event(true) yield end ensure @timeouts.delete(fiber) end |
#move_on_after(interval) {|Fiber| ... } ⇒ any #move_on_after(interval, with_value: value) {|Fiber| ... } ⇒ any
Runs the given block after setting up a cancellation timer for
cancellation. If the cancellation timer elapses, the execution will be
interrupted with a Polyphony::MoveOn
exception, which will be rescued,
and with cause the operation to return the given value.
This method should be used when a timeout is to be handled locally, without generating an exception that is to propagated down the call stack or up the fiber tree.
Example of normal use:
timer.move_on_after(10) { sleep 60 42 } #=> nil
timer.move_on_after(10, with_value: :oops) { sleep 60 42 } #=> :oops
The timeout period can be reset using Timer#reset
, as shown in the
following example:
timer.move_on_after(10) do loop do msg = socket.gets timer.reset handle_msg(msg) end end
164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/polyphony/core/timer.rb', line 164 def move_on_after(interval, with_value: nil) fiber = Fiber.current @timeouts[fiber] = { interval:, target_stamp: now + interval, exception: [Polyphony::MoveOn, with_value] } yield rescue Polyphony::MoveOn => e e.value ensure @timeouts.delete(fiber) end |
#reset ⇒ Object
Resets the timeout for the current fiber.
179 180 181 182 183 184 |
# File 'lib/polyphony/core/timer.rb', line 179 def reset record = @timeouts[Fiber.current] return unless record record[:target_stamp] = now + record[:interval] end |
#sleep(duration) ⇒ Object
Sleeps for the given duration.
29 30 31 32 33 34 35 36 37 38 |
# File 'lib/polyphony/core/timer.rb', line 29 def sleep(duration) fiber = Fiber.current @timeouts[fiber] = { interval: duration, target_stamp: now + duration } Polyphony.backend_wait_event(true) ensure @timeouts.delete(fiber) end |
#stop ⇒ Polyphony::Timer
Stops the timer's associated fiber.
21 22 23 24 |
# File 'lib/polyphony/core/timer.rb', line 21 def stop @fiber.stop self end |