concurrent-ruby 0.8.0.pre2-java → 0.9.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +114 -3
- data/README.md +111 -55
- data/lib/concurrent.rb +90 -14
- data/lib/concurrent/async.rb +143 -51
- data/lib/concurrent/atom.rb +131 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +57 -107
- data/lib/concurrent/atomic/atomic_fixnum.rb +73 -101
- data/lib/concurrent/atomic/atomic_reference.rb +49 -0
- data/lib/concurrent/atomic/condition.rb +23 -12
- data/lib/concurrent/atomic/count_down_latch.rb +23 -21
- data/lib/concurrent/atomic/cyclic_barrier.rb +47 -47
- data/lib/concurrent/atomic/event.rb +33 -42
- data/lib/concurrent/atomic/read_write_lock.rb +252 -0
- data/lib/concurrent/atomic/semaphore.rb +64 -89
- data/lib/concurrent/atomic/thread_local_var.rb +130 -58
- data/lib/concurrent/atomic/thread_local_var/weak_key_map.rb +236 -0
- data/lib/concurrent/atomic_reference/direct_update.rb +34 -3
- data/lib/concurrent/atomic_reference/jruby.rb +6 -3
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +17 -39
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +3 -0
- data/lib/concurrent/atomic_reference/rbx.rb +4 -1
- data/lib/concurrent/atomic_reference/ruby.rb +6 -3
- data/lib/concurrent/atomics.rb +74 -4
- data/lib/concurrent/collection/copy_on_notify_observer_set.rb +115 -0
- data/lib/concurrent/collection/copy_on_write_observer_set.rb +119 -0
- data/lib/concurrent/collection/priority_queue.rb +300 -245
- data/lib/concurrent/concern/deprecation.rb +34 -0
- data/lib/concurrent/concern/dereferenceable.rb +88 -0
- data/lib/concurrent/concern/logging.rb +27 -0
- data/lib/concurrent/concern/obligation.rb +228 -0
- data/lib/concurrent/concern/observable.rb +85 -0
- data/lib/concurrent/configuration.rb +234 -109
- data/lib/concurrent/dataflow.rb +2 -3
- data/lib/concurrent/delay.rb +141 -50
- data/lib/concurrent/edge.rb +30 -0
- data/lib/concurrent/errors.rb +19 -7
- data/lib/concurrent/exchanger.rb +25 -1
- data/lib/concurrent/executor/cached_thread_pool.rb +51 -33
- data/lib/concurrent/executor/executor.rb +46 -299
- data/lib/concurrent/executor/executor_service.rb +521 -0
- data/lib/concurrent/executor/fixed_thread_pool.rb +196 -23
- data/lib/concurrent/executor/immediate_executor.rb +9 -9
- data/lib/concurrent/executor/indirect_immediate_executor.rb +4 -3
- data/lib/concurrent/executor/java_single_thread_executor.rb +17 -16
- data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
- data/lib/concurrent/executor/safe_task_executor.rb +5 -4
- data/lib/concurrent/executor/serialized_execution.rb +22 -18
- data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
- data/lib/concurrent/executor/single_thread_executor.rb +32 -21
- data/lib/concurrent/executor/thread_pool_executor.rb +73 -60
- data/lib/concurrent/executor/timer_set.rb +96 -84
- data/lib/concurrent/executors.rb +1 -1
- data/lib/concurrent/future.rb +71 -38
- data/lib/concurrent/immutable_struct.rb +89 -0
- data/lib/concurrent/ivar.rb +152 -60
- data/lib/concurrent/lazy_register.rb +40 -20
- data/lib/concurrent/maybe.rb +226 -0
- data/lib/concurrent/mutable_struct.rb +227 -0
- data/lib/concurrent/mvar.rb +44 -43
- data/lib/concurrent/promise.rb +229 -136
- data/lib/concurrent/scheduled_task.rb +341 -43
- data/lib/concurrent/settable_struct.rb +127 -0
- data/lib/concurrent/synchronization.rb +17 -0
- data/lib/concurrent/synchronization/abstract_object.rb +163 -0
- data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
- data/lib/concurrent/synchronization/condition.rb +53 -0
- data/lib/concurrent/synchronization/java_object.rb +34 -0
- data/lib/concurrent/synchronization/lock.rb +32 -0
- data/lib/concurrent/synchronization/monitor_object.rb +26 -0
- data/lib/concurrent/synchronization/mutex_object.rb +43 -0
- data/lib/concurrent/synchronization/object.rb +78 -0
- data/lib/concurrent/synchronization/rbx_object.rb +75 -0
- data/lib/concurrent/timer_task.rb +92 -103
- data/lib/concurrent/tvar.rb +42 -38
- data/lib/concurrent/utilities.rb +3 -1
- data/lib/concurrent/utility/at_exit.rb +97 -0
- data/lib/concurrent/utility/engine.rb +44 -0
- data/lib/concurrent/utility/monotonic_time.rb +59 -0
- data/lib/concurrent/utility/native_extension_loader.rb +56 -0
- data/lib/concurrent/utility/processor_counter.rb +156 -0
- data/lib/concurrent/utility/timeout.rb +18 -14
- data/lib/concurrent/utility/timer.rb +11 -6
- data/lib/concurrent/version.rb +2 -1
- data/lib/concurrent_ruby.rb +1 -0
- data/lib/concurrent_ruby_ext.jar +0 -0
- metadata +46 -66
- data/lib/concurrent/actor.rb +0 -103
- data/lib/concurrent/actor/behaviour.rb +0 -70
- data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
- data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
- data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
- data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
- data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
- data/lib/concurrent/actor/behaviour/linking.rb +0 -45
- data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
- data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
- data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
- data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
- data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
- data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
- data/lib/concurrent/actor/behaviour/termination.rb +0 -54
- data/lib/concurrent/actor/context.rb +0 -154
- data/lib/concurrent/actor/core.rb +0 -217
- data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
- data/lib/concurrent/actor/envelope.rb +0 -41
- data/lib/concurrent/actor/errors.rb +0 -27
- data/lib/concurrent/actor/internal_delegations.rb +0 -49
- data/lib/concurrent/actor/public_delegations.rb +0 -40
- data/lib/concurrent/actor/reference.rb +0 -81
- data/lib/concurrent/actor/root.rb +0 -37
- data/lib/concurrent/actor/type_check.rb +0 -48
- data/lib/concurrent/actor/utils.rb +0 -10
- data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
- data/lib/concurrent/actor/utils/balancer.rb +0 -42
- data/lib/concurrent/actor/utils/broadcast.rb +0 -52
- data/lib/concurrent/actor/utils/pool.rb +0 -59
- data/lib/concurrent/actress.rb +0 -3
- data/lib/concurrent/agent.rb +0 -209
- data/lib/concurrent/atomic.rb +0 -92
- data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
- data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
- data/lib/concurrent/atomic/synchronization.rb +0 -51
- data/lib/concurrent/channel/buffered_channel.rb +0 -85
- data/lib/concurrent/channel/channel.rb +0 -41
- data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
- data/lib/concurrent/channel/waitable_list.rb +0 -40
- data/lib/concurrent/channels.rb +0 -5
- data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
- data/lib/concurrent/collection/ring_buffer.rb +0 -59
- data/lib/concurrent/collections.rb +0 -3
- data/lib/concurrent/dereferenceable.rb +0 -108
- data/lib/concurrent/executor/java_cached_thread_pool.rb +0 -32
- data/lib/concurrent/executor/java_fixed_thread_pool.rb +0 -31
- data/lib/concurrent/executor/ruby_cached_thread_pool.rb +0 -29
- data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +0 -32
- data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
- data/lib/concurrent/logging.rb +0 -20
- data/lib/concurrent/obligation.rb +0 -171
- data/lib/concurrent/observable.rb +0 -73
- data/lib/concurrent/options_parser.rb +0 -48
- data/lib/concurrent/utility/processor_count.rb +0 -152
- data/lib/extension_helper.rb +0 -37
@@ -1,79 +1,377 @@
|
|
1
|
+
require 'concurrent/errors'
|
1
2
|
require 'concurrent/ivar'
|
2
|
-
require 'concurrent/
|
3
|
-
require 'concurrent/
|
3
|
+
require 'concurrent/configuration'
|
4
|
+
require 'concurrent/collection/copy_on_notify_observer_set'
|
5
|
+
require 'concurrent/executor/executor'
|
6
|
+
require 'concurrent/executor/timer_set'
|
7
|
+
require 'concurrent/utility/monotonic_time'
|
8
|
+
require 'concurrent/concern/deprecation'
|
4
9
|
|
5
10
|
module Concurrent
|
11
|
+
include Concern::Deprecation
|
6
12
|
|
7
|
-
#
|
13
|
+
# `ScheduledTask` is a close relative of `Concurrent::Future` but with one
|
14
|
+
# important difference: A `Future` is set to execute as soon as possible
|
15
|
+
# whereas a `ScheduledTask` is set to execute after a specified delay. This
|
16
|
+
# implementation is loosely based on Java's
|
17
|
+
# [ScheduledExecutorService](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledExecutorService.html).
|
18
|
+
# It is a more feature-rich variant of {Concurrent.timer}.
|
19
|
+
#
|
20
|
+
# The *intended* schedule time of task execution is set on object construction
|
21
|
+
# with the `delay` argument. The delay is a numeric (floating point or integer)
|
22
|
+
# representing a number of seconds in the future. Any other value or a numeric
|
23
|
+
# equal to or less than zero will result in an exception. The *actual* schedule
|
24
|
+
# time of task execution is set when the `execute` method is called.
|
25
|
+
#
|
26
|
+
# The constructor can also be given zero or more processing options. Currently
|
27
|
+
# the only supported options are those recognized by the
|
28
|
+
# [Dereferenceable](Dereferenceable) module.
|
29
|
+
#
|
30
|
+
# The final constructor argument is a block representing the task to be performed.
|
31
|
+
# If no block is given an `ArgumentError` will be raised.
|
32
|
+
#
|
33
|
+
# **States**
|
34
|
+
#
|
35
|
+
# `ScheduledTask` mixes in the [Obligation](Obligation) module thus giving it
|
36
|
+
# "future" behavior. This includes the expected lifecycle states. `ScheduledTask`
|
37
|
+
# has one additional state, however. While the task (block) is being executed the
|
38
|
+
# state of the object will be `:processing`. This additional state is necessary
|
39
|
+
# because it has implications for task cancellation.
|
40
|
+
#
|
41
|
+
# **Cancellation**
|
42
|
+
#
|
43
|
+
# A `:pending` task can be cancelled using the `#cancel` method. A task in any
|
44
|
+
# other state, including `:processing`, cannot be cancelled. The `#cancel`
|
45
|
+
# method returns a boolean indicating the success of the cancellation attempt.
|
46
|
+
# A cancelled `ScheduledTask` cannot be restarted. It is immutable.
|
47
|
+
#
|
48
|
+
# **Obligation and Observation**
|
49
|
+
#
|
50
|
+
# The result of a `ScheduledTask` can be obtained either synchronously or
|
51
|
+
# asynchronously. `ScheduledTask` mixes in both the [Obligation](Obligation)
|
52
|
+
# module and the
|
53
|
+
# [Observable](http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html)
|
54
|
+
# module from the Ruby standard library. With one exception `ScheduledTask`
|
55
|
+
# behaves identically to [Future](Observable) with regard to these modules.
|
56
|
+
#
|
57
|
+
# @!macro copy_options
|
58
|
+
#
|
59
|
+
# @example Basic usage
|
60
|
+
#
|
61
|
+
# require 'concurrent'
|
62
|
+
# require 'thread' # for Queue
|
63
|
+
# require 'open-uri' # for open(uri)
|
64
|
+
#
|
65
|
+
# class Ticker
|
66
|
+
# def get_year_end_closing(symbol, year)
|
67
|
+
# uri = "http://ichart.finance.yahoo.com/table.csv?s=#{symbol}&a=11&b=01&c=#{year}&d=11&e=31&f=#{year}&g=m"
|
68
|
+
# data = open(uri) {|f| f.collect{|line| line.strip } }
|
69
|
+
# data[1].split(',')[4].to_f
|
70
|
+
# end
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# # Future
|
74
|
+
# price = Concurrent::Future.execute{ Ticker.new.get_year_end_closing('TWTR', 2013) }
|
75
|
+
# price.state #=> :pending
|
76
|
+
# sleep(1) # do other stuff
|
77
|
+
# price.value #=> 63.65
|
78
|
+
# price.state #=> :fulfilled
|
79
|
+
#
|
80
|
+
# # ScheduledTask
|
81
|
+
# task = Concurrent::ScheduledTask.execute(2){ Ticker.new.get_year_end_closing('INTC', 2013) }
|
82
|
+
# task.state #=> :pending
|
83
|
+
# sleep(3) # do other stuff
|
84
|
+
# task.value #=> 25.96
|
85
|
+
#
|
86
|
+
# @example Successful task execution
|
87
|
+
#
|
88
|
+
# task = Concurrent::ScheduledTask.new(2){ 'What does the fox say?' }
|
89
|
+
# task.state #=> :unscheduled
|
90
|
+
# task.execute
|
91
|
+
# task.state #=> pending
|
92
|
+
#
|
93
|
+
# # wait for it...
|
94
|
+
# sleep(3)
|
95
|
+
#
|
96
|
+
# task.unscheduled? #=> false
|
97
|
+
# task.pending? #=> false
|
98
|
+
# task.fulfilled? #=> true
|
99
|
+
# task.rejected? #=> false
|
100
|
+
# task.value #=> 'What does the fox say?'
|
101
|
+
#
|
102
|
+
# @example One line creation and execution
|
103
|
+
#
|
104
|
+
# task = Concurrent::ScheduledTask.new(2){ 'What does the fox say?' }.execute
|
105
|
+
# task.state #=> pending
|
106
|
+
#
|
107
|
+
# task = Concurrent::ScheduledTask.execute(2){ 'What do you get when you multiply 6 by 9?' }
|
108
|
+
# task.state #=> pending
|
109
|
+
#
|
110
|
+
# @example Failed task execution
|
111
|
+
#
|
112
|
+
# task = Concurrent::ScheduledTask.execute(2){ raise StandardError.new('Call me maybe?') }
|
113
|
+
# task.pending? #=> true
|
114
|
+
#
|
115
|
+
# # wait for it...
|
116
|
+
# sleep(3)
|
117
|
+
#
|
118
|
+
# task.unscheduled? #=> false
|
119
|
+
# task.pending? #=> false
|
120
|
+
# task.fulfilled? #=> false
|
121
|
+
# task.rejected? #=> true
|
122
|
+
# task.value #=> nil
|
123
|
+
# task.reason #=> #<StandardError: Call me maybe?>
|
124
|
+
#
|
125
|
+
# @example Task execution with observation
|
126
|
+
#
|
127
|
+
# observer = Class.new{
|
128
|
+
# def update(time, value, reason)
|
129
|
+
# puts "The task completed at #{time} with value '#{value}'"
|
130
|
+
# end
|
131
|
+
# }.new
|
132
|
+
#
|
133
|
+
# task = Concurrent::ScheduledTask.new(2){ 'What does the fox say?' }
|
134
|
+
# task.add_observer(observer)
|
135
|
+
# task.execute
|
136
|
+
# task.pending? #=> true
|
137
|
+
#
|
138
|
+
# # wait for it...
|
139
|
+
# sleep(3)
|
140
|
+
#
|
141
|
+
# #>> The task completed at 2013-11-07 12:26:09 -0500 with value 'What does the fox say?'
|
142
|
+
#
|
143
|
+
# @!macro monotonic_clock_warning
|
144
|
+
#
|
145
|
+
# @see Concurrent.timer
|
8
146
|
class ScheduledTask < IVar
|
147
|
+
include Comparable
|
9
148
|
|
10
|
-
|
149
|
+
# The executor on which to execute the task.
|
150
|
+
# @!visibility private
|
151
|
+
attr_reader :executor
|
11
152
|
|
12
|
-
|
153
|
+
# Schedule a task for execution at a specified future time.
|
154
|
+
#
|
155
|
+
# @param [Float] delay the number of seconds to wait for before executing the task
|
156
|
+
#
|
157
|
+
# @yield the task to be performed
|
158
|
+
#
|
159
|
+
# @!macro executor_and_deref_options
|
160
|
+
#
|
161
|
+
# @option opts [object, Array] :args zero or more arguments to be passed the task
|
162
|
+
# block on execution
|
163
|
+
#
|
164
|
+
# @raise [ArgumentError] When no block is given
|
165
|
+
# @raise [ArgumentError] When given a time that is in the past
|
166
|
+
#
|
167
|
+
# @!macro [attach] deprecated_scheduling_by_clock_time
|
168
|
+
#
|
169
|
+
# @note Scheduling is now based on a monotonic clock. This makes the timer much
|
170
|
+
# more accurate, but only when scheduling based on a delay interval.
|
171
|
+
# Scheduling a task based on a clock time is deprecated. It will still work
|
172
|
+
# but will not be supported in the 1.0 release.
|
173
|
+
def initialize(delay, opts = {}, &task)
|
13
174
|
raise ArgumentError.new('no block given') unless block_given?
|
14
|
-
|
175
|
+
super(IVar::NO_VALUE, opts, &nil)
|
176
|
+
synchronize do
|
177
|
+
@delay = calculate_delay!(delay) # may raise exception
|
178
|
+
ns_set_state(:unscheduled)
|
179
|
+
@parent = opts.fetch(:timer_set, Concurrent.global_timer_set)
|
180
|
+
@args = get_arguments_from(opts)
|
181
|
+
@task = task
|
182
|
+
@time = nil
|
183
|
+
@executor = Executor.executor_from_options(opts) || Concurrent.global_io_executor
|
184
|
+
self.observers = Collection::CopyOnNotifyObserverSet.new
|
185
|
+
end
|
186
|
+
end
|
15
187
|
|
16
|
-
|
188
|
+
# The `delay` value given at instanciation.
|
189
|
+
#
|
190
|
+
# @return [Float] the initial delay.
|
191
|
+
def initial_delay
|
192
|
+
synchronize { @delay }
|
193
|
+
end
|
17
194
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
195
|
+
# The `delay` value given at instanciation.
|
196
|
+
#
|
197
|
+
# @return [Float] the initial delay.
|
198
|
+
#
|
199
|
+
# @deprecated use {#initial_delay} instead
|
200
|
+
def delay
|
201
|
+
deprecated_method 'delay', 'initial_delay'
|
202
|
+
initial_delay
|
23
203
|
end
|
24
204
|
|
25
|
-
#
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
self
|
31
|
-
end
|
205
|
+
# The monotonic time at which the the task is scheduled to be executed.
|
206
|
+
#
|
207
|
+
# @return [Float] the schedule time or nil if `unscheduled`
|
208
|
+
def schedule_time
|
209
|
+
synchronize { @time }
|
32
210
|
end
|
33
211
|
|
34
|
-
#
|
35
|
-
|
36
|
-
|
212
|
+
# Comparator which orders by schedule time.
|
213
|
+
#
|
214
|
+
# @!visibility private
|
215
|
+
def <=>(other)
|
216
|
+
schedule_time <=> other.schedule_time
|
37
217
|
end
|
38
218
|
|
219
|
+
# Has the task been cancelled?
|
220
|
+
#
|
221
|
+
# @return [Boolean] true if the task is in the given state else false
|
39
222
|
def cancelled?
|
40
|
-
|
223
|
+
synchronize { ns_check_state?(:cancelled) }
|
41
224
|
end
|
42
225
|
|
226
|
+
# In the task execution in progress?
|
227
|
+
#
|
228
|
+
# @return [Boolean] true if the task is in the given state else false
|
229
|
+
def processing?
|
230
|
+
synchronize { ns_check_state?(:processing) }
|
231
|
+
end
|
232
|
+
|
233
|
+
# In the task execution in progress?
|
234
|
+
#
|
235
|
+
# @return [Boolean] true if the task is in the given state else false
|
236
|
+
#
|
237
|
+
# @deprecated Use {#processing?} instead.
|
43
238
|
def in_progress?
|
44
|
-
|
239
|
+
deprecated_method 'in_progress?', 'processing?'
|
240
|
+
processing?
|
45
241
|
end
|
46
242
|
|
243
|
+
# Cancel this task and prevent it from executing. A task can only be
|
244
|
+
# cancelled if it is pending or unscheduled.
|
245
|
+
#
|
246
|
+
# @return [Boolean] true if successfully cancelled else false
|
47
247
|
def cancel
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
248
|
+
if compare_and_set_state(:cancelled, :pending, :unscheduled)
|
249
|
+
complete(false, nil, CancelledOperationError.new)
|
250
|
+
# To avoid deadlocks this call must occur outside of #synchronize
|
251
|
+
# Changing the state above should prevent redundant calls
|
252
|
+
@parent.send(:remove_task, self)
|
253
|
+
else
|
254
|
+
false
|
52
255
|
end
|
53
256
|
end
|
54
|
-
alias_method :stop, :cancel
|
55
257
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
258
|
+
# Cancel this task and prevent it from executing. A task can only be
|
259
|
+
# cancelled if it is `:pending` or `:unscheduled`.
|
260
|
+
#
|
261
|
+
# @return [Boolean] true if successfully cancelled else false
|
262
|
+
#
|
263
|
+
# @deprecated Use {#cancel} instead.
|
264
|
+
def stop
|
265
|
+
deprecated_method 'stop', 'cancel'
|
266
|
+
cancel
|
267
|
+
end
|
268
|
+
|
269
|
+
# Reschedule the task using the original delay and the current time.
|
270
|
+
# A task can only be reset while it is `:pending`.
|
271
|
+
#
|
272
|
+
# @return [Boolean] true if successfully rescheduled else false
|
273
|
+
def reset
|
274
|
+
synchronize{ ns_reschedule(@delay) }
|
275
|
+
end
|
276
|
+
|
277
|
+
# Reschedule the task using the given delay and the current time.
|
278
|
+
# A task can only be reset while it is `:pending`.
|
279
|
+
#
|
280
|
+
# @param [Float] delay the number of seconds to wait for before executing the task
|
281
|
+
#
|
282
|
+
# @return [Boolean] true if successfully rescheduled else false
|
283
|
+
#
|
284
|
+
# @raise [ArgumentError] When given a time that is in the past
|
285
|
+
def reschedule(delay)
|
286
|
+
synchronize{ ns_reschedule(calculate_delay!(delay)) }
|
60
287
|
end
|
61
288
|
|
62
|
-
|
289
|
+
# Execute an `:unscheduled` `ScheduledTask`. Immediately sets the state to `:pending`
|
290
|
+
# and starts counting down toward execution. Does nothing if the `ScheduledTask` is
|
291
|
+
# in any state other than `:unscheduled`.
|
292
|
+
#
|
293
|
+
# @return [ScheduledTask] a reference to `self`
|
294
|
+
def execute
|
295
|
+
if compare_and_set_state(:pending, :unscheduled)
|
296
|
+
synchronize{ ns_schedule(@delay) }
|
297
|
+
end
|
298
|
+
self
|
299
|
+
end
|
63
300
|
|
64
|
-
|
301
|
+
# Create a new `ScheduledTask` object with the given block, execute it, and return the
|
302
|
+
# `:pending` object.
|
303
|
+
#
|
304
|
+
# @param [Float] delay the number of seconds to wait for before executing the task
|
305
|
+
#
|
306
|
+
# @!macro executor_and_deref_options
|
307
|
+
#
|
308
|
+
# @return [ScheduledTask] the newly created `ScheduledTask` in the `:pending` state
|
309
|
+
#
|
310
|
+
# @raise [ArgumentError] if no block is given
|
311
|
+
#
|
312
|
+
# @!macro deprecated_scheduling_by_clock_time
|
313
|
+
def self.execute(delay, opts = {}, &task)
|
314
|
+
new(delay, opts, &task).execute
|
315
|
+
end
|
65
316
|
|
317
|
+
# Execute the task.
|
318
|
+
#
|
319
|
+
# @!visibility private
|
66
320
|
def process_task
|
67
|
-
|
68
|
-
|
321
|
+
safe_execute(@task, @args)
|
322
|
+
end
|
323
|
+
|
324
|
+
protected :set, :try_set, :fail, :complete
|
325
|
+
|
326
|
+
protected
|
69
327
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
328
|
+
# Schedule the task using the given delay and the current time.
|
329
|
+
#
|
330
|
+
# @param [Float] delay the number of seconds to wait for before executing the task
|
331
|
+
#
|
332
|
+
# @return [Boolean] true if successfully rescheduled else false
|
333
|
+
#
|
334
|
+
# @!visibility private
|
335
|
+
def ns_schedule(delay)
|
336
|
+
@delay = delay
|
337
|
+
@time = Concurrent.monotonic_time + @delay
|
338
|
+
@parent.send(:post_task, self)
|
339
|
+
end
|
340
|
+
|
341
|
+
# Reschedule the task using the given delay and the current time.
|
342
|
+
# A task can only be reset while it is `:pending`.
|
343
|
+
#
|
344
|
+
# @param [Float] delay the number of seconds to wait for before executing the task
|
345
|
+
#
|
346
|
+
# @return [Boolean] true if successfully rescheduled else false
|
347
|
+
#
|
348
|
+
# @!visibility private
|
349
|
+
def ns_reschedule(delay)
|
350
|
+
return false unless ns_check_state?(:pending)
|
351
|
+
@parent.send(:remove_task, self) && ns_schedule(delay)
|
352
|
+
end
|
74
353
|
|
75
|
-
|
76
|
-
|
354
|
+
# Calculate the actual delay in seconds based on the given delay.
|
355
|
+
#
|
356
|
+
# @param [Float] delay the number of seconds to wait for before executing the task
|
357
|
+
#
|
358
|
+
# @return [Float] the number of seconds to delay
|
359
|
+
#
|
360
|
+
# @raise [ArgumentError] if the intended execution time is not in the future
|
361
|
+
# @raise [ArgumentError] if no block is given
|
362
|
+
#
|
363
|
+
# @!macro deprecated_scheduling_by_clock_time
|
364
|
+
#
|
365
|
+
# @!visibility private
|
366
|
+
def calculate_delay!(delay)
|
367
|
+
if delay.is_a?(Time)
|
368
|
+
deprecated 'Use an interval not a clock time; schedule is now based on a monotonic clock'
|
369
|
+
now = Time.now
|
370
|
+
raise ArgumentError.new('schedule time must be in the future') if delay <= now
|
371
|
+
delay.to_f - now.to_f
|
372
|
+
else
|
373
|
+
raise ArgumentError.new('seconds must be greater than zero') if delay.to_f < 0.0
|
374
|
+
delay.to_f
|
77
375
|
end
|
78
376
|
end
|
79
377
|
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'concurrent/synchronization/abstract_struct'
|
2
|
+
require 'concurrent/errors'
|
3
|
+
require 'concurrent/synchronization'
|
4
|
+
|
5
|
+
module Concurrent
|
6
|
+
|
7
|
+
# An thread-safe, write-once variation of Ruby's standard `Struct`.
|
8
|
+
# Each member can have its value set at most once, either at construction
|
9
|
+
# or any time thereafter. Attempting to assign a value to a member
|
10
|
+
# that has already been set will result in a `Concurrent::ImmutabilityError`.
|
11
|
+
#
|
12
|
+
# @see http://ruby-doc.org/core-2.2.0/Struct.html Ruby standard library `Struct`
|
13
|
+
# @see http://en.wikipedia.org/wiki/Final_(Java) Java `final` keyword
|
14
|
+
module SettableStruct
|
15
|
+
include Synchronization::AbstractStruct
|
16
|
+
|
17
|
+
# @!macro struct_values
|
18
|
+
def values
|
19
|
+
synchronize { ns_values }
|
20
|
+
end
|
21
|
+
alias_method :to_a, :values
|
22
|
+
|
23
|
+
# @!macro struct_values_at
|
24
|
+
def values_at(*indexes)
|
25
|
+
synchronize { ns_values_at(indexes) }
|
26
|
+
end
|
27
|
+
|
28
|
+
# @!macro struct_inspect
|
29
|
+
def inspect
|
30
|
+
synchronize { ns_inspect }
|
31
|
+
end
|
32
|
+
alias_method :to_s, :inspect
|
33
|
+
|
34
|
+
# @!macro struct_merge
|
35
|
+
def merge(other, &block)
|
36
|
+
synchronize { ns_merge(other, &block) }
|
37
|
+
end
|
38
|
+
|
39
|
+
# @!macro struct_to_h
|
40
|
+
def to_h
|
41
|
+
synchronize { ns_to_h }
|
42
|
+
end
|
43
|
+
|
44
|
+
# @!macro struct_get
|
45
|
+
def [](member)
|
46
|
+
synchronize { ns_get(member) }
|
47
|
+
end
|
48
|
+
|
49
|
+
# @!macro struct_equality
|
50
|
+
def ==(other)
|
51
|
+
synchronize { ns_equality(other) }
|
52
|
+
end
|
53
|
+
|
54
|
+
# @!macro struct_each
|
55
|
+
def each(&block)
|
56
|
+
return enum_for(:each) unless block_given?
|
57
|
+
synchronize { ns_each(&block) }
|
58
|
+
end
|
59
|
+
|
60
|
+
# @!macro struct_each_pair
|
61
|
+
def each_pair(&block)
|
62
|
+
return enum_for(:each_pair) unless block_given?
|
63
|
+
synchronize { ns_each_pair(&block) }
|
64
|
+
end
|
65
|
+
|
66
|
+
# @!macro struct_select
|
67
|
+
def select(&block)
|
68
|
+
return enum_for(:select) unless block_given?
|
69
|
+
synchronize { ns_select(&block) }
|
70
|
+
end
|
71
|
+
|
72
|
+
# @!macro struct_set
|
73
|
+
#
|
74
|
+
# @raise [Concurrent::ImmutabilityError] if the given member has already been set
|
75
|
+
def []=(member, value)
|
76
|
+
if member.is_a? Integer
|
77
|
+
if member >= @values.length
|
78
|
+
raise IndexError.new("offset #{member} too large for struct(size:#{@values.length})")
|
79
|
+
end
|
80
|
+
synchronize do
|
81
|
+
unless @values[member].nil?
|
82
|
+
raise Concurrent::ImmutabilityError.new('struct member has already been set')
|
83
|
+
end
|
84
|
+
@values[member] = value
|
85
|
+
end
|
86
|
+
else
|
87
|
+
send("#{member}=", value)
|
88
|
+
end
|
89
|
+
rescue NoMethodError
|
90
|
+
raise NameError.new("no member '#{member}' in struct")
|
91
|
+
end
|
92
|
+
|
93
|
+
# @!macro struct_new
|
94
|
+
def self.new(*args, &block)
|
95
|
+
clazz_name = nil
|
96
|
+
if args.length == 0
|
97
|
+
raise ArgumentError.new('wrong number of arguments (0 for 1+)')
|
98
|
+
elsif args.length > 0 && args.first.is_a?(String)
|
99
|
+
clazz_name = args.shift
|
100
|
+
end
|
101
|
+
FACTORY.define_struct(clazz_name, args, &block)
|
102
|
+
end
|
103
|
+
|
104
|
+
FACTORY = Class.new(Synchronization::Object) do
|
105
|
+
def define_struct(name, members, &block)
|
106
|
+
synchronize do
|
107
|
+
clazz = Synchronization::AbstractStruct.define_struct_class(SettableStruct, Synchronization::Object, name, members, &block)
|
108
|
+
members.each_with_index do |member, index|
|
109
|
+
clazz.send(:define_method, member) do
|
110
|
+
synchronize { @values[index] }
|
111
|
+
end
|
112
|
+
clazz.send(:define_method, "#{member}=") do |value|
|
113
|
+
synchronize do
|
114
|
+
unless @values[index].nil?
|
115
|
+
raise Concurrent::ImmutabilityError.new('struct member has already been set')
|
116
|
+
end
|
117
|
+
@values[index] = value
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
clazz
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end.new
|
125
|
+
private_constant :FACTORY
|
126
|
+
end
|
127
|
+
end
|