Skip to content

Commit acca08d

Browse files
committed
Better documentation of TimerSet and ScheduledTask.
1 parent 260aea7 commit acca08d

File tree

3 files changed

+68
-167
lines changed

3 files changed

+68
-167
lines changed

doc/scheduled_task.md

Lines changed: 0 additions & 148 deletions
This file was deleted.

lib/concurrent/executor/timer_set.rb

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
module Concurrent
1010

11-
# Executes a collection of tasks, each after a given delay. A master thread
11+
# Executes a collection of tasks, each after a given delay. A master task
1212
# monitors the set and schedules each task for execution at the appropriate
1313
# time. Tasks are run on the global task pool or on the supplied executor.
1414
#
@@ -48,15 +48,15 @@ def initialize(opts = {})
4848
# @!macro deprecated_scheduling_by_clock_time
4949
def post(delay, *args, &task)
5050
raise ArgumentError.new('no block given') unless block_given?
51-
interval = TimerSet.calculate_interval(delay)
51+
delay = TimerSet.calculate_delay!(delay) # raises exceptions
5252

5353
mutex.synchronize do
5454
return false unless running?
5555

56-
if (interval) <= 0.01
56+
if (delay) <= 0.01
5757
@task_executor.post(*args, &task)
5858
else
59-
@queue.push(Task.new(Concurrent.monotonic_time + interval, args, task))
59+
@queue.push(Task.new(Concurrent.monotonic_time + delay, args, task))
6060
@timer_executor.post(&method(:process_tasks))
6161
end
6262
end
@@ -75,10 +75,11 @@ def <<(task)
7575
# (and destructively) clear the queue first
7676
def kill
7777
mutex.synchronize { @queue.clear }
78+
# possible race condition
7879
shutdown
7980
end
8081

81-
# Schedule a task to be execute run after a given delay (in seconds).
82+
# Schedule a task to be executed after a given delay (in seconds).
8283
#
8384
# @param [Float] delay the number of seconds to wait for before executing the task
8485
#
@@ -90,9 +91,9 @@ def kill
9091
# @!macro deprecated_scheduling_by_clock_time
9192
#
9293
# @!visibility private
93-
def self.calculate_interval(delay)
94+
def self.calculate_delay!(delay)
9495
if delay.is_a?(Time)
95-
warn '[DEPRECATED] Use an interval not a clock time, schedule is now based on a monotonic clock'
96+
warn '[DEPRECATED] Use an interval not a clock time; schedule is now based on a monotonic clock'
9697
now = Time.now
9798
raise ArgumentError.new('schedule time must be in the future') if delay <= now
9899
delay.to_f - now.to_f

lib/concurrent/scheduled_task.rb

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
module Concurrent
66

77
# `ScheduledTask` is a close relative of `Concurrent::Future` but with one
8-
# important difference. A `Future` is set to execute as soon as possible
9-
# whereas a `ScheduledTask` is set to execute at a specific time. This
8+
# important difference: A `Future` is set to execute as soon as possible
9+
# whereas a `ScheduledTask` is set to execute after a specified delay. This
1010
# implementation is loosely based on Java's
1111
# [ScheduledExecutorService](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledExecutorService.html).
1212
#
1313
# The *intended* schedule time of task execution is set on object construction
14-
# with first argument, `delay`. The delay is a numeric (floating point or integer)
14+
# with the `delay` argument. The delay is a numeric (floating point or integer)
1515
# representing a number of seconds in the future. Any other value or a numeric
1616
# equal to or less than zero will result in an exception. The *actual* schedule
1717
# time of task execution is set when the `execute` method is called.
@@ -95,6 +95,9 @@ module Concurrent
9595
# task = Concurrent::ScheduledTask.new(2){ 'What does the fox say?' }.execute
9696
# task.state #=> pending
9797
#
98+
# task = Concurrent::ScheduledTask.execute(2){ 'What do you get when you multiply 6 by 9?' }
99+
# task.state #=> pending
100+
#
98101
# @example Failed task execution
99102
#
100103
# task = Concurrent::ScheduledTask.execute(2){ raise StandardError.new('Call me maybe?') }
@@ -129,21 +132,32 @@ module Concurrent
129132
# #>> The task completed at 2013-11-07 12:26:09 -0500 with value 'What does the fox say?'
130133
#
131134
# @!macro monotonic_clock_warning
132-
#
133-
# @!macro [attach] deprecated_scheduling_by_clock_time
134-
#
135-
# @note Scheduling is now based on a monotonic clock. This makes the timer much
136-
# more accurate, but only when scheduling by passing a delay in seconds.
137-
# Scheduling a task based on a clock time is deprecated. It will still work
138-
# but will not be supported in the 1.0 release.
139135
class ScheduledTask < IVar
140136

