concurrent-ruby 1.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +478 -0
- data/Gemfile +41 -0
- data/LICENSE.md +23 -0
- data/README.md +381 -0
- data/Rakefile +327 -0
- data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +159 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +307 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
- data/lib/concurrent-ruby.rb +1 -0
- data/lib/concurrent.rb +134 -0
- data/lib/concurrent/agent.rb +587 -0
- data/lib/concurrent/array.rb +66 -0
- data/lib/concurrent/async.rb +459 -0
- data/lib/concurrent/atom.rb +222 -0
- data/lib/concurrent/atomic/abstract_thread_local_var.rb +66 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +126 -0
- data/lib/concurrent/atomic/atomic_fixnum.rb +143 -0
- data/lib/concurrent/atomic/atomic_markable_reference.rb +164 -0
- data/lib/concurrent/atomic/atomic_reference.rb +204 -0
- data/lib/concurrent/atomic/count_down_latch.rb +100 -0
- data/lib/concurrent/atomic/cyclic_barrier.rb +128 -0
- data/lib/concurrent/atomic/event.rb +109 -0
- data/lib/concurrent/atomic/java_count_down_latch.rb +42 -0
- data/lib/concurrent/atomic/java_thread_local_var.rb +37 -0
- data/lib/concurrent/atomic/mutex_atomic_boolean.rb +62 -0
- data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +75 -0
- data/lib/concurrent/atomic/mutex_count_down_latch.rb +44 -0
- data/lib/concurrent/atomic/mutex_semaphore.rb +115 -0
- data/lib/concurrent/atomic/read_write_lock.rb +254 -0
- data/lib/concurrent/atomic/reentrant_read_write_lock.rb +379 -0
- data/lib/concurrent/atomic/ruby_thread_local_var.rb +161 -0
- data/lib/concurrent/atomic/semaphore.rb +145 -0
- data/lib/concurrent/atomic/thread_local_var.rb +104 -0
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +56 -0
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +28 -0
- data/lib/concurrent/atomics.rb +10 -0
- data/lib/concurrent/collection/copy_on_notify_observer_set.rb +107 -0
- data/lib/concurrent/collection/copy_on_write_observer_set.rb +111 -0
- data/lib/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
- data/lib/concurrent/collection/lock_free_stack.rb +158 -0
- data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
- data/lib/concurrent/collection/map/mri_map_backend.rb +66 -0
- data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +140 -0
- data/lib/concurrent/collection/map/synchronized_map_backend.rb +82 -0
- data/lib/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
- data/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb +150 -0
- data/lib/concurrent/concern/deprecation.rb +34 -0
- data/lib/concurrent/concern/dereferenceable.rb +73 -0
- data/lib/concurrent/concern/logging.rb +32 -0
- data/lib/concurrent/concern/obligation.rb +220 -0
- data/lib/concurrent/concern/observable.rb +110 -0
- data/lib/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent/configuration.rb +184 -0
- data/lib/concurrent/constants.rb +8 -0
- data/lib/concurrent/dataflow.rb +81 -0
- data/lib/concurrent/delay.rb +199 -0
- data/lib/concurrent/errors.rb +69 -0
- data/lib/concurrent/exchanger.rb +352 -0
- data/lib/concurrent/executor/abstract_executor_service.rb +134 -0
- data/lib/concurrent/executor/cached_thread_pool.rb +62 -0
- data/lib/concurrent/executor/executor_service.rb +185 -0
- data/lib/concurrent/executor/fixed_thread_pool.rb +206 -0
- data/lib/concurrent/executor/immediate_executor.rb +66 -0
- data/lib/concurrent/executor/indirect_immediate_executor.rb +44 -0
- data/lib/concurrent/executor/java_executor_service.rb +91 -0
- data/lib/concurrent/executor/java_single_thread_executor.rb +29 -0
- data/lib/concurrent/executor/java_thread_pool_executor.rb +123 -0
- data/lib/concurrent/executor/ruby_executor_service.rb +78 -0
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +22 -0
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +362 -0
- data/lib/concurrent/executor/safe_task_executor.rb +35 -0
- data/lib/concurrent/executor/serial_executor_service.rb +34 -0
- data/lib/concurrent/executor/serialized_execution.rb +107 -0
- data/lib/concurrent/executor/serialized_execution_delegator.rb +28 -0
- data/lib/concurrent/executor/simple_executor_service.rb +100 -0
- data/lib/concurrent/executor/single_thread_executor.rb +56 -0
- data/lib/concurrent/executor/thread_pool_executor.rb +87 -0
- data/lib/concurrent/executor/timer_set.rb +173 -0
- data/lib/concurrent/executors.rb +20 -0
- data/lib/concurrent/future.rb +141 -0
- data/lib/concurrent/hash.rb +59 -0
- data/lib/concurrent/immutable_struct.rb +93 -0
- data/lib/concurrent/ivar.rb +207 -0
- data/lib/concurrent/map.rb +337 -0
- data/lib/concurrent/maybe.rb +229 -0
- data/lib/concurrent/mutable_struct.rb +229 -0
- data/lib/concurrent/mvar.rb +242 -0
- data/lib/concurrent/options.rb +42 -0
- data/lib/concurrent/promise.rb +579 -0
- data/lib/concurrent/promises.rb +2167 -0
- data/lib/concurrent/re_include.rb +58 -0
- data/lib/concurrent/scheduled_task.rb +318 -0
- data/lib/concurrent/set.rb +66 -0
- data/lib/concurrent/settable_struct.rb +129 -0
- data/lib/concurrent/synchronization.rb +30 -0
- data/lib/concurrent/synchronization/abstract_lockable_object.rb +98 -0
- data/lib/concurrent/synchronization/abstract_object.rb +24 -0
- data/lib/concurrent/synchronization/abstract_struct.rb +160 -0
- data/lib/concurrent/synchronization/condition.rb +60 -0
- data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
- data/lib/concurrent/synchronization/jruby_object.rb +45 -0
- data/lib/concurrent/synchronization/lock.rb +36 -0
- data/lib/concurrent/synchronization/lockable_object.rb +74 -0
- data/lib/concurrent/synchronization/mri_object.rb +44 -0
- data/lib/concurrent/synchronization/mutex_lockable_object.rb +76 -0
- data/lib/concurrent/synchronization/object.rb +183 -0
- data/lib/concurrent/synchronization/rbx_lockable_object.rb +65 -0
- data/lib/concurrent/synchronization/rbx_object.rb +49 -0
- data/lib/concurrent/synchronization/truffleruby_object.rb +47 -0
- data/lib/concurrent/synchronization/volatile.rb +36 -0
- data/lib/concurrent/thread_safe/synchronized_delegator.rb +50 -0
- data/lib/concurrent/thread_safe/util.rb +16 -0
- data/lib/concurrent/thread_safe/util/adder.rb +74 -0
- data/lib/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
- data/lib/concurrent/thread_safe/util/data_structures.rb +63 -0
- data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
- data/lib/concurrent/thread_safe/util/striped64.rb +246 -0
- data/lib/concurrent/thread_safe/util/volatile.rb +75 -0
- data/lib/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
- data/lib/concurrent/timer_task.rb +334 -0
- data/lib/concurrent/tuple.rb +86 -0
- data/lib/concurrent/tvar.rb +258 -0
- data/lib/concurrent/utility/at_exit.rb +97 -0
- data/lib/concurrent/utility/engine.rb +56 -0
- data/lib/concurrent/utility/monotonic_time.rb +58 -0
- data/lib/concurrent/utility/native_extension_loader.rb +79 -0
- data/lib/concurrent/utility/native_integer.rb +53 -0
- data/lib/concurrent/utility/processor_counter.rb +158 -0
- data/lib/concurrent/version.rb +3 -0
- metadata +193 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
module Concurrent
|
2
|
+
|
3
|
+
# Methods form module A included to a module B, which is already included into class C,
|
4
|
+
# will not be visible in the C class. If this module is extended to B then A's methods
|
5
|
+
# are correctly made visible to C.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# module A
|
9
|
+
# def a
|
10
|
+
# :a
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# module B1
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# class C1
|
18
|
+
# include B1
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# module B2
|
22
|
+
# extend Concurrent::ReInclude
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# class C2
|
26
|
+
# include B2
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# B1.send :include, A
|
30
|
+
# B2.send :include, A
|
31
|
+
#
|
32
|
+
# C1.new.respond_to? :a # => false
|
33
|
+
# C2.new.respond_to? :a # => true
|
34
|
+
module ReInclude
|
35
|
+
# @!visibility private
|
36
|
+
def included(base)
|
37
|
+
(@re_include_to_bases ||= []) << [:include, base]
|
38
|
+
super(base)
|
39
|
+
end
|
40
|
+
|
41
|
+
# @!visibility private
|
42
|
+
def extended(base)
|
43
|
+
(@re_include_to_bases ||= []) << [:extend, base]
|
44
|
+
super(base)
|
45
|
+
end
|
46
|
+
|
47
|
+
# @!visibility private
|
48
|
+
def include(*modules)
|
49
|
+
result = super(*modules)
|
50
|
+
modules.reverse.each do |module_being_included|
|
51
|
+
(@re_include_to_bases ||= []).each do |method, mod|
|
52
|
+
mod.send method, module_being_included
|
53
|
+
end
|
54
|
+
end
|
55
|
+
result
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,318 @@
|
|
1
|
+
require 'concurrent/constants'
|
2
|
+
require 'concurrent/errors'
|
3
|
+
require 'concurrent/configuration'
|
4
|
+
require 'concurrent/ivar'
|
5
|
+
require 'concurrent/collection/copy_on_notify_observer_set'
|
6
|
+
require 'concurrent/utility/monotonic_time'
|
7
|
+
|
8
|
+
require 'concurrent/options'
|
9
|
+
|
10
|
+
module Concurrent
|
11
|
+
|
12
|
+
# `ScheduledTask` is a close relative of `Concurrent::Future` but with one
|
13
|
+
# important difference: A `Future` is set to execute as soon as possible
|
14
|
+
# whereas a `ScheduledTask` is set to execute after a specified delay. This
|
15
|
+
# implementation is loosely based on Java's
|
16
|
+
# [ScheduledExecutorService](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledExecutorService.html).
|
17
|
+
# It is a more feature-rich variant of {Concurrent.timer}.
|
18
|
+
#
|
19
|
+
# The *intended* schedule time of task execution is set on object construction
|
20
|
+
# with the `delay` argument. The delay is a numeric (floating point or integer)
|
21
|
+
# representing a number of seconds in the future. Any other value or a numeric
|
22
|
+
# equal to or less than zero will result in an exception. The *actual* schedule
|
23
|
+
# time of task execution is set when the `execute` method is called.
|
24
|
+
#
|
25
|
+
# The constructor can also be given zero or more processing options. Currently
|
26
|
+
# the only supported options are those recognized by the
|
27
|
+
# [Dereferenceable](Dereferenceable) module.
|
28
|
+
#
|
29
|
+
# The final constructor argument is a block representing the task to be performed.
|
30
|
+
# If no block is given an `ArgumentError` will be raised.
|
31
|
+
#
|
32
|
+
# **States**
|
33
|
+
#
|
34
|
+
# `ScheduledTask` mixes in the [Obligation](Obligation) module thus giving it
|
35
|
+
# "future" behavior. This includes the expected lifecycle states. `ScheduledTask`
|
36
|
+
# has one additional state, however. While the task (block) is being executed the
|
37
|
+
# state of the object will be `:processing`. This additional state is necessary
|
38
|
+
# because it has implications for task cancellation.
|
39
|
+
#
|
40
|
+
# **Cancellation**
|
41
|
+
#
|
42
|
+
# A `:pending` task can be cancelled using the `#cancel` method. A task in any
|
43
|
+
# other state, including `:processing`, cannot be cancelled. The `#cancel`
|
44
|
+
# method returns a boolean indicating the success of the cancellation attempt.
|
45
|
+
# A cancelled `ScheduledTask` cannot be restarted. It is immutable.
|
46
|
+
#
|
47
|
+
# **Obligation and Observation**
|
48
|
+
#
|
49
|
+
# The result of a `ScheduledTask` can be obtained either synchronously or
|
50
|
+
# asynchronously. `ScheduledTask` mixes in both the [Obligation](Obligation)
|
51
|
+
# module and the
|
52
|
+
# [Observable](http://ruby-doc.org/stdlib-2.0/libdoc/observer/rdoc/Observable.html)
|
53
|
+
# module from the Ruby standard library. With one exception `ScheduledTask`
|
54
|
+
# behaves identically to [Future](Observable) with regard to these modules.
|
55
|
+
#
|
56
|
+
# @!macro copy_options
|
57
|
+
#
|
58
|
+
# @example Basic usage
|
59
|
+
#
|
60
|
+
# require 'concurrent'
|
61
|
+
# require 'thread' # for Queue
|
62
|
+
# require 'open-uri' # for open(uri)
|
63
|
+
#
|
64
|
+
# class Ticker
|
65
|
+
# def get_year_end_closing(symbol, year)
|
66
|
+
# uri = "http://ichart.finance.yahoo.com/table.csv?s=#{symbol}&a=11&b=01&c=#{year}&d=11&e=31&f=#{year}&g=m"
|
67
|
+
# data = open(uri) {|f| f.collect{|line| line.strip } }
|
68
|
+
# data[1].split(',')[4].to_f
|
69
|
+
# end
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# # Future
|
73
|
+
# price = Concurrent::Future.execute{ Ticker.new.get_year_end_closing('TWTR', 2013) }
|
74
|
+
# price.state #=> :pending
|
75
|
+
# sleep(1) # do other stuff
|
76
|
+
# price.value #=> 63.65
|
77
|
+
# price.state #=> :fulfilled
|
78
|
+
#
|
79
|
+
# # ScheduledTask
|
80
|
+
# task = Concurrent::ScheduledTask.execute(2){ Ticker.new.get_year_end_closing('INTC', 2013) }
|
81
|
+
# task.state #=> :pending
|
82
|
+
# sleep(3) # do other stuff
|
83
|
+
# task.value #=> 25.96
|
84
|
+
#
|
85
|
+
# @example Successful task execution
|
86
|
+
#
|
87
|
+
# task = Concurrent::ScheduledTask.new(2){ 'What does the fox say?' }
|
88
|
+
# task.state #=> :unscheduled
|
89
|
+
# task.execute
|
90
|
+
# task.state #=> pending
|
91
|
+
#
|
92
|
+
# # wait for it...
|
93
|
+
# sleep(3)
|
94
|
+
#
|
95
|
+
# task.unscheduled? #=> false
|
96
|
+
# task.pending? #=> false
|
97
|
+
# task.fulfilled? #=> true
|
98
|
+
# task.rejected? #=> false
|
99
|
+
# task.value #=> 'What does the fox say?'
|
100
|
+
#
|
101
|
+
# @example One line creation and execution
|
102
|
+
#
|
103
|
+
# task = Concurrent::ScheduledTask.new(2){ 'What does the fox say?' }.execute
|
104
|
+
# task.state #=> pending
|
105
|
+
#
|
106
|
+
# task = Concurrent::ScheduledTask.execute(2){ 'What do you get when you multiply 6 by 9?' }
|
107
|
+
# task.state #=> pending
|
108
|
+
#
|
109
|
+
# @example Failed task execution
|
110
|
+
#
|
111
|
+
# task = Concurrent::ScheduledTask.execute(2){ raise StandardError.new('Call me maybe?') }
|
112
|
+
# task.pending? #=> true
|
113
|
+
#
|
114
|
+
# # wait for it...
|
115
|
+
# sleep(3)
|
116
|
+
#
|
117
|
+
# task.unscheduled? #=> false
|
118
|
+
# task.pending? #=> false
|
119
|
+
# task.fulfilled? #=> false
|
120
|
+
# task.rejected? #=> true
|
121
|
+
# task.value #=> nil
|
122
|
+
# task.reason #=> #<StandardError: Call me maybe?>
|
123
|
+
#
|
124
|
+
# @example Task execution with observation
|
125
|
+
#
|
126
|
+
# observer = Class.new{
|
127
|
+
# def update(time, value, reason)
|
128
|
+
# puts "The task completed at #{time} with value '#{value}'"
|
129
|
+
# end
|
130
|
+
# }.new
|
131
|
+
#
|
132
|
+
# task = Concurrent::ScheduledTask.new(2){ 'What does the fox say?' }
|
133
|
+
# task.add_observer(observer)
|
134
|
+
# task.execute
|
135
|
+
# task.pending? #=> true
|
136
|
+
#
|
137
|
+
# # wait for it...
|
138
|
+
# sleep(3)
|
139
|
+
#
|
140
|
+
# #>> The task completed at 2013-11-07 12:26:09 -0500 with value 'What does the fox say?'
|
141
|
+
#
|
142
|
+
# @!macro monotonic_clock_warning
|
143
|
+
#
|
144
|
+
# @see Concurrent.timer
|
145
|
+
class ScheduledTask < IVar
|
146
|
+
include Comparable
|
147
|
+
|
148
|
+
# The executor on which to execute the task.
|
149
|
+
# @!visibility private
|
150
|
+
attr_reader :executor
|
151
|
+
|
152
|
+
# Schedule a task for execution at a specified future time.
|
153
|
+
#
|
154
|
+
# @param [Float] delay the number of seconds to wait for before executing the task
|
155
|
+
#
|
156
|
+
# @yield the task to be performed
|
157
|
+
#
|
158
|
+
# @!macro executor_and_deref_options
|
159
|
+
#
|
160
|
+
# @option opts [object, Array] :args zero or more arguments to be passed the task
|
161
|
+
# block on execution
|
162
|
+
#
|
163
|
+
# @raise [ArgumentError] When no block is given
|
164
|
+
# @raise [ArgumentError] When given a time that is in the past
|
165
|
+
def initialize(delay, opts = {}, &task)
|
166
|
+
raise ArgumentError.new('no block given') unless block_given?
|
167
|
+
raise ArgumentError.new('seconds must be greater than zero') if delay.to_f < 0.0
|
168
|
+
|
169
|
+
super(NULL, opts, &nil)
|
170
|
+
|
171
|
+
synchronize do
|
172
|
+
ns_set_state(:unscheduled)
|
173
|
+
@parent = opts.fetch(:timer_set, Concurrent.global_timer_set)
|
174
|
+
@args = get_arguments_from(opts)
|
175
|
+
@delay = delay.to_f
|
176
|
+
@task = task
|
177
|
+
@time = nil
|
178
|
+
@executor = Options.executor_from_options(opts) || Concurrent.global_io_executor
|
179
|
+
self.observers = Collection::CopyOnNotifyObserverSet.new
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# The `delay` value given at instanciation.
|
184
|
+
#
|
185
|
+
# @return [Float] the initial delay.
|
186
|
+
def initial_delay
|
187
|
+
synchronize { @delay }
|
188
|
+
end
|
189
|
+
|
190
|
+
# The monotonic time at which the the task is scheduled to be executed.
|
191
|
+
#
|
192
|
+
# @return [Float] the schedule time or nil if `unscheduled`
|
193
|
+
def schedule_time
|
194
|
+
synchronize { @time }
|
195
|
+
end
|
196
|
+
|
197
|
+
# Comparator which orders by schedule time.
|
198
|
+
#
|
199
|
+
# @!visibility private
|
200
|
+
def <=>(other)
|
201
|
+
schedule_time <=> other.schedule_time
|
202
|
+
end
|
203
|
+
|
204
|
+
# Has the task been cancelled?
|
205
|
+
#
|
206
|
+
# @return [Boolean] true if the task is in the given state else false
|
207
|
+
def cancelled?
|
208
|
+
synchronize { ns_check_state?(:cancelled) }
|
209
|
+
end
|
210
|
+
|
211
|
+
# In the task execution in progress?
|
212
|
+
#
|
213
|
+
# @return [Boolean] true if the task is in the given state else false
|
214
|
+
def processing?
|
215
|
+
synchronize { ns_check_state?(:processing) }
|
216
|
+
end
|
217
|
+
|
218
|
+
# Cancel this task and prevent it from executing. A task can only be
|
219
|
+
# cancelled if it is pending or unscheduled.
|
220
|
+
#
|
221
|
+
# @return [Boolean] true if successfully cancelled else false
|
222
|
+
def cancel
|
223
|
+
if compare_and_set_state(:cancelled, :pending, :unscheduled)
|
224
|
+
complete(false, nil, CancelledOperationError.new)
|
225
|
+
# To avoid deadlocks this call must occur outside of #synchronize
|
226
|
+
# Changing the state above should prevent redundant calls
|
227
|
+
@parent.send(:remove_task, self)
|
228
|
+
else
|
229
|
+
false
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Reschedule the task using the original delay and the current time.
|
234
|
+
# A task can only be reset while it is `:pending`.
|
235
|
+
#
|
236
|
+
# @return [Boolean] true if successfully rescheduled else false
|
237
|
+
def reset
|
238
|
+
synchronize{ ns_reschedule(@delay) }
|
239
|
+
end
|
240
|
+
|
241
|
+
# Reschedule the task using the given delay and the current time.
|
242
|
+
# A task can only be reset while it is `:pending`.
|
243
|
+
#
|
244
|
+
# @param [Float] delay the number of seconds to wait for before executing the task
|
245
|
+
#
|
246
|
+
# @return [Boolean] true if successfully rescheduled else false
|
247
|
+
#
|
248
|
+
# @raise [ArgumentError] When given a time that is in the past
|
249
|
+
def reschedule(delay)
|
250
|
+
delay = delay.to_f
|
251
|
+
raise ArgumentError.new('seconds must be greater than zero') if delay < 0.0
|
252
|
+
synchronize{ ns_reschedule(delay) }
|
253
|
+
end
|
254
|
+
|
255
|
+
# Execute an `:unscheduled` `ScheduledTask`. Immediately sets the state to `:pending`
|
256
|
+
# and starts counting down toward execution. Does nothing if the `ScheduledTask` is
|
257
|
+
# in any state other than `:unscheduled`.
|
258
|
+
#
|
259
|
+
# @return [ScheduledTask] a reference to `self`
|
260
|
+
def execute
|
261
|
+
if compare_and_set_state(:pending, :unscheduled)
|
262
|
+
synchronize{ ns_schedule(@delay) }
|
263
|
+
end
|
264
|
+
self
|
265
|
+
end
|
266
|
+
|
267
|
+
# Create a new `ScheduledTask` object with the given block, execute it, and return the
|
268
|
+
# `:pending` object.
|
269
|
+
#
|
270
|
+
# @param [Float] delay the number of seconds to wait for before executing the task
|
271
|
+
#
|
272
|
+
# @!macro executor_and_deref_options
|
273
|
+
#
|
274
|
+
# @return [ScheduledTask] the newly created `ScheduledTask` in the `:pending` state
|
275
|
+
#
|
276
|
+
# @raise [ArgumentError] if no block is given
|
277
|
+
def self.execute(delay, opts = {}, &task)
|
278
|
+
new(delay, opts, &task).execute
|
279
|
+
end
|
280
|
+
|
281
|
+
# Execute the task.
|
282
|
+
#
|
283
|
+
# @!visibility private
|
284
|
+
def process_task
|
285
|
+
safe_execute(@task, @args)
|
286
|
+
end
|
287
|
+
|
288
|
+
protected :set, :try_set, :fail, :complete
|
289
|
+
|
290
|
+
protected
|
291
|
+
|
292
|
+
# Schedule the task using the given delay and the current time.
|
293
|
+
#
|
294
|
+
# @param [Float] delay the number of seconds to wait for before executing the task
|
295
|
+
#
|
296
|
+
# @return [Boolean] true if successfully rescheduled else false
|
297
|
+
#
|
298
|
+
# @!visibility private
|
299
|
+
def ns_schedule(delay)
|
300
|
+
@delay = delay
|
301
|
+
@time = Concurrent.monotonic_time + @delay
|
302
|
+
@parent.send(:post_task, self)
|
303
|
+
end
|
304
|
+
|
305
|
+
# Reschedule the task using the given delay and the current time.
|
306
|
+
# A task can only be reset while it is `:pending`.
|
307
|
+
#
|
308
|
+
# @param [Float] delay the number of seconds to wait for before executing the task
|
309
|
+
#
|
310
|
+
# @return [Boolean] true if successfully rescheduled else false
|
311
|
+
#
|
312
|
+
# @!visibility private
|
313
|
+
def ns_reschedule(delay)
|
314
|
+
return false unless ns_check_state?(:pending)
|
315
|
+
@parent.send(:remove_task, self) && ns_schedule(delay)
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'concurrent/utility/engine'
|
2
|
+
require 'concurrent/thread_safe/util'
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
module Concurrent
|
6
|
+
|
7
|
+
# @!macro concurrent_set
|
8
|
+
#
|
9
|
+
# A thread-safe subclass of Set. This version locks against the object
|
10
|
+
# itself for every method call, ensuring only one thread can be reading
|
11
|
+
# or writing at a time. This includes iteration methods like `#each`.
|
12
|
+
#
|
13
|
+
# @note `a += b` is **not** a **thread-safe** operation on
|
14
|
+
# `Concurrent::Set`. It reads Set `a`, then it creates new `Concurrent::Set`
|
15
|
+
# which is union of `a` and `b`, then it writes the union to `a`.
|
16
|
+
# The read and write are independent operations they do not form a single atomic
|
17
|
+
# operation therefore when two `+=` operations are executed concurrently updates
|
18
|
+
# may be lost. Use `#merge` instead.
|
19
|
+
#
|
20
|
+
# @see http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html Ruby standard library `Set`
|
21
|
+
|
22
|
+
|
23
|
+
# @!macro internal_implementation_note
|
24
|
+
SetImplementation = case
|
25
|
+
when Concurrent.on_cruby?
|
26
|
+
# Because MRI never runs code in parallel, the existing
|
27
|
+
# non-thread-safe structures should usually work fine.
|
28
|
+
::Set
|
29
|
+
|
30
|
+
when Concurrent.on_jruby?
|
31
|
+
require 'jruby/synchronized'
|
32
|
+
|
33
|
+
class JRubySet < ::Set
|
34
|
+
include JRuby::Synchronized
|
35
|
+
end
|
36
|
+
JRubySet
|
37
|
+
|
38
|
+
when Concurrent.on_rbx?
|
39
|
+
require 'monitor'
|
40
|
+
require 'concurrent/thread_safe/util/data_structures'
|
41
|
+
|
42
|
+
class RbxSet < ::Set
|
43
|
+
end
|
44
|
+
ThreadSafe::Util.make_synchronized_on_rbx Concurrent::RbxSet
|
45
|
+
RbxSet
|
46
|
+
|
47
|
+
when Concurrent.on_truffleruby?
|
48
|
+
require 'concurrent/thread_safe/util/data_structures'
|
49
|
+
|
50
|
+
class TruffleRubySet < ::Set
|
51
|
+
end
|
52
|
+
|
53
|
+
ThreadSafe::Util.make_synchronized_on_truffleruby Concurrent::TruffleRubySet
|
54
|
+
TruffleRubySet
|
55
|
+
|
56
|
+
else
|
57
|
+
warn 'Possibly unsupported Ruby implementation'
|
58
|
+
::Set
|
59
|
+
end
|
60
|
+
private_constant :SetImplementation
|
61
|
+
|
62
|
+
# @!macro concurrent_set
|
63
|
+
class Set < SetImplementation
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
@@ -0,0 +1,129 @@
|
|
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
|
+
length = synchronize { @values.length }
|
78
|
+
if member >= length
|
79
|
+
raise IndexError.new("offset #{member} too large for struct(size:#{length})")
|
80
|
+
end
|
81
|
+
synchronize do
|
82
|
+
unless @values[member].nil?
|
83
|
+
raise Concurrent::ImmutabilityError.new('struct member has already been set')
|
84
|
+
end
|
85
|
+
@values[member] = value
|
86
|
+
end
|
87
|
+
else
|
88
|
+
send("#{member}=", value)
|
89
|
+
end
|
90
|
+
rescue NoMethodError
|
91
|
+
raise NameError.new("no member '#{member}' in struct")
|
92
|
+
end
|
93
|
+
|
94
|
+
# @!macro struct_new
|
95
|
+
def self.new(*args, &block)
|
96
|
+
clazz_name = nil
|
97
|
+
if args.length == 0
|
98
|
+
raise ArgumentError.new('wrong number of arguments (0 for 1+)')
|
99
|
+
elsif args.length > 0 && args.first.is_a?(String)
|
100
|
+
clazz_name = args.shift
|
101
|
+
end
|
102
|
+
FACTORY.define_struct(clazz_name, args, &block)
|
103
|
+
end
|
104
|
+
|
105
|
+
FACTORY = Class.new(Synchronization::LockableObject) do
|
106
|
+
def define_struct(name, members, &block)
|
107
|
+
synchronize do
|
108
|
+
clazz = Synchronization::AbstractStruct.define_struct_class(SettableStruct, Synchronization::LockableObject, name, members, &block)
|
109
|
+
members.each_with_index do |member, index|
|
110
|
+
clazz.send :remove_method, member if clazz.instance_methods.include? member
|
111
|
+
clazz.send(:define_method, member) do
|
112
|
+
synchronize { @values[index] }
|
113
|
+
end
|
114
|
+
clazz.send(:define_method, "#{member}=") do |value|
|
115
|
+
synchronize do
|
116
|
+
unless @values[index].nil?
|
117
|
+
raise Concurrent::ImmutabilityError.new('struct member has already been set')
|
118
|
+
end
|
119
|
+
@values[index] = value
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
clazz
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end.new
|
127
|
+
private_constant :FACTORY
|
128
|
+
end
|
129
|
+
end
|