Class: Concurrent::ScheduledTask
- Inherits:
-
IVar
- Object
- Concurrent::Synchronization::LockableObject
- IVar
- Concurrent::ScheduledTask
- Includes:
- Comparable
- Defined in:
- lib/concurrent-ruby/concurrent/scheduled_task.rb
Overview
Time calculations on all platforms and languages are sensitive to changes to the system clock. To alleviate the potential problems associated with changing the system clock while an application is running, most modern operating systems provide a monotonic clock that operates independently of the system clock. A monotonic clock cannot be used to determine human-friendly clock times. A monotonic clock is used exclusively for calculating time intervals. Not all Ruby platforms provide access to an operating system monotonic clock. On these platforms a pure-Ruby monotonic clock will be used as a fallback. An operating system monotonic clock is both faster and more reliable than the pure-Ruby implementation. The pure-Ruby implementation should be fast and reliable enough for most non-realtime operations. At this time the common Ruby platforms that provide access to an operating system monotonic clock are MRI 2.1 and above and JRuby (all versions).
ScheduledTask
is a close relative of Concurrent::Future
but with one
important difference: A Future
is set to execute as soon as possible
whereas a ScheduledTask
is set to execute after a specified delay. This
implementation is loosely based on Java's
ScheduledExecutorService.
It is a more feature-rich variant of timer.
The intended schedule time of task execution is set on object construction
with the delay
argument. The delay is a numeric (floating point or integer)
representing a number of seconds in the future. Any other value or a numeric
equal to or less than zero will result in an exception. The actual schedule
time of task execution is set when the execute
method is called.
The constructor can also be given zero or more processing options. Currently the only supported options are those recognized by the Dereferenceable module.
The final constructor argument is a block representing the task to be performed.
If no block is given an ArgumentError
will be raised.
States
ScheduledTask
mixes in the Obligation module thus giving it
"future" behavior. This includes the expected lifecycle states. ScheduledTask
has one additional state, however. While the task (block) is being executed the
state of the object will be :processing
. This additional state is necessary
because it has implications for task cancellation.
Cancellation
A :pending
task can be cancelled using the #cancel
method. A task in any
other state, including :processing
, cannot be cancelled. The #cancel
method returns a boolean indicating the success of the cancellation attempt.
A cancelled ScheduledTask
cannot be restarted. It is immutable.
Obligation and Observation
The result of a ScheduledTask
can be obtained either synchronously or
asynchronously. ScheduledTask
mixes in both the Obligation
module and the
Observable
module from the Ruby standard library. With one exception ScheduledTask
behaves identically to Future with regard to these modules.
Copy Options
Object references in Ruby are mutable. This can lead to serious
problems when the Concern::Obligation#value of an object is a mutable reference. Which
is always the case unless the value is a Fixnum
, Symbol
, or similar
"primitive" data type. Each instance can be configured with a few
options that can help protect the program from potentially dangerous
operations. Each of these options can be optionally set when the object
instance is created:
:dup_on_deref
When true the object will call the#dup
method on thevalue
object every time the#value
method is called (default: false):freeze_on_deref
When true the object will call the#freeze
method on thevalue
object every time the#value
method is called (default: false):copy_on_deref
When given aProc
object theProc
will be run every time the#value
method is called. TheProc
will be given the currentvalue
as its only argument and the result returned by the block will be the return value of the#value
call. Whennil
this option will be ignored (default: nil)
When multiple deref options are set the order of operations is strictly defined. The order of deref operations is:
:copy_on_deref
:dup_on_deref
:freeze_on_deref
Because of this ordering there is no need to #freeze
an object created by a
provided :copy_on_deref
block. Simply set :freeze_on_deref
to true
.
Setting both :dup_on_deref
to true
and :freeze_on_deref
to true
is
as close to the behavior of a "pure" functional language (like Erlang, Clojure,
or Haskell) as we are likely to get in Ruby.
Class Method Summary collapse
-
.execute(delay, opts = {}, &task) ⇒ ScheduledTask
Create a new
ScheduledTask
object with the given block, execute it, and return the:pending
object.
Instance Method Summary collapse
-
#cancel ⇒ Boolean
Cancel this task and prevent it from executing.
-
#cancelled? ⇒ Boolean
Has the task been cancelled?.
-
#execute ⇒ ScheduledTask
Execute an
:unscheduled
ScheduledTask
. -
#initial_delay ⇒ Float
The
delay
value given at instanciation. -
#initialize(delay, opts = {}) { ... } ⇒ ScheduledTask
constructor
Schedule a task for execution at a specified future time.
-
#processing? ⇒ Boolean
In the task execution in progress?.
-
#reschedule(delay) ⇒ Boolean
Reschedule the task using the given delay and the current time.
-
#reset ⇒ Boolean
Reschedule the task using the original delay and the current time.
-
#schedule_time ⇒ Float
The monotonic time at which the the task is scheduled to be executed.
Constructor Details
#initialize(delay, opts = {}) { ... } ⇒ ScheduledTask
Schedule a task for execution at a specified future time.
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/concurrent-ruby/concurrent/scheduled_task.rb', line 178 def initialize(delay, opts = {}, &task) raise ArgumentError.new('no block given') unless block_given? raise ArgumentError.new('seconds must be greater than zero') if delay.to_f < 0.0 super(NULL, opts, &nil) synchronize do ns_set_state(:unscheduled) @parent = opts.fetch(:timer_set, Concurrent.global_timer_set) @args = get_arguments_from(opts) @delay = delay.to_f @task = task @time = nil @executor = Options.(opts) || Concurrent.global_io_executor self.observers = Collection::CopyOnNotifyObserverSet.new end end |
Class Method Details
.execute(delay, opts = {}, &task) ⇒ ScheduledTask
Create a new ScheduledTask
object with the given block, execute it, and return the
:pending
object.
290 291 292 |
# File 'lib/concurrent-ruby/concurrent/scheduled_task.rb', line 290 def self.execute(delay, opts = {}, &task) new(delay, opts, &task).execute end |
Instance Method Details
#cancel ⇒ Boolean
Cancel this task and prevent it from executing. A task can only be cancelled if it is pending or unscheduled.
235 236 237 238 239 240 241 242 243 244 |
# File 'lib/concurrent-ruby/concurrent/scheduled_task.rb', line 235 def cancel if compare_and_set_state(:cancelled, :pending, :unscheduled) complete(false, nil, CancelledOperationError.new) # To avoid deadlocks this call must occur outside of #synchronize # Changing the state above should prevent redundant calls @parent.send(:remove_task, self) else false end end |
#cancelled? ⇒ Boolean
Has the task been cancelled?
220 221 222 |
# File 'lib/concurrent-ruby/concurrent/scheduled_task.rb', line 220 def cancelled? synchronize { ns_check_state?(:cancelled) } end |
#execute ⇒ ScheduledTask
Execute an :unscheduled
ScheduledTask
. Immediately sets the state to :pending
and starts counting down toward execution. Does nothing if the ScheduledTask
is
in any state other than :unscheduled
.
273 274 275 276 277 278 |
# File 'lib/concurrent-ruby/concurrent/scheduled_task.rb', line 273 def execute if compare_and_set_state(:pending, :unscheduled) synchronize{ ns_schedule(@delay) } end self end |
#initial_delay ⇒ Float
The delay
value given at instanciation.
199 200 201 |
# File 'lib/concurrent-ruby/concurrent/scheduled_task.rb', line 199 def initial_delay synchronize { @delay } end |
#processing? ⇒ Boolean
In the task execution in progress?
227 228 229 |
# File 'lib/concurrent-ruby/concurrent/scheduled_task.rb', line 227 def processing? synchronize { ns_check_state?(:processing) } end |
#reschedule(delay) ⇒ Boolean
Reschedule the task using the given delay and the current time.
A task can only be reset while it is :pending
.
262 263 264 265 266 |
# File 'lib/concurrent-ruby/concurrent/scheduled_task.rb', line 262 def reschedule(delay) delay = delay.to_f raise ArgumentError.new('seconds must be greater than zero') if delay < 0.0 synchronize{ ns_reschedule(delay) } end |
#reset ⇒ Boolean
Reschedule the task using the original delay and the current time.
A task can only be reset while it is :pending
.
250 251 252 |
# File 'lib/concurrent-ruby/concurrent/scheduled_task.rb', line 250 def reset synchronize{ ns_reschedule(@delay) } end |
#schedule_time ⇒ Float
The monotonic time at which the the task is scheduled to be executed.
206 207 208 |
# File 'lib/concurrent-ruby/concurrent/scheduled_task.rb', line 206 def schedule_time synchronize { @time } end |