141137
attr_reader :delay
142138

143-
# @!macro deprecated_scheduling_by_clock_time
139+
# Schedule a task for execution at a specified future time.
140+
#
141+
# @yield the task to be performed
142+
#
143+
# @param [Float] delay the number of seconds to wait for before executing the task
144+
#
145+
# @param [Hash] opts the options controlling how the future will be processed
146+
# @option opts [Boolean] :operation (false) when `true` will execute the future on the global
147+
# operation pool (for long-running operations), when `false` will execute the future on the
148+
# global task pool (for short-running tasks)
149+
# @option opts [object] :executor when provided will run all operations on
150+
# this executor rather than the global thread pool (overrides :operation)
151+
#
152+
# @!macro [attach] deprecated_scheduling_by_clock_time
153+
#
154+
# @note Scheduling is now based on a monotonic clock. This makes the timer much
155+
# more accurate, but only when scheduling based on a delay interval.
156+
# Scheduling a task based on a clock time is deprecated. It will still work
157+
# but will not be supported in the 1.0 release.
144158
def initialize(delay, opts = {}, &block)
145159
raise ArgumentError.new('no block given') unless block_given?
146-
@delay = TimerSet.calculate_interval(delay)
160+
@delay = TimerSet.calculate_delay!(delay)
147161

148162
super(NO_VALUE, opts)
149163

@@ -153,6 +167,12 @@ def initialize(delay, opts = {}, &block)
153167
@executor = OptionsParser::get_executor_from(opts) || Concurrent.configuration.global_operation_pool
154168
end
155169

170+
171+
# Execute an `:unscheduled` `ScheduledTask`. Immediately sets the state to `:pending`
172+
# and starts counting down toward execution. Does nothing if the `ScheduledTask` is
173+
# in any state other than `:unscheduled`.
174+
#
175+
# @return [ScheduledTask] a reference to `self`
156176
def execute
157177
if compare_and_set_state(:pending, :unscheduled)
158178
@schedule_time = Time.now + @delay
@@ -161,6 +181,22 @@ def execute
161181
end
162182
end
163183

184+
# Create a new `ScheduledTask` object with the given block, execute it, and return the
185+
# `:pending` object.
186+
#
187+
# @param [Float] delay the number of seconds to wait for before executing the task
188+
#
189+
# @param [Hash] opts the options controlling how the future will be processed
190+
# @option opts [Boolean] :operation (false) when `true` will execute the future on the global
191+
# operation pool (for long-running operations), when `false` will execute the future on the
192+
# global task pool (for short-running tasks)
193+
# @option opts [object] :executor when provided will run all operations on
194+
# this executor rather than the global thread pool (overrides :operation)
195+
#
196+
# @return [ScheduledTask] the newly created `ScheduledTask` in the `:pending` state
197+
#
198+
# @raise [ArgumentError] if no block is given
199+
#
164200
# @!macro deprecated_scheduling_by_clock_time
165201
def self.execute(delay, opts = {}, &block)
166202
return ScheduledTask.new(delay, opts, &block).execute
@@ -172,14 +208,25 @@ def schedule_time
172208
@schedule_time
173209
end
174210

211+
# Has the task been cancelled?
212+
#
213+
# @return [Boolean] true if the task is in the given state else false
175214
def cancelled?
176215
state == :cancelled
177216
end
178217

218+
# In the task execution in progress?
219+
#
220+
# @return [Boolean] true if the task is in the given state else false
179221
def in_progress?
180222
state == :in_progress
181223
end
182224

225+
# Cancel this task and prevent it from executing. A task can only be
226+
# cancelled if it is pending or unscheduled.
227+
#
228+
# @return [Boolean] true if task execution is successfully cancelled
229+
# else false
183230
def cancel
184231
if_state(:unscheduled, :pending) do
185232
@state = :cancelled
@@ -193,6 +240,7 @@ def cancel
193240

194241
private
195242

243+
# @!visibility private
196244
def process_task
197245
if compare_and_set_state(:in_progress, :pending)
198246
success, val, reason = SafeTaskExecutor.new(@task).execute

0 commit comments

Comments
 (0)