concurrent-ruby 1.1.8 → 1.2.2
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 +45 -0
- data/Gemfile +2 -8
- data/README.md +49 -28
- data/Rakefile +66 -81
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +0 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +52 -22
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
- data/lib/concurrent-ruby/concurrent/agent.rb +2 -1
- data/lib/concurrent-ruby/concurrent/array.rb +0 -10
- data/lib/concurrent-ruby/concurrent/async.rb +1 -0
- data/lib/concurrent-ruby/concurrent/atom.rb +1 -1
- 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 +82 -151
- data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/event.rb +3 -3
- 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 +28 -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 +19 -3
- data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +2 -1
- data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +9 -9
- data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +32 -14
- 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 +15 -4
- 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/truffleruby_map_backend.rb +14 -0
- data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +11 -1
- data/lib/concurrent-ruby/concurrent/concern/logging.rb +86 -2
- 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 +2 -2
- 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 +17 -14
- data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +13 -3
- data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +3 -3
- data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +4 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +10 -4
- data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +26 -37
- data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +6 -6
- 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/hash.rb +0 -9
- 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 -30
- 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 +1 -1
- data/lib/concurrent-ruby/concurrent/promise.rb +2 -1
- data/lib/concurrent-ruby/concurrent/promises.rb +7 -6
- data/lib/concurrent-ruby/concurrent/re_include.rb +2 -0
- data/lib/concurrent-ruby/concurrent/scheduled_task.rb +30 -17
- data/lib/concurrent-ruby/concurrent/set.rb +12 -14
- 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/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 +6 -5
- data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +18 -5
- data/lib/concurrent-ruby/concurrent/synchronization/object.rb +12 -44
- 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/cheap_lockable.rb +2 -39
- data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +16 -27
- data/lib/concurrent-ruby/concurrent/timer_task.rb +11 -33
- data/lib/concurrent-ruby/concurrent/tuple.rb +1 -5
- data/lib/concurrent-ruby/concurrent/tvar.rb +22 -61
- data/lib/concurrent-ruby/concurrent/utility/engine.rb +5 -16
- data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +7 -46
- 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 +36 -89
- data/lib/concurrent-ruby/concurrent/version.rb +1 -1
- data/lib/concurrent-ruby/concurrent-ruby.rb +5 -1
- metadata +11 -12
- 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/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 -65
- data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +0 -49
- data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +0 -47
@@ -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);
|
@@ -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
|
|
@@ -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
|
|
@@ -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
|
#
|
@@ -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?
|
@@ -170,28 +40,89 @@ module Concurrent
|
|
170
40
|
alias_method :compare_and_swap, :compare_and_set
|
171
41
|
alias_method :swap, :get_and_set
|
172
42
|
end
|
173
|
-
|
174
|
-
# @note Extends `Rubinius::AtomicReference` version adding aliases
|
175
|
-
# and numeric logic.
|
176
|
-
#
|
177
|
-
# @!visibility private
|
178
|
-
# @!macro internal_implementation_note
|
179
|
-
class RbxAtomicReference < Rubinius::AtomicReference
|
180
|
-
alias_method :_compare_and_set, :compare_and_set
|
181
|
-
include AtomicDirectUpdate
|
182
|
-
include AtomicNumericCompareAndSetWrapper
|
183
|
-
alias_method :value, :get
|
184
|
-
alias_method :value=, :set
|
185
|
-
alias_method :swap, :get_and_set
|
186
|
-
alias_method :compare_and_swap, :compare_and_set
|
187
|
-
end
|
188
|
-
RbxAtomicReference
|
43
|
+
TruffleRubyAtomicReference
|
189
44
|
else
|
190
45
|
MutexAtomicReference
|
191
46
|
end
|
192
47
|
private_constant :AtomicReferenceImplementation
|
193
48
|
|
194
|
-
#
|
49
|
+
# An object reference that may be updated atomically. All read and write
|
50
|
+
# operations have java volatile semantic.
|
51
|
+
#
|
52
|
+
# @!macro thread_safe_variable_comparison
|
53
|
+
#
|
54
|
+
# @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
|
55
|
+
# @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html
|
56
|
+
#
|
57
|
+
# @!method initialize(value = nil)
|
58
|
+
# @!macro atomic_reference_method_initialize
|
59
|
+
# @param [Object] value The initial value.
|
60
|
+
#
|
61
|
+
# @!method get
|
62
|
+
# @!macro atomic_reference_method_get
|
63
|
+
# Gets the current value.
|
64
|
+
# @return [Object] the current value
|
65
|
+
#
|
66
|
+
# @!method set(new_value)
|
67
|
+
# @!macro atomic_reference_method_set
|
68
|
+
# Sets to the given value.
|
69
|
+
# @param [Object] new_value the new value
|
70
|
+
# @return [Object] the new value
|
71
|
+
#
|
72
|
+
# @!method get_and_set(new_value)
|
73
|
+
# @!macro atomic_reference_method_get_and_set
|
74
|
+
# Atomically sets to the given value and returns the old value.
|
75
|
+
# @param [Object] new_value the new value
|
76
|
+
# @return [Object] the old value
|
77
|
+
#
|
78
|
+
# @!method compare_and_set(old_value, new_value)
|
79
|
+
# @!macro atomic_reference_method_compare_and_set
|
80
|
+
#
|
81
|
+
# Atomically sets the value to the given updated value if
|
82
|
+
# the current value == the expected value.
|
83
|
+
#
|
84
|
+
# @param [Object] old_value the expected value
|
85
|
+
# @param [Object] new_value the new value
|
86
|
+
#
|
87
|
+
# @return [Boolean] `true` if successful. A `false` return indicates
|
88
|
+
# that the actual value was not equal to the expected value.
|
89
|
+
#
|
90
|
+
# @!method update
|
91
|
+
# Pass the current value to the given block, replacing it
|
92
|
+
# with the block's result. May retry if the value changes
|
93
|
+
# during the block's execution.
|
94
|
+
#
|
95
|
+
# @yield [Object] Calculate a new value for the atomic reference using
|
96
|
+
# given (old) value
|
97
|
+
# @yieldparam [Object] old_value the starting value of the atomic reference
|
98
|
+
# @return [Object] the new value
|
99
|
+
#
|
100
|
+
# @!method try_update
|
101
|
+
# Pass the current value to the given block, replacing it
|
102
|
+
# with the block's result. Return nil if the update fails.
|
103
|
+
#
|
104
|
+
# @yield [Object] Calculate a new value for the atomic reference using
|
105
|
+
# given (old) value
|
106
|
+
# @yieldparam [Object] old_value the starting value of the atomic reference
|
107
|
+
# @note This method was altered to avoid raising an exception by default.
|
108
|
+
# Instead, this method now returns `nil` in case of failure. For more info,
|
109
|
+
# please see: https://github.com/ruby-concurrency/concurrent-ruby/pull/336
|
110
|
+
# @return [Object] the new value, or nil if update failed
|
111
|
+
#
|
112
|
+
# @!method try_update!
|
113
|
+
# Pass the current value to the given block, replacing it
|
114
|
+
# with the block's result. Raise an exception if the update
|
115
|
+
# fails.
|
116
|
+
#
|
117
|
+
# @yield [Object] Calculate a new value for the atomic reference using
|
118
|
+
# given (old) value
|
119
|
+
# @yieldparam [Object] old_value the starting value of the atomic reference
|
120
|
+
# @note This behavior mimics the behavior of the original
|
121
|
+
# `AtomicReference#try_update` API. The reason this was changed was to
|
122
|
+
# avoid raising exceptions (which are inherently slow) by default. For more
|
123
|
+
# info: https://github.com/ruby-concurrency/concurrent-ruby/pull/336
|
124
|
+
# @return [Object] the new value
|
125
|
+
# @raise [Concurrent::ConcurrentUpdateError] if the update fails
|
195
126
|
class AtomicReference < AtomicReferenceImplementation
|
196
127
|
|
197
128
|
# @return [String] Short string representation.
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'thread'
|
2
|
-
require 'concurrent/synchronization'
|
2
|
+
require 'concurrent/synchronization/lockable_object'
|
3
3
|
|
4
4
|
module Concurrent
|
5
5
|
|
@@ -19,7 +19,7 @@ module Concurrent
|
|
19
19
|
# t1 = Thread.new do
|
20
20
|
# puts "t1 is waiting"
|
21
21
|
# event.wait(1)
|
22
|
-
# puts "event
|
22
|
+
# puts "event occurred"
|
23
23
|
# end
|
24
24
|
#
|
25
25
|
# t2 = Thread.new do
|
@@ -30,8 +30,8 @@ module Concurrent
|
|
30
30
|
# [t1, t2].each(&:join)
|
31
31
|
#
|
32
32
|
# # prints:
|
33
|
-
# # t2 calling set
|
34
33
|
# # t1 is waiting
|
34
|
+
# # t2 calling set
|
35
35
|
# # event occurred
|
36
36
|
class Event < Synchronization::LockableObject
|
37
37
|
|
@@ -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
|