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
|
@@ -55,12 +55,6 @@ public class SynchronizationLibrary implements Library {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
private static final ObjectAllocator JRUBY_OBJECT_ALLOCATOR = new ObjectAllocator() {
|
|
59
|
-
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
|
60
|
-
return new JRubyObject(runtime, klazz);
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
|
|
64
58
|
private static final ObjectAllocator OBJECT_ALLOCATOR = new ObjectAllocator() {
|
|
65
59
|
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
|
|
66
60
|
return new Object(runtime, klazz);
|
|
@@ -87,10 +81,7 @@ public class SynchronizationLibrary implements Library {
|
|
|
87
81
|
RubyModule jrubyAttrVolatileModule = synchronizationModule.defineModuleUnder("JRubyAttrVolatile");
|
|
88
82
|
jrubyAttrVolatileModule.defineAnnotatedMethods(JRubyAttrVolatile.class);
|
|
89
83
|
|
|
90
|
-
defineClass(runtime, synchronizationModule, "AbstractObject", "
|
|
91
|
-
JRubyObject.class, JRUBY_OBJECT_ALLOCATOR);
|
|
92
|
-
|
|
93
|
-
defineClass(runtime, synchronizationModule, "JRubyObject", "Object",
|
|
84
|
+
defineClass(runtime, synchronizationModule, "AbstractObject", "Object",
|
|
94
85
|
Object.class, OBJECT_ALLOCATOR);
|
|
95
86
|
|
|
96
87
|
defineClass(runtime, synchronizationModule, "Object", "AbstractLockableObject",
|
|
@@ -143,8 +134,8 @@ public class SynchronizationLibrary implements Library {
|
|
|
143
134
|
// attempt to avoid code elimination.
|
|
144
135
|
private static volatile int volatileField;
|
|
145
136
|
|
|
146
|
-
@JRubyMethod(name = "full_memory_barrier", visibility = Visibility.PUBLIC)
|
|
147
|
-
public static IRubyObject fullMemoryBarrier(ThreadContext context, IRubyObject
|
|
137
|
+
@JRubyMethod(name = "full_memory_barrier", visibility = Visibility.PUBLIC, module = true)
|
|
138
|
+
public static IRubyObject fullMemoryBarrier(ThreadContext context, IRubyObject module) {
|
|
148
139
|
// Prevent reordering of ivar writes with publication of this instance
|
|
149
140
|
if (!FULL_FENCE) {
|
|
150
141
|
// Assuming that following volatile read and write is not eliminated it simulates fullFence.
|
|
@@ -158,9 +149,10 @@ public class SynchronizationLibrary implements Library {
|
|
|
158
149
|
return context.nil;
|
|
159
150
|
}
|
|
160
151
|
|
|
161
|
-
@JRubyMethod(name = "instance_variable_get_volatile", visibility = Visibility.PUBLIC)
|
|
152
|
+
@JRubyMethod(name = "instance_variable_get_volatile", visibility = Visibility.PUBLIC, module = true)
|
|
162
153
|
public static IRubyObject instanceVariableGetVolatile(
|
|
163
154
|
ThreadContext context,
|
|
155
|
+
IRubyObject module,
|
|
164
156
|
IRubyObject self,
|
|
165
157
|
IRubyObject name) {
|
|
166
158
|
// Ensure we ses latest value with loadFence
|
|
@@ -174,9 +166,10 @@ public class SynchronizationLibrary implements Library {
|
|
|
174
166
|
}
|
|
175
167
|
}
|
|
176
168
|
|
|
177
|
-
@JRubyMethod(name = "instance_variable_set_volatile", visibility = Visibility.PUBLIC)
|
|
169
|
+
@JRubyMethod(name = "instance_variable_set_volatile", visibility = Visibility.PUBLIC, module = true)
|
|
178
170
|
public static IRubyObject InstanceVariableSetVolatile(
|
|
179
171
|
ThreadContext context,
|
|
172
|
+
IRubyObject module,
|
|
180
173
|
IRubyObject self,
|
|
181
174
|
IRubyObject name,
|
|
182
175
|
IRubyObject value) {
|
|
@@ -195,16 +188,8 @@ public class SynchronizationLibrary implements Library {
|
|
|
195
188
|
}
|
|
196
189
|
}
|
|
197
190
|
|
|
198
|
-
@JRubyClass(name = "
|
|
199
|
-
public static class
|
|
200
|
-
|
|
201
|
-
public JRubyObject(Ruby runtime, RubyClass metaClass) {
|
|
202
|
-
super(runtime, metaClass);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
@JRubyClass(name = "Object", parent = "JRubyObject")
|
|
207
|
-
public static class Object extends JRubyObject {
|
|
191
|
+
@JRubyClass(name = "Object", parent = "AbstractObject")
|
|
192
|
+
public static class Object extends RubyObject {
|
|
208
193
|
|
|
209
194
|
public Object(Ruby runtime, RubyClass metaClass) {
|
|
210
195
|
super(runtime, metaClass);
|
|
@@ -220,7 +205,7 @@ public class SynchronizationLibrary implements Library {
|
|
|
220
205
|
}
|
|
221
206
|
|
|
222
207
|
@JRubyClass(name = "JRubyLockableObject", parent = "AbstractLockableObject")
|
|
223
|
-
public static class JRubyLockableObject extends
|
|
208
|
+
public static class JRubyLockableObject extends AbstractLockableObject {
|
|
224
209
|
|
|
225
210
|
public JRubyLockableObject(Ruby runtime, RubyClass metaClass) {
|
|
226
211
|
super(runtime, metaClass);
|
|
@@ -481,7 +481,7 @@ public class ConcurrentHashMapV8<K, V>
|
|
|
481
481
|
*
|
|
482
482
|
* Maintaining API and serialization compatibility with previous
|
|
483
483
|
* versions of this class introduces several oddities. Mainly: We
|
|
484
|
-
* leave untouched but unused constructor arguments
|
|
484
|
+
* leave untouched but unused constructor arguments referring to
|
|
485
485
|
* concurrencyLevel. We accept a loadFactor constructor argument,
|
|
486
486
|
* but apply it only to initial table capacity (which is the only
|
|
487
487
|
* time that we can guarantee to honor it.) We also declare an
|
|
@@ -484,7 +484,7 @@ public class ConcurrentHashMapV8<K, V>
|
|
|
484
484
|
*
|
|
485
485
|
* Maintaining API and serialization compatibility with previous
|
|
486
486
|
* versions of this class introduces several oddities. Mainly: We
|
|
487
|
-
* leave untouched but unused constructor arguments
|
|
487
|
+
* leave untouched but unused constructor arguments referring to
|
|
488
488
|
* concurrencyLevel. We accept a loadFactor constructor argument,
|
|
489
489
|
* but apply it only to initial table capacity (which is the only
|
|
490
490
|
* time that we can guarantee to honor it.) We also declare an
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
require 'concurrent/configuration'
|
|
2
2
|
require 'concurrent/atomic/atomic_reference'
|
|
3
|
+
require 'concurrent/atomic/count_down_latch'
|
|
3
4
|
require 'concurrent/atomic/thread_local_var'
|
|
4
5
|
require 'concurrent/collection/copy_on_write_observer_set'
|
|
5
6
|
require 'concurrent/concern/observable'
|
|
6
|
-
require 'concurrent/synchronization'
|
|
7
|
+
require 'concurrent/synchronization/lockable_object'
|
|
7
8
|
|
|
8
9
|
module Concurrent
|
|
9
10
|
|
|
@@ -370,7 +371,7 @@ module Concurrent
|
|
|
370
371
|
# @param [Float] timeout the maximum number of seconds to wait
|
|
371
372
|
# @return [Boolean] true if all actions complete before timeout
|
|
372
373
|
#
|
|
373
|
-
# @raise [Concurrent::TimeoutError] when
|
|
374
|
+
# @raise [Concurrent::TimeoutError] when timeout is reached
|
|
374
375
|
#
|
|
375
376
|
# @!macro agent_await_warning
|
|
376
377
|
def await_for!(timeout)
|
|
@@ -476,7 +477,7 @@ module Concurrent
|
|
|
476
477
|
# @param [Array<Concurrent::Agent>] agents the Agents on which to wait
|
|
477
478
|
# @return [Boolean] true if all actions complete before timeout
|
|
478
479
|
#
|
|
479
|
-
# @raise [Concurrent::TimeoutError] when
|
|
480
|
+
# @raise [Concurrent::TimeoutError] when timeout is reached
|
|
480
481
|
# @!macro agent_await_warning
|
|
481
482
|
def await_for!(timeout, *agents)
|
|
482
483
|
raise Concurrent::TimeoutError unless await_for(timeout, *agents)
|
|
@@ -21,9 +21,9 @@ module Concurrent
|
|
|
21
21
|
# @!macro internal_implementation_note
|
|
22
22
|
ArrayImplementation = case
|
|
23
23
|
when Concurrent.on_cruby?
|
|
24
|
-
# Array is thread-safe
|
|
25
|
-
#
|
|
26
|
-
#
|
|
24
|
+
# Array is not fully thread-safe on CRuby, see
|
|
25
|
+
# https://github.com/ruby-concurrency/concurrent-ruby/issues/929
|
|
26
|
+
# So we will need to add synchronization here
|
|
27
27
|
::Array
|
|
28
28
|
|
|
29
29
|
when Concurrent.on_jruby?
|
|
@@ -34,16 +34,6 @@ module Concurrent
|
|
|
34
34
|
end
|
|
35
35
|
JRubyArray
|
|
36
36
|
|
|
37
|
-
when Concurrent.on_rbx?
|
|
38
|
-
require 'monitor'
|
|
39
|
-
require 'concurrent/thread_safe/util/data_structures'
|
|
40
|
-
|
|
41
|
-
class RbxArray < ::Array
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
ThreadSafe::Util.make_synchronized_on_rbx RbxArray
|
|
45
|
-
RbxArray
|
|
46
|
-
|
|
47
37
|
when Concurrent.on_truffleruby?
|
|
48
38
|
require 'concurrent/thread_safe/util/data_structures'
|
|
49
39
|
|
|
@@ -218,7 +218,7 @@ module Concurrent
|
|
|
218
218
|
|
|
219
219
|
# @!method self.new(*args, &block)
|
|
220
220
|
#
|
|
221
|
-
#
|
|
221
|
+
# Instantiate a new object and ensure proper initialization of the
|
|
222
222
|
# synchronization mechanisms.
|
|
223
223
|
#
|
|
224
224
|
# @param [Array<Object>] args Zero or more arguments to be passed to the
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require 'concurrent/atomic/atomic_reference'
|
|
2
2
|
require 'concurrent/collection/copy_on_notify_observer_set'
|
|
3
3
|
require 'concurrent/concern/observable'
|
|
4
|
-
require 'concurrent/synchronization'
|
|
4
|
+
require 'concurrent/synchronization/object'
|
|
5
5
|
|
|
6
6
|
# @!macro thread_safe_variable_comparison
|
|
7
7
|
#
|
|
@@ -113,7 +113,7 @@ module Concurrent
|
|
|
113
113
|
# @option opts [Proc] :validator (nil) Optional proc used to validate new
|
|
114
114
|
# values. It must accept one and only one argument which will be the
|
|
115
115
|
# intended new value. The validator will return true if the new value
|
|
116
|
-
# is acceptable else return false (
|
|
116
|
+
# is acceptable else return false (preferably) or raise an exception.
|
|
117
117
|
#
|
|
118
118
|
# @!macro deref_options
|
|
119
119
|
#
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
require 'concurrent/utility/native_extension_loader' # load native parts first
|
|
2
|
+
|
|
1
3
|
require 'concurrent/atomic/mutex_atomic_boolean'
|
|
2
|
-
require 'concurrent/synchronization'
|
|
3
4
|
|
|
4
5
|
module Concurrent
|
|
5
6
|
|
|
@@ -79,10 +80,10 @@ module Concurrent
|
|
|
79
80
|
# @!visibility private
|
|
80
81
|
# @!macro internal_implementation_note
|
|
81
82
|
AtomicBooleanImplementation = case
|
|
82
|
-
when
|
|
83
|
-
JavaAtomicBoolean
|
|
84
|
-
when defined?(CAtomicBoolean)
|
|
83
|
+
when Concurrent.on_cruby? && Concurrent.c_extensions_loaded?
|
|
85
84
|
CAtomicBoolean
|
|
85
|
+
when Concurrent.on_jruby?
|
|
86
|
+
JavaAtomicBoolean
|
|
86
87
|
else
|
|
87
88
|
MutexAtomicBoolean
|
|
88
89
|
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
require 'concurrent/utility/native_extension_loader' # load native parts first
|
|
2
|
+
|
|
1
3
|
require 'concurrent/atomic/mutex_atomic_fixnum'
|
|
2
|
-
require 'concurrent/synchronization'
|
|
3
4
|
|
|
4
5
|
module Concurrent
|
|
5
6
|
|
|
@@ -96,10 +97,10 @@ module Concurrent
|
|
|
96
97
|
# @!visibility private
|
|
97
98
|
# @!macro internal_implementation_note
|
|
98
99
|
AtomicFixnumImplementation = case
|
|
99
|
-
when
|
|
100
|
-
JavaAtomicFixnum
|
|
101
|
-
when defined?(CAtomicFixnum)
|
|
100
|
+
when Concurrent.on_cruby? && Concurrent.c_extensions_loaded?
|
|
102
101
|
CAtomicFixnum
|
|
102
|
+
when Concurrent.on_jruby?
|
|
103
|
+
JavaAtomicFixnum
|
|
103
104
|
else
|
|
104
105
|
MutexAtomicFixnum
|
|
105
106
|
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
require 'concurrent/
|
|
2
|
-
|
|
1
|
+
require 'concurrent/utility/native_extension_loader' # load native parts first
|
|
2
|
+
|
|
3
|
+
require 'concurrent/atomic_reference/atomic_direct_update'
|
|
3
4
|
require 'concurrent/atomic_reference/numeric_cas_wrapper'
|
|
5
|
+
require 'concurrent/atomic_reference/mutex_atomic'
|
|
4
6
|
|
|
5
7
|
# Shim for TruffleRuby::AtomicReference
|
|
6
8
|
if Concurrent.on_truffleruby? && !defined?(TruffleRuby::AtomicReference)
|
|
@@ -12,138 +14,6 @@ end
|
|
|
12
14
|
|
|
13
15
|
module Concurrent
|
|
14
16
|
|
|
15
|
-
# Define update methods that use direct paths
|
|
16
|
-
#
|
|
17
|
-
# @!visibility private
|
|
18
|
-
# @!macro internal_implementation_note
|
|
19
|
-
module AtomicDirectUpdate
|
|
20
|
-
|
|
21
|
-
# @!macro atomic_reference_method_update
|
|
22
|
-
#
|
|
23
|
-
# Pass the current value to the given block, replacing it
|
|
24
|
-
# with the block's result. May retry if the value changes
|
|
25
|
-
# during the block's execution.
|
|
26
|
-
#
|
|
27
|
-
# @yield [Object] Calculate a new value for the atomic reference using
|
|
28
|
-
# given (old) value
|
|
29
|
-
# @yieldparam [Object] old_value the starting value of the atomic reference
|
|
30
|
-
# @return [Object] the new value
|
|
31
|
-
def update
|
|
32
|
-
true until compare_and_set(old_value = get, new_value = yield(old_value))
|
|
33
|
-
new_value
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# @!macro atomic_reference_method_try_update
|
|
37
|
-
#
|
|
38
|
-
# Pass the current value to the given block, replacing it
|
|
39
|
-
# with the block's result. Return nil if the update fails.
|
|
40
|
-
#
|
|
41
|
-
# @yield [Object] Calculate a new value for the atomic reference using
|
|
42
|
-
# given (old) value
|
|
43
|
-
# @yieldparam [Object] old_value the starting value of the atomic reference
|
|
44
|
-
# @note This method was altered to avoid raising an exception by default.
|
|
45
|
-
# Instead, this method now returns `nil` in case of failure. For more info,
|
|
46
|
-
# please see: https://github.com/ruby-concurrency/concurrent-ruby/pull/336
|
|
47
|
-
# @return [Object] the new value, or nil if update failed
|
|
48
|
-
def try_update
|
|
49
|
-
old_value = get
|
|
50
|
-
new_value = yield old_value
|
|
51
|
-
|
|
52
|
-
return unless compare_and_set old_value, new_value
|
|
53
|
-
|
|
54
|
-
new_value
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# @!macro atomic_reference_method_try_update!
|
|
58
|
-
#
|
|
59
|
-
# Pass the current value to the given block, replacing it
|
|
60
|
-
# with the block's result. Raise an exception if the update
|
|
61
|
-
# fails.
|
|
62
|
-
#
|
|
63
|
-
# @yield [Object] Calculate a new value for the atomic reference using
|
|
64
|
-
# given (old) value
|
|
65
|
-
# @yieldparam [Object] old_value the starting value of the atomic reference
|
|
66
|
-
# @note This behavior mimics the behavior of the original
|
|
67
|
-
# `AtomicReference#try_update` API. The reason this was changed was to
|
|
68
|
-
# avoid raising exceptions (which are inherently slow) by default. For more
|
|
69
|
-
# info: https://github.com/ruby-concurrency/concurrent-ruby/pull/336
|
|
70
|
-
# @return [Object] the new value
|
|
71
|
-
# @raise [Concurrent::ConcurrentUpdateError] if the update fails
|
|
72
|
-
def try_update!
|
|
73
|
-
old_value = get
|
|
74
|
-
new_value = yield old_value
|
|
75
|
-
unless compare_and_set(old_value, new_value)
|
|
76
|
-
if $VERBOSE
|
|
77
|
-
raise ConcurrentUpdateError, "Update failed"
|
|
78
|
-
else
|
|
79
|
-
raise ConcurrentUpdateError, "Update failed", ConcurrentUpdateError::CONC_UP_ERR_BACKTRACE
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
new_value
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
require 'concurrent/atomic_reference/mutex_atomic'
|
|
87
|
-
|
|
88
|
-
# @!macro atomic_reference
|
|
89
|
-
#
|
|
90
|
-
# An object reference that may be updated atomically. All read and write
|
|
91
|
-
# operations have java volatile semantic.
|
|
92
|
-
#
|
|
93
|
-
# @!macro thread_safe_variable_comparison
|
|
94
|
-
#
|
|
95
|
-
# @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
|
|
96
|
-
# @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html
|
|
97
|
-
#
|
|
98
|
-
# @!method initialize(value = nil)
|
|
99
|
-
# @!macro atomic_reference_method_initialize
|
|
100
|
-
# @param [Object] value The initial value.
|
|
101
|
-
#
|
|
102
|
-
# @!method get
|
|
103
|
-
# @!macro atomic_reference_method_get
|
|
104
|
-
# Gets the current value.
|
|
105
|
-
# @return [Object] the current value
|
|
106
|
-
#
|
|
107
|
-
# @!method set(new_value)
|
|
108
|
-
# @!macro atomic_reference_method_set
|
|
109
|
-
# Sets to the given value.
|
|
110
|
-
# @param [Object] new_value the new value
|
|
111
|
-
# @return [Object] the new value
|
|
112
|
-
#
|
|
113
|
-
# @!method get_and_set(new_value)
|
|
114
|
-
# @!macro atomic_reference_method_get_and_set
|
|
115
|
-
# Atomically sets to the given value and returns the old value.
|
|
116
|
-
# @param [Object] new_value the new value
|
|
117
|
-
# @return [Object] the old value
|
|
118
|
-
#
|
|
119
|
-
# @!method compare_and_set(old_value, new_value)
|
|
120
|
-
# @!macro atomic_reference_method_compare_and_set
|
|
121
|
-
#
|
|
122
|
-
# Atomically sets the value to the given updated value if
|
|
123
|
-
# the current value == the expected value.
|
|
124
|
-
#
|
|
125
|
-
# @param [Object] old_value the expected value
|
|
126
|
-
# @param [Object] new_value the new value
|
|
127
|
-
#
|
|
128
|
-
# @return [Boolean] `true` if successful. A `false` return indicates
|
|
129
|
-
# that the actual value was not equal to the expected value.
|
|
130
|
-
#
|
|
131
|
-
# @!method update
|
|
132
|
-
# @!macro atomic_reference_method_update
|
|
133
|
-
#
|
|
134
|
-
# @!method try_update
|
|
135
|
-
# @!macro atomic_reference_method_try_update
|
|
136
|
-
#
|
|
137
|
-
# @!method try_update!
|
|
138
|
-
# @!macro atomic_reference_method_try_update!
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
# @!macro internal_implementation_note
|
|
142
|
-
class ConcurrentUpdateError < ThreadError
|
|
143
|
-
# frozen pre-allocated backtrace to speed ConcurrentUpdateError
|
|
144
|
-
CONC_UP_ERR_BACKTRACE = ['backtrace elided; set verbose to enable'].freeze
|
|
145
|
-
end
|
|
146
|
-
|
|
147
17
|
# @!macro internal_implementation_note
|
|
148
18
|
AtomicReferenceImplementation = case
|
|
149
19
|
when Concurrent.on_cruby? && Concurrent.c_extensions_loaded?
|
|
@@ -152,7 +22,6 @@ module Concurrent
|
|
|
152
22
|
class CAtomicReference
|
|
153
23
|
include AtomicDirectUpdate
|
|
154
24
|
include AtomicNumericCompareAndSetWrapper
|
|
155
|
-
alias_method :compare_and_swap, :compare_and_set
|
|
156
25
|
end
|
|
157
26
|
CAtomicReference
|
|
158
27
|
when Concurrent.on_jruby?
|
|
@@ -160,39 +29,107 @@ module Concurrent
|
|
|
160
29
|
# @!macro internal_implementation_note
|
|
161
30
|
class JavaAtomicReference
|
|
162
31
|
include AtomicDirectUpdate
|
|
32
|
+
include AtomicNumericCompareAndSetWrapper
|
|
163
33
|
end
|
|
164
34
|
JavaAtomicReference
|
|
165
35
|
when Concurrent.on_truffleruby?
|
|
166
36
|
class TruffleRubyAtomicReference < TruffleRuby::AtomicReference
|
|
167
37
|
include AtomicDirectUpdate
|
|
38
|
+
if private_method_defined?(:compare_and_set_reference)
|
|
39
|
+
alias_method :_compare_and_set, :compare_and_set_reference
|
|
40
|
+
private :_compare_and_set
|
|
41
|
+
include AtomicNumericCompareAndSetWrapper
|
|
42
|
+
else
|
|
43
|
+
# AtomicNumericCompareAndSetWrapper behavior already in TruffleRuby::AtomicReference
|
|
44
|
+
alias_method :compare_and_swap, :compare_and_set
|
|
45
|
+
end
|
|
168
46
|
alias_method :value, :get
|
|
169
47
|
alias_method :value=, :set
|
|
170
|
-
alias_method :compare_and_swap, :compare_and_set
|
|
171
48
|
alias_method :swap, :get_and_set
|
|
172
49
|
end
|
|
173
50
|
TruffleRubyAtomicReference
|
|
174
|
-
when Concurrent.on_rbx?
|
|
175
|
-
# @note Extends `Rubinius::AtomicReference` version adding aliases
|
|
176
|
-
# and numeric logic.
|
|
177
|
-
#
|
|
178
|
-
# @!visibility private
|
|
179
|
-
# @!macro internal_implementation_note
|
|
180
|
-
class RbxAtomicReference < Rubinius::AtomicReference
|
|
181
|
-
alias_method :_compare_and_set, :compare_and_set
|
|
182
|
-
include AtomicDirectUpdate
|
|
183
|
-
include AtomicNumericCompareAndSetWrapper
|
|
184
|
-
alias_method :value, :get
|
|
185
|
-
alias_method :value=, :set
|
|
186
|
-
alias_method :swap, :get_and_set
|
|
187
|
-
alias_method :compare_and_swap, :compare_and_set
|
|
188
|
-
end
|
|
189
|
-
RbxAtomicReference
|
|
190
51
|
else
|
|
191
52
|
MutexAtomicReference
|
|
192
53
|
end
|
|
193
54
|
private_constant :AtomicReferenceImplementation
|
|
194
55
|
|
|
195
|
-
#
|
|
56
|
+
# An object reference that may be updated atomically. All read and write
|
|
57
|
+
# operations have java volatile semantic.
|
|
58
|
+
#
|
|
59
|
+
# @!macro thread_safe_variable_comparison
|
|
60
|
+
#
|
|
61
|
+
# @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
|
|
62
|
+
# @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html
|
|
63
|
+
#
|
|
64
|
+
# @!method initialize(value = nil)
|
|
65
|
+
# @!macro atomic_reference_method_initialize
|
|
66
|
+
# @param [Object] value The initial value.
|
|
67
|
+
#
|
|
68
|
+
# @!method get
|
|
69
|
+
# @!macro atomic_reference_method_get
|
|
70
|
+
# Gets the current value.
|
|
71
|
+
# @return [Object] the current value
|
|
72
|
+
#
|
|
73
|
+
# @!method set(new_value)
|
|
74
|
+
# @!macro atomic_reference_method_set
|
|
75
|
+
# Sets to the given value.
|
|
76
|
+
# @param [Object] new_value the new value
|
|
77
|
+
# @return [Object] the new value
|
|
78
|
+
#
|
|
79
|
+
# @!method get_and_set(new_value)
|
|
80
|
+
# @!macro atomic_reference_method_get_and_set
|
|
81
|
+
# Atomically sets to the given value and returns the old value.
|
|
82
|
+
# @param [Object] new_value the new value
|
|
83
|
+
# @return [Object] the old value
|
|
84
|
+
#
|
|
85
|
+
# @!method compare_and_set(old_value, new_value)
|
|
86
|
+
# @!macro atomic_reference_method_compare_and_set
|
|
87
|
+
#
|
|
88
|
+
# Atomically sets the value to the given updated value if
|
|
89
|
+
# the current value == the expected value.
|
|
90
|
+
#
|
|
91
|
+
# @param [Object] old_value the expected value
|
|
92
|
+
# @param [Object] new_value the new value
|
|
93
|
+
#
|
|
94
|
+
# @return [Boolean] `true` if successful. A `false` return indicates
|
|
95
|
+
# that the actual value was not equal to the expected value.
|
|
96
|
+
#
|
|
97
|
+
# @!method update
|
|
98
|
+
# Pass the current value to the given block, replacing it
|
|
99
|
+
# with the block's result. May retry if the value changes
|
|
100
|
+
# during the block's execution.
|
|
101
|
+
#
|
|
102
|
+
# @yield [Object] Calculate a new value for the atomic reference using
|
|
103
|
+
# given (old) value
|
|
104
|
+
# @yieldparam [Object] old_value the starting value of the atomic reference
|
|
105
|
+
# @return [Object] the new value
|
|
106
|
+
#
|
|
107
|
+
# @!method try_update
|
|
108
|
+
# Pass the current value to the given block, replacing it
|
|
109
|
+
# with the block's result. Return nil if the update fails.
|
|
110
|
+
#
|
|
111
|
+
# @yield [Object] Calculate a new value for the atomic reference using
|
|
112
|
+
# given (old) value
|
|
113
|
+
# @yieldparam [Object] old_value the starting value of the atomic reference
|
|
114
|
+
# @note This method was altered to avoid raising an exception by default.
|
|
115
|
+
# Instead, this method now returns `nil` in case of failure. For more info,
|
|
116
|
+
# please see: https://github.com/ruby-concurrency/concurrent-ruby/pull/336
|
|
117
|
+
# @return [Object] the new value, or nil if update failed
|
|
118
|
+
#
|
|
119
|
+
# @!method try_update!
|
|
120
|
+
# Pass the current value to the given block, replacing it
|
|
121
|
+
# with the block's result. Raise an exception if the update
|
|
122
|
+
# fails.
|
|
123
|
+
#
|
|
124
|
+
# @yield [Object] Calculate a new value for the atomic reference using
|
|
125
|
+
# given (old) value
|
|
126
|
+
# @yieldparam [Object] old_value the starting value of the atomic reference
|
|
127
|
+
# @note This behavior mimics the behavior of the original
|
|
128
|
+
# `AtomicReference#try_update` API. The reason this was changed was to
|
|
129
|
+
# avoid raising exceptions (which are inherently slow) by default. For more
|
|
130
|
+
# info: https://github.com/ruby-concurrency/concurrent-ruby/pull/336
|
|
131
|
+
# @return [Object] the new value
|
|
132
|
+
# @raise [Concurrent::ConcurrentUpdateError] if the update fails
|
|
196
133
|
class AtomicReference < AtomicReferenceImplementation
|
|
197
134
|
|
|
198
135
|
# @return [String] Short string representation.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
require 'concurrent/constants'
|
|
2
|
+
require_relative 'locals'
|
|
3
|
+
|
|
4
|
+
module Concurrent
|
|
5
|
+
|
|
6
|
+
# A `FiberLocalVar` is a variable where the value is different for each fiber.
|
|
7
|
+
# Each variable may have a default value, but when you modify the variable only
|
|
8
|
+
# the current fiber will ever see that change.
|
|
9
|
+
#
|
|
10
|
+
# This is similar to Ruby's built-in fiber-local variables (`Thread.current[:name]`),
|
|
11
|
+
# but with these major advantages:
|
|
12
|
+
# * `FiberLocalVar` has its own identity, it doesn't need a Symbol.
|
|
13
|
+
# * Each Ruby's built-in fiber-local variable leaks some memory forever (it's a Symbol held forever on the fiber),
|
|
14
|
+
# so it's only OK to create a small amount of them.
|
|
15
|
+
# `FiberLocalVar` has no such issue and it is fine to create many of them.
|
|
16
|
+
# * Ruby's built-in fiber-local variables leak forever the value set on each fiber (unless set to nil explicitly).
|
|
17
|
+
# `FiberLocalVar` automatically removes the mapping for each fiber once the `FiberLocalVar` instance is GC'd.
|
|
18
|
+
#
|
|
19
|
+
# @example
|
|
20
|
+
# v = FiberLocalVar.new(14)
|
|
21
|
+
# v.value #=> 14
|
|
22
|
+
# v.value = 2
|
|
23
|
+
# v.value #=> 2
|
|
24
|
+
#
|
|
25
|
+
# @example
|
|
26
|
+
# v = FiberLocalVar.new(14)
|
|
27
|
+
#
|
|
28
|
+
# Fiber.new do
|
|
29
|
+
# v.value #=> 14
|
|
30
|
+
# v.value = 1
|
|
31
|
+
# v.value #=> 1
|
|
32
|
+
# end.resume
|
|
33
|
+
#
|
|
34
|
+
# Fiber.new do
|
|
35
|
+
# v.value #=> 14
|
|
36
|
+
# v.value = 2
|
|
37
|
+
# v.value #=> 2
|
|
38
|
+
# end.resume
|
|
39
|
+
#
|
|
40
|
+
# v.value #=> 14
|
|
41
|
+
class FiberLocalVar
|
|
42
|
+
LOCALS = FiberLocals.new
|
|
43
|
+
|
|
44
|
+
# Creates a fiber local variable.
|
|
45
|
+
#
|
|
46
|
+
# @param [Object] default the default value when otherwise unset
|
|
47
|
+
# @param [Proc] default_block Optional block that gets called to obtain the
|
|
48
|
+
# default value for each fiber
|
|
49
|
+
def initialize(default = nil, &default_block)
|
|
50
|
+
if default && block_given?
|
|
51
|
+
raise ArgumentError, "Cannot use both value and block as default value"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
if block_given?
|
|
55
|
+
@default_block = default_block
|
|
56
|
+
@default = nil
|
|
57
|
+
else
|
|
58
|
+
@default_block = nil
|
|
59
|
+
@default = default
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
@index = LOCALS.next_index(self)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Returns the value in the current fiber's copy of this fiber-local variable.
|
|
66
|
+
#
|
|
67
|
+
# @return [Object] the current value
|
|
68
|
+
def value
|
|
69
|
+
LOCALS.fetch(@index) { default }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Sets the current fiber's copy of this fiber-local variable to the specified value.
|
|
73
|
+
#
|
|
74
|
+
# @param [Object] value the value to set
|
|
75
|
+
# @return [Object] the new value
|
|
76
|
+
def value=(value)
|
|
77
|
+
LOCALS.set(@index, value)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Bind the given value to fiber local storage during
|
|
81
|
+
# execution of the given block.
|
|
82
|
+
#
|
|
83
|
+
# @param [Object] value the value to bind
|
|
84
|
+
# @yield the operation to be performed with the bound variable
|
|
85
|
+
# @return [Object] the value
|
|
86
|
+
def bind(value)
|
|
87
|
+
if block_given?
|
|
88
|
+
old_value = self.value
|
|
89
|
+
self.value = value
|
|
90
|
+
begin
|
|
91
|
+
yield
|
|
92
|
+
ensure
|
|
93
|
+
self.value = old_value
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
protected
|
|
99
|
+
|
|
100
|
+
# @!visibility private
|
|
101
|
+
def default
|
|
102
|
+
if @default_block
|
|
103
|
+
self.value = @default_block.call
|
|
104
|
+
else
|
|
105
|
+
@default
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|