concurrent-ruby 1.1.10 → 1.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +76 -3
- data/Gemfile +6 -7
- data/README.md +28 -23
- data/Rakefile +90 -76
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +3 -38
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +1 -1
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +1 -1
- data/lib/concurrent-ruby/concurrent/agent.rb +4 -3
- data/lib/concurrent-ruby/concurrent/array.rb +3 -13
- data/lib/concurrent-ruby/concurrent/async.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atom.rb +2 -2
- data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +5 -4
- data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +5 -4
- data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +3 -0
- data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +90 -153
- data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/event.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb +109 -0
- data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +1 -0
- data/lib/concurrent-ruby/concurrent/atomic/locals.rb +189 -0
- data/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb +29 -0
- data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +11 -5
- data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +11 -5
- data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +17 -3
- data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +13 -4
- data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +6 -9
- data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +96 -89
- data/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb +37 -0
- data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +16 -6
- data/lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb +9 -1
- data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +1 -1
- data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +1 -1
- data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +2 -0
- data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +2 -2
- data/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb +16 -8
- data/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb +23 -20
- data/lib/concurrent-ruby/concurrent/collection/ruby_timeout_queue.rb +55 -0
- data/lib/concurrent-ruby/concurrent/collection/timeout_queue.rb +18 -0
- data/lib/concurrent-ruby/concurrent/concern/logging.rb +95 -6
- data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent-ruby/concurrent/configuration.rb +4 -87
- data/lib/concurrent-ruby/concurrent/delay.rb +3 -3
- data/lib/concurrent-ruby/concurrent/errors.rb +5 -0
- data/lib/concurrent-ruby/concurrent/exchanger.rb +1 -0
- data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +1 -1
- data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +6 -4
- data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +7 -9
- data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +7 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb +2 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +61 -31
- data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +1 -1
- data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +1 -1
- data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +4 -1
- data/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb +1 -1
- data/lib/concurrent-ruby/concurrent/executor/timer_set.rb +10 -3
- data/lib/concurrent-ruby/concurrent/executors.rb +0 -1
- data/lib/concurrent-ruby/concurrent/hash.rb +5 -12
- data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/ivar.rb +2 -1
- data/lib/concurrent-ruby/concurrent/map.rb +43 -39
- data/lib/concurrent-ruby/concurrent/maybe.rb +1 -1
- data/lib/concurrent-ruby/concurrent/mutable_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/mvar.rb +5 -5
- data/lib/concurrent-ruby/concurrent/promise.rb +3 -3
- data/lib/concurrent-ruby/concurrent/promises.rb +40 -29
- data/lib/concurrent-ruby/concurrent/re_include.rb +2 -0
- data/lib/concurrent-ruby/concurrent/scheduled_task.rb +2 -2
- data/lib/concurrent-ruby/concurrent/set.rb +0 -10
- data/lib/concurrent-ruby/concurrent/settable_struct.rb +2 -2
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +5 -1
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +1 -3
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +2 -0
- data/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb +29 -0
- data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +3 -1
- data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +2 -0
- data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +5 -2
- data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +6 -5
- data/lib/concurrent-ruby/concurrent/synchronization/object.rb +13 -45
- data/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb +36 -0
- data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +77 -12
- data/lib/concurrent-ruby/concurrent/synchronization.rb +1 -18
- data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +36 -39
- data/lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb +1 -1
- data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +1 -37
- data/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb +1 -1
- data/lib/concurrent-ruby/concurrent/timer_task.rb +65 -10
- data/lib/concurrent-ruby/concurrent/tuple.rb +1 -5
- data/lib/concurrent-ruby/concurrent/tvar.rb +2 -1
- data/lib/concurrent-ruby/concurrent/utility/engine.rb +5 -16
- data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +3 -74
- data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +8 -10
- data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +1 -0
- data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +148 -58
- data/lib/concurrent-ruby/concurrent/version.rb +1 -1
- metadata +14 -19
- data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +0 -66
- data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +0 -37
- data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +0 -181
- data/lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb +0 -927
- data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +0 -45
- data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +0 -44
- data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +0 -71
- data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +0 -49
- data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +0 -47
- data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +0 -118
|
@@ -2,49 +2,46 @@ require 'delegate'
|
|
|
2
2
|
require 'monitor'
|
|
3
3
|
|
|
4
4
|
module Concurrent
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
Thread.abort_on_exception = true
|
|
27
|
-
end
|
|
5
|
+
# This class provides a trivial way to synchronize all calls to a given object
|
|
6
|
+
# by wrapping it with a `Delegator` that performs `Monitor#enter/exit` calls
|
|
7
|
+
# around the delegated `#send`. Example:
|
|
8
|
+
#
|
|
9
|
+
# array = [] # not thread-safe on many impls
|
|
10
|
+
# array = SynchronizedDelegator.new([]) # thread-safe
|
|
11
|
+
#
|
|
12
|
+
# A simple `Monitor` provides a very coarse-grained way to synchronize a given
|
|
13
|
+
# object, in that it will cause synchronization for methods that have no need
|
|
14
|
+
# for it, but this is a trivial way to get thread-safety where none may exist
|
|
15
|
+
# currently on some implementations.
|
|
16
|
+
#
|
|
17
|
+
# This class is currently being considered for inclusion into stdlib, via
|
|
18
|
+
# https://bugs.ruby-lang.org/issues/8556
|
|
19
|
+
#
|
|
20
|
+
# @!visibility private
|
|
21
|
+
class SynchronizedDelegator < SimpleDelegator
|
|
22
|
+
def setup
|
|
23
|
+
@old_abort = Thread.abort_on_exception
|
|
24
|
+
Thread.abort_on_exception = true
|
|
25
|
+
end
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
def teardown
|
|
28
|
+
Thread.abort_on_exception = @old_abort
|
|
29
|
+
end
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
def initialize(obj)
|
|
32
|
+
__setobj__(obj)
|
|
33
|
+
@monitor = Monitor.new
|
|
34
|
+
end
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
end
|
|
36
|
+
def method_missing(method, *args, &block)
|
|
37
|
+
monitor = @monitor
|
|
38
|
+
begin
|
|
39
|
+
monitor.enter
|
|
40
|
+
super
|
|
41
|
+
ensure
|
|
42
|
+
monitor.exit
|
|
46
43
|
end
|
|
47
|
-
|
|
48
44
|
end
|
|
45
|
+
|
|
49
46
|
end
|
|
50
47
|
end
|
|
@@ -9,7 +9,7 @@ module Concurrent
|
|
|
9
9
|
# @!visibility private
|
|
10
10
|
module Util
|
|
11
11
|
|
|
12
|
-
# A Ruby port of the Doug Lea's jsr166e.
|
|
12
|
+
# A Ruby port of the Doug Lea's jsr166e.LongAdder class version 1.8
|
|
13
13
|
# available in public domain.
|
|
14
14
|
#
|
|
15
15
|
# Original source code available here:
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'concurrent/thread_safe/util'
|
|
2
|
+
require 'concurrent/utility/engine'
|
|
2
3
|
|
|
3
4
|
# Shim for TruffleRuby.synchronized
|
|
4
5
|
if Concurrent.on_truffleruby? && !TruffleRuby.respond_to?(:synchronized)
|
|
@@ -37,43 +38,6 @@ module Concurrent
|
|
|
37
38
|
end
|
|
38
39
|
end
|
|
39
40
|
|
|
40
|
-
def self.make_synchronized_on_rbx(klass)
|
|
41
|
-
klass.class_eval do
|
|
42
|
-
private
|
|
43
|
-
|
|
44
|
-
def _mon_initialize
|
|
45
|
-
@_monitor ||= Monitor.new # avoid double initialisation
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def self.new(*args)
|
|
49
|
-
obj = super(*args)
|
|
50
|
-
obj.send(:_mon_initialize)
|
|
51
|
-
obj
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
klass.superclass.instance_methods(false).each do |method|
|
|
56
|
-
case method
|
|
57
|
-
when :new_range, :new_reserved
|
|
58
|
-
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
59
|
-
def #{method}(*args)
|
|
60
|
-
obj = super
|
|
61
|
-
obj.send(:_mon_initialize)
|
|
62
|
-
obj
|
|
63
|
-
end
|
|
64
|
-
RUBY
|
|
65
|
-
else
|
|
66
|
-
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
67
|
-
def #{method}(*args)
|
|
68
|
-
monitor = @_monitor
|
|
69
|
-
monitor or raise("BUG: Internal monitor was not properly initialized. Please report this to the concurrent-ruby developers.")
|
|
70
|
-
monitor.synchronize { super }
|
|
71
|
-
end
|
|
72
|
-
RUBY
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
41
|
def self.make_synchronized_on_truffleruby(klass)
|
|
78
42
|
klass.superclass.instance_methods(false).each do |method|
|
|
79
43
|
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
@@ -15,7 +15,7 @@ module Concurrent
|
|
|
15
15
|
# Usage:
|
|
16
16
|
# x = XorShiftRandom.get # uses Kernel.rand to generate an initial seed
|
|
17
17
|
# while true
|
|
18
|
-
# if (x = XorShiftRandom.xorshift).odd? # thread-
|
|
18
|
+
# if (x = XorShiftRandom.xorshift).odd? # thread-locally generate a next random number
|
|
19
19
|
# do_something_at_random
|
|
20
20
|
# end
|
|
21
21
|
# end
|
|
@@ -2,6 +2,7 @@ require 'concurrent/collection/copy_on_notify_observer_set'
|
|
|
2
2
|
require 'concurrent/concern/dereferenceable'
|
|
3
3
|
require 'concurrent/concern/observable'
|
|
4
4
|
require 'concurrent/atomic/atomic_boolean'
|
|
5
|
+
require 'concurrent/atomic/atomic_fixnum'
|
|
5
6
|
require 'concurrent/executor/executor_service'
|
|
6
7
|
require 'concurrent/executor/ruby_executor_service'
|
|
7
8
|
require 'concurrent/executor/safe_task_executor'
|
|
@@ -32,6 +33,17 @@ module Concurrent
|
|
|
32
33
|
# be tested separately then passed to the `TimerTask` for scheduling and
|
|
33
34
|
# running.
|
|
34
35
|
#
|
|
36
|
+
# A `TimerTask` supports two different types of interval calculations.
|
|
37
|
+
# A fixed delay will always wait the same amount of time between the
|
|
38
|
+
# completion of one task and the start of the next. A fixed rate will
|
|
39
|
+
# attempt to maintain a constant rate of execution regardless of the
|
|
40
|
+
# duration of the task. For example, if a fixed rate task is scheduled
|
|
41
|
+
# to run every 60 seconds but the task itself takes 10 seconds to
|
|
42
|
+
# complete, the next task will be scheduled to run 50 seconds after
|
|
43
|
+
# the start of the previous task. If the task takes 70 seconds to
|
|
44
|
+
# complete, the next task will be start immediately after the previous
|
|
45
|
+
# task completes. Tasks will not be executed concurrently.
|
|
46
|
+
#
|
|
35
47
|
# In some cases it may be necessary for a `TimerTask` to affect its own
|
|
36
48
|
# execution cycle. To facilitate this, a reference to the TimerTask instance
|
|
37
49
|
# is passed as an argument to the provided block every time the task is
|
|
@@ -74,6 +86,12 @@ module Concurrent
|
|
|
74
86
|
#
|
|
75
87
|
# #=> 'Boom!'
|
|
76
88
|
#
|
|
89
|
+
# @example Configuring `:interval_type` with either :fixed_delay or :fixed_rate, default is :fixed_delay
|
|
90
|
+
# task = Concurrent::TimerTask.new(execution_interval: 5, interval_type: :fixed_rate) do
|
|
91
|
+
# puts 'Boom!'
|
|
92
|
+
# end
|
|
93
|
+
# task.interval_type #=> :fixed_rate
|
|
94
|
+
#
|
|
77
95
|
# @example Last `#value` and `Dereferenceable` mixin
|
|
78
96
|
# task = Concurrent::TimerTask.new(
|
|
79
97
|
# dup_on_deref: true,
|
|
@@ -87,7 +105,7 @@ module Concurrent
|
|
|
87
105
|
#
|
|
88
106
|
# @example Controlling execution from within the block
|
|
89
107
|
# timer_task = Concurrent::TimerTask.new(execution_interval: 1) do |task|
|
|
90
|
-
# task.execution_interval.times{ print 'Boom! ' }
|
|
108
|
+
# task.execution_interval.to_i.times{ print 'Boom! ' }
|
|
91
109
|
# print "\n"
|
|
92
110
|
# task.execution_interval += 1
|
|
93
111
|
# if task.execution_interval > 5
|
|
@@ -96,7 +114,7 @@ module Concurrent
|
|
|
96
114
|
# end
|
|
97
115
|
# end
|
|
98
116
|
#
|
|
99
|
-
# timer_task.execute
|
|
117
|
+
# timer_task.execute
|
|
100
118
|
# #=> Boom!
|
|
101
119
|
# #=> Boom! Boom!
|
|
102
120
|
# #=> Boom! Boom! Boom!
|
|
@@ -152,18 +170,30 @@ module Concurrent
|
|
|
152
170
|
# Default `:execution_interval` in seconds.
|
|
153
171
|
EXECUTION_INTERVAL = 60
|
|
154
172
|
|
|
155
|
-
#
|
|
156
|
-
|
|
173
|
+
# Maintain the interval between the end of one execution and the start of the next execution.
|
|
174
|
+
FIXED_DELAY = :fixed_delay
|
|
175
|
+
|
|
176
|
+
# Maintain the interval between the start of one execution and the start of the next.
|
|
177
|
+
# If execution time exceeds the interval, the next execution will start immediately
|
|
178
|
+
# after the previous execution finishes. Executions will not run concurrently.
|
|
179
|
+
FIXED_RATE = :fixed_rate
|
|
180
|
+
|
|
181
|
+
# Default `:interval_type`
|
|
182
|
+
DEFAULT_INTERVAL_TYPE = FIXED_DELAY
|
|
157
183
|
|
|
158
184
|
# Create a new TimerTask with the given task and configuration.
|
|
159
185
|
#
|
|
160
186
|
# @!macro timer_task_initialize
|
|
161
187
|
# @param [Hash] opts the options defining task execution.
|
|
162
|
-
# @option opts [
|
|
188
|
+
# @option opts [Float] :execution_interval number of seconds between
|
|
163
189
|
# task executions (default: EXECUTION_INTERVAL)
|
|
164
190
|
# @option opts [Boolean] :run_now Whether to run the task immediately
|
|
165
191
|
# upon instantiation or to wait until the first # execution_interval
|
|
166
192
|
# has passed (default: false)
|
|
193
|
+
# @options opts [Symbol] :interval_type method to calculate the interval
|
|
194
|
+
# between executions, can be either :fixed_rate or :fixed_delay.
|
|
195
|
+
# (default: :fixed_delay)
|
|
196
|
+
# @option opts [Executor] executor, default is `global_io_executor`
|
|
167
197
|
#
|
|
168
198
|
# @!macro deref_options
|
|
169
199
|
#
|
|
@@ -207,6 +237,7 @@ module Concurrent
|
|
|
207
237
|
synchronize do
|
|
208
238
|
if @running.false?
|
|
209
239
|
@running.make_true
|
|
240
|
+
@age.increment
|
|
210
241
|
schedule_next_task(@run_now ? 0 : @execution_interval)
|
|
211
242
|
end
|
|
212
243
|
end
|
|
@@ -242,6 +273,10 @@ module Concurrent
|
|
|
242
273
|
end
|
|
243
274
|
end
|
|
244
275
|
|
|
276
|
+
# @!attribute [r] interval_type
|
|
277
|
+
# @return [Symbol] method to calculate the interval between executions
|
|
278
|
+
attr_reader :interval_type
|
|
279
|
+
|
|
245
280
|
# @!attribute [rw] timeout_interval
|
|
246
281
|
# @return [Fixnum] Number of seconds the task can run before it is
|
|
247
282
|
# considered to have failed.
|
|
@@ -264,12 +299,19 @@ module Concurrent
|
|
|
264
299
|
set_deref_options(opts)
|
|
265
300
|
|
|
266
301
|
self.execution_interval = opts[:execution] || opts[:execution_interval] || EXECUTION_INTERVAL
|
|
302
|
+
if opts[:interval_type] && ![FIXED_DELAY, FIXED_RATE].include?(opts[:interval_type])
|
|
303
|
+
raise ArgumentError.new('interval_type must be either :fixed_delay or :fixed_rate')
|
|
304
|
+
end
|
|
267
305
|
if opts[:timeout] || opts[:timeout_interval]
|
|
268
306
|
warn 'TimeTask timeouts are now ignored as these were not able to be implemented correctly'
|
|
269
307
|
end
|
|
308
|
+
|
|
270
309
|
@run_now = opts[:now] || opts[:run_now]
|
|
271
|
-
@
|
|
310
|
+
@interval_type = opts[:interval_type] || DEFAULT_INTERVAL_TYPE
|
|
311
|
+
@task = Concurrent::SafeTaskExecutor.new(task)
|
|
312
|
+
@executor = opts[:executor] || Concurrent.global_io_executor
|
|
272
313
|
@running = Concurrent::AtomicBoolean.new(false)
|
|
314
|
+
@age = Concurrent::AtomicFixnum.new(0)
|
|
273
315
|
@value = nil
|
|
274
316
|
|
|
275
317
|
self.observers = Collection::CopyOnNotifyObserverSet.new
|
|
@@ -289,17 +331,20 @@ module Concurrent
|
|
|
289
331
|
|
|
290
332
|
# @!visibility private
|
|
291
333
|
def schedule_next_task(interval = execution_interval)
|
|
292
|
-
ScheduledTask.execute(interval, args: [Concurrent::Event.new], &method(:execute_task))
|
|
334
|
+
ScheduledTask.execute(interval, executor: @executor, args: [Concurrent::Event.new, @age.value], &method(:execute_task))
|
|
293
335
|
nil
|
|
294
336
|
end
|
|
295
337
|
|
|
296
338
|
# @!visibility private
|
|
297
|
-
def execute_task(completion)
|
|
339
|
+
def execute_task(completion, age_when_scheduled)
|
|
298
340
|
return nil unless @running.true?
|
|
299
|
-
|
|
341
|
+
return nil unless @age.value == age_when_scheduled
|
|
342
|
+
|
|
343
|
+
start_time = Concurrent.monotonic_time
|
|
344
|
+
_success, value, reason = @task.execute(self)
|
|
300
345
|
if completion.try?
|
|
301
346
|
self.value = value
|
|
302
|
-
schedule_next_task
|
|
347
|
+
schedule_next_task(calculate_next_interval(start_time))
|
|
303
348
|
time = Time.now
|
|
304
349
|
observers.notify_observers do
|
|
305
350
|
[time, self.value, reason]
|
|
@@ -307,5 +352,15 @@ module Concurrent
|
|
|
307
352
|
end
|
|
308
353
|
nil
|
|
309
354
|
end
|
|
355
|
+
|
|
356
|
+
# @!visibility private
|
|
357
|
+
def calculate_next_interval(start_time)
|
|
358
|
+
if @interval_type == FIXED_RATE
|
|
359
|
+
run_time = Concurrent.monotonic_time - start_time
|
|
360
|
+
[execution_interval - run_time, 0].max
|
|
361
|
+
else # FIXED_DELAY
|
|
362
|
+
execution_interval
|
|
363
|
+
end
|
|
364
|
+
end
|
|
310
365
|
end
|
|
311
366
|
end
|
|
@@ -23,16 +23,12 @@ module Concurrent
|
|
|
23
23
|
# The (fixed) size of the tuple.
|
|
24
24
|
attr_reader :size
|
|
25
25
|
|
|
26
|
-
# @!visibility private
|
|
27
|
-
Tuple = defined?(Rubinius::Tuple) ? Rubinius::Tuple : ::Array
|
|
28
|
-
private_constant :Tuple
|
|
29
|
-
|
|
30
26
|
# Create a new tuple of the given size.
|
|
31
27
|
#
|
|
32
28
|
# @param [Integer] size the number of elements in the tuple
|
|
33
29
|
def initialize(size)
|
|
34
30
|
@size = size
|
|
35
|
-
@tuple = tuple =
|
|
31
|
+
@tuple = tuple = ::Array.new(size)
|
|
36
32
|
i = 0
|
|
37
33
|
while i < size
|
|
38
34
|
tuple[i] = Concurrent::AtomicReference.new
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require 'set'
|
|
2
|
-
require 'concurrent/synchronization'
|
|
2
|
+
require 'concurrent/synchronization/object'
|
|
3
3
|
|
|
4
4
|
module Concurrent
|
|
5
5
|
|
|
@@ -149,6 +149,7 @@ module Concurrent
|
|
|
149
149
|
|
|
150
150
|
private
|
|
151
151
|
|
|
152
|
+
# @!visibility private
|
|
152
153
|
class Transaction
|
|
153
154
|
|
|
154
155
|
ABORTED = ::Object.new
|
|
@@ -1,26 +1,19 @@
|
|
|
1
1
|
module Concurrent
|
|
2
|
+
# @!visibility private
|
|
2
3
|
module Utility
|
|
3
4
|
|
|
4
5
|
# @!visibility private
|
|
5
6
|
module EngineDetector
|
|
6
|
-
def on_jruby?
|
|
7
|
-
ruby_engine == 'jruby'
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def on_jruby_9000?
|
|
11
|
-
on_jruby? && ruby_version(JRUBY_VERSION, :>=, 9, 0, 0)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
7
|
def on_cruby?
|
|
15
|
-
|
|
8
|
+
RUBY_ENGINE == 'ruby'
|
|
16
9
|
end
|
|
17
10
|
|
|
18
|
-
def
|
|
19
|
-
|
|
11
|
+
def on_jruby?
|
|
12
|
+
RUBY_ENGINE == 'jruby'
|
|
20
13
|
end
|
|
21
14
|
|
|
22
15
|
def on_truffleruby?
|
|
23
|
-
|
|
16
|
+
RUBY_ENGINE == 'truffleruby'
|
|
24
17
|
end
|
|
25
18
|
|
|
26
19
|
def on_windows?
|
|
@@ -35,10 +28,6 @@ module Concurrent
|
|
|
35
28
|
!(RbConfig::CONFIG['host_os'] =~ /linux/).nil?
|
|
36
29
|
end
|
|
37
30
|
|
|
38
|
-
def ruby_engine
|
|
39
|
-
defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
|
|
40
|
-
end
|
|
41
|
-
|
|
42
31
|
def ruby_version(version = RUBY_VERSION, comparison, major, minor, patch)
|
|
43
32
|
result = (version.split('.').map(&:to_i) <=> [major, minor, patch])
|
|
44
33
|
comparisons = { :== => [0],
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
require 'concurrent/synchronization'
|
|
2
|
-
|
|
3
1
|
module Concurrent
|
|
4
2
|
|
|
5
3
|
# @!macro monotonic_get_time
|
|
6
4
|
#
|
|
7
|
-
# Returns the current time
|
|
5
|
+
# Returns the current time as tracked by the application monotonic clock.
|
|
8
6
|
#
|
|
9
7
|
# @param [Symbol] unit the time unit to be returned, can be either
|
|
10
8
|
# :float_second, :float_millisecond, :float_microsecond, :second,
|
|
@@ -14,77 +12,8 @@ module Concurrent
|
|
|
14
12
|
# starting point
|
|
15
13
|
#
|
|
16
14
|
# @!macro monotonic_clock_warning
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def monotonic_time(unit = :float_second)
|
|
20
|
-
Process.clock_gettime(Process::CLOCK_MONOTONIC, unit)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
elsif Concurrent.on_jruby?
|
|
24
|
-
|
|
25
|
-
# @!visibility private
|
|
26
|
-
TIME_UNITS = Hash.new { |_hash, key| raise ArgumentError, "unexpected unit: #{key}" }.compare_by_identity
|
|
27
|
-
TIME_UNITS.merge!(
|
|
28
|
-
second: 1_000_000_000,
|
|
29
|
-
millisecond: 1_000_000,
|
|
30
|
-
microsecond: 1_000,
|
|
31
|
-
nanosecond: 1,
|
|
32
|
-
float_second: 1_000_000_000.0,
|
|
33
|
-
float_millisecond: 1_000_000.0,
|
|
34
|
-
float_microsecond: 1_000.0,
|
|
35
|
-
)
|
|
36
|
-
TIME_UNITS.freeze
|
|
37
|
-
private_constant :TIME_UNITS
|
|
38
|
-
|
|
39
|
-
def monotonic_time(unit = :float_second)
|
|
40
|
-
java.lang.System.nanoTime() / TIME_UNITS[unit]
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
else
|
|
44
|
-
|
|
45
|
-
class_definition = Class.new(Synchronization::LockableObject) do
|
|
46
|
-
def initialize
|
|
47
|
-
@last_time = Time.now.to_f
|
|
48
|
-
@time_units = Hash.new { |_hash, key| raise ArgumentError, "unexpected unit: #{key}" }.compare_by_identity
|
|
49
|
-
@time_units.merge!(
|
|
50
|
-
second: [nil, true],
|
|
51
|
-
millisecond: [1_000, true],
|
|
52
|
-
microsecond: [1_000_000, true],
|
|
53
|
-
nanosecond: [1_000_000_000, true],
|
|
54
|
-
float_second: [nil, false],
|
|
55
|
-
float_millisecond: [1_000.0, false],
|
|
56
|
-
float_microsecond: [1_000_000.0, false],
|
|
57
|
-
)
|
|
58
|
-
super()
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# @!visibility private
|
|
62
|
-
def get_time(unit)
|
|
63
|
-
synchronize do
|
|
64
|
-
now = Time.now.to_f
|
|
65
|
-
if @last_time < now
|
|
66
|
-
@last_time = now
|
|
67
|
-
else # clock has moved back in time
|
|
68
|
-
@last_time += 0.000_001
|
|
69
|
-
end
|
|
70
|
-
scale, to_int = @time_units[unit]
|
|
71
|
-
now *= scale if scale
|
|
72
|
-
now = now.to_i if to_int
|
|
73
|
-
now
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# Clock that cannot be set and represents monotonic time since
|
|
79
|
-
# some unspecified starting point.
|
|
80
|
-
#
|
|
81
|
-
# @!visibility private
|
|
82
|
-
GLOBAL_MONOTONIC_CLOCK = class_definition.new
|
|
83
|
-
private_constant :GLOBAL_MONOTONIC_CLOCK
|
|
84
|
-
|
|
85
|
-
def monotonic_time(unit = :float_second)
|
|
86
|
-
GLOBAL_MONOTONIC_CLOCK.get_time(unit)
|
|
87
|
-
end
|
|
15
|
+
def monotonic_time(unit = :float_second)
|
|
16
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC, unit)
|
|
88
17
|
end
|
|
89
18
|
module_function :monotonic_time
|
|
90
19
|
end
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
require 'concurrent/utility/engine'
|
|
2
|
+
# Synchronization::AbstractObject must be defined before loading the extension
|
|
3
|
+
require 'concurrent/synchronization/abstract_object'
|
|
2
4
|
|
|
3
5
|
module Concurrent
|
|
4
|
-
|
|
6
|
+
# @!visibility private
|
|
5
7
|
module Utility
|
|
6
|
-
|
|
7
8
|
# @!visibility private
|
|
8
9
|
module NativeExtensionLoader
|
|
9
10
|
|
|
@@ -15,15 +16,7 @@ module Concurrent
|
|
|
15
16
|
defined?(@c_extensions_loaded) && @c_extensions_loaded
|
|
16
17
|
end
|
|
17
18
|
|
|
18
|
-
def java_extensions_loaded?
|
|
19
|
-
defined?(@java_extensions_loaded) && @java_extensions_loaded
|
|
20
|
-
end
|
|
21
|
-
|
|
22
19
|
def load_native_extensions
|
|
23
|
-
unless defined? Synchronization::AbstractObject
|
|
24
|
-
raise 'native_extension_loader loaded before Synchronization::AbstractObject'
|
|
25
|
-
end
|
|
26
|
-
|
|
27
20
|
if Concurrent.on_cruby? && !c_extensions_loaded?
|
|
28
21
|
['concurrent/concurrent_ruby_ext',
|
|
29
22
|
"concurrent/#{RUBY_VERSION[0..2]}/concurrent_ruby_ext"
|
|
@@ -54,6 +47,10 @@ module Concurrent
|
|
|
54
47
|
@c_extensions_loaded = true
|
|
55
48
|
end
|
|
56
49
|
|
|
50
|
+
def java_extensions_loaded?
|
|
51
|
+
defined?(@java_extensions_loaded) && @java_extensions_loaded
|
|
52
|
+
end
|
|
53
|
+
|
|
57
54
|
def set_java_extensions_loaded
|
|
58
55
|
@java_extensions_loaded = true
|
|
59
56
|
end
|
|
@@ -77,3 +74,4 @@ module Concurrent
|
|
|
77
74
|
extend Utility::NativeExtensionLoader
|
|
78
75
|
end
|
|
79
76
|
|
|
77
|
+
Concurrent.load_native_extensions
|