concurrent-ruby 1.0.5 → 1.1.0.pre1
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 +5 -5
- data/CHANGELOG.md +42 -0
- data/Gemfile +39 -0
- data/{LICENSE.txt → LICENSE.md} +2 -0
- data/README.md +203 -105
- data/Rakefile +278 -0
- data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +159 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +304 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
- data/lib/concurrent-ruby.rb +1 -0
- data/lib/concurrent.rb +24 -20
- data/lib/concurrent/agent.rb +7 -7
- data/lib/concurrent/array.rb +59 -32
- data/lib/concurrent/async.rb +4 -4
- data/lib/concurrent/atom.rb +9 -9
- data/lib/concurrent/atomic/atomic_boolean.rb +24 -20
- data/lib/concurrent/atomic/atomic_fixnum.rb +27 -23
- data/lib/concurrent/atomic/atomic_markable_reference.rb +164 -0
- data/lib/concurrent/atomic/atomic_reference.rb +176 -33
- data/lib/concurrent/atomic/count_down_latch.rb +6 -6
- data/lib/concurrent/atomic/cyclic_barrier.rb +1 -1
- data/lib/concurrent/atomic/event.rb +1 -1
- data/lib/concurrent/atomic/java_count_down_latch.rb +6 -5
- data/lib/concurrent/atomic/mutex_count_down_latch.rb +1 -0
- data/lib/concurrent/atomic/read_write_lock.rb +2 -1
- data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
- data/lib/concurrent/atomic/semaphore.rb +8 -8
- data/lib/concurrent/atomic/thread_local_var.rb +7 -7
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +3 -8
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +1 -1
- data/lib/concurrent/atomics.rb +0 -43
- data/lib/concurrent/collection/lock_free_stack.rb +127 -0
- data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +3 -3
- data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +1 -2
- data/lib/concurrent/collection/non_concurrent_priority_queue.rb +29 -29
- data/lib/concurrent/concern/dereferenceable.rb +1 -1
- data/lib/concurrent/concern/logging.rb +6 -1
- data/lib/concurrent/concern/observable.rb +7 -7
- data/lib/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent/configuration.rb +1 -6
- data/lib/concurrent/constants.rb +1 -1
- data/lib/concurrent/dataflow.rb +2 -1
- data/lib/concurrent/delay.rb +9 -7
- data/lib/concurrent/exchanger.rb +13 -21
- data/lib/concurrent/executor/abstract_executor_service.rb +2 -2
- data/lib/concurrent/executor/cached_thread_pool.rb +1 -1
- data/lib/concurrent/executor/executor_service.rb +15 -15
- data/lib/concurrent/executor/fixed_thread_pool.rb +18 -18
- data/lib/concurrent/executor/java_thread_pool_executor.rb +10 -7
- data/lib/concurrent/executor/single_thread_executor.rb +2 -2
- data/lib/concurrent/executor/thread_pool_executor.rb +6 -6
- data/lib/concurrent/executor/timer_set.rb +1 -1
- data/lib/concurrent/future.rb +4 -1
- data/lib/concurrent/hash.rb +53 -30
- data/lib/concurrent/ivar.rb +5 -6
- data/lib/concurrent/map.rb +20 -25
- data/lib/concurrent/maybe.rb +1 -1
- data/lib/concurrent/mutable_struct.rb +15 -14
- data/lib/concurrent/mvar.rb +2 -2
- data/lib/concurrent/promise.rb +53 -21
- data/lib/concurrent/promises.rb +1938 -0
- data/lib/concurrent/re_include.rb +58 -0
- data/lib/concurrent/set.rb +66 -0
- data/lib/concurrent/settable_struct.rb +1 -0
- data/lib/concurrent/synchronization.rb +4 -5
- data/lib/concurrent/synchronization/abstract_lockable_object.rb +5 -5
- data/lib/concurrent/synchronization/abstract_struct.rb +6 -4
- data/lib/concurrent/synchronization/lockable_object.rb +6 -6
- data/lib/concurrent/synchronization/{mri_lockable_object.rb → mutex_lockable_object.rb} +19 -14
- data/lib/concurrent/synchronization/object.rb +8 -4
- data/lib/concurrent/synchronization/truffleruby_object.rb +46 -0
- data/lib/concurrent/synchronization/volatile.rb +11 -9
- data/lib/concurrent/thread_safe/util/data_structures.rb +55 -0
- data/lib/concurrent/thread_safe/util/striped64.rb +9 -4
- data/lib/concurrent/timer_task.rb +5 -2
- data/lib/concurrent/tuple.rb +1 -1
- data/lib/concurrent/tvar.rb +2 -2
- data/lib/concurrent/utility/at_exit.rb +1 -1
- data/lib/concurrent/utility/engine.rb +2 -2
- data/lib/concurrent/utility/monotonic_time.rb +3 -3
- data/lib/concurrent/utility/native_extension_loader.rb +31 -33
- data/lib/concurrent/utility/processor_counter.rb +0 -2
- data/lib/concurrent/version.rb +2 -2
- metadata +35 -21
- data/lib/concurrent/atomic_reference/concurrent_update_error.rb +0 -8
- data/lib/concurrent/atomic_reference/direct_update.rb +0 -81
- data/lib/concurrent/atomic_reference/jruby+truffle.rb +0 -2
- data/lib/concurrent/atomic_reference/jruby.rb +0 -16
- data/lib/concurrent/atomic_reference/rbx.rb +0 -22
- data/lib/concurrent/atomic_reference/ruby.rb +0 -32
- data/lib/concurrent/edge.rb +0 -26
- data/lib/concurrent/lazy_register.rb +0 -81
- data/lib/concurrent/synchronization/truffle_lockable_object.rb +0 -9
- data/lib/concurrent/synchronization/truffle_object.rb +0 -31
- data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +0 -30
data/lib/concurrent/async.rb
CHANGED
@@ -159,7 +159,7 @@ module Concurrent
|
|
159
159
|
#
|
160
160
|
# To get the state *at the current* time, irrespective of an enqueued method
|
161
161
|
# calls, a reader method must be called directly. This is inherently unsafe
|
162
|
-
# unless the instance variable is itself thread-safe,
|
162
|
+
# unless the instance variable is itself thread-safe, preferably using one
|
163
163
|
# of the thread-safe classes within this library. Because the thread-safe
|
164
164
|
# classes within this library are internally-locking or non-locking, they can
|
165
165
|
# be safely used from within asynchronous methods without causing deadlocks.
|
@@ -188,7 +188,7 @@ module Concurrent
|
|
188
188
|
#
|
189
189
|
# Class variables should be avoided. Class variables represent shared state.
|
190
190
|
# Shared state is anathema to concurrency. Should there be a need to share
|
191
|
-
# state using class variables they *must* be thread-safe,
|
191
|
+
# state using class variables they *must* be thread-safe, preferably
|
192
192
|
# using the thread-safe classes within this library. When updating class
|
193
193
|
# variables, never assign a new value/object to the variable itself. Assignment
|
194
194
|
# is not thread-safe in Ruby. Instead, use the thread-safe update functions
|
@@ -392,7 +392,7 @@ module Concurrent
|
|
392
392
|
# object's thread. The final disposition of the method call can be obtained
|
393
393
|
# by inspecting the returned future.
|
394
394
|
#
|
395
|
-
# @!macro
|
395
|
+
# @!macro async_thread_safety_warning
|
396
396
|
# @note The method call is guaranteed to be thread safe with respect to
|
397
397
|
# all other method calls against the same object that are called with
|
398
398
|
# either `async` or `await`. The mutable nature of Ruby references
|
@@ -435,7 +435,7 @@ module Concurrent
|
|
435
435
|
#
|
436
436
|
# @!visibility private
|
437
437
|
def init_synchronization
|
438
|
-
return self if @__async_initialized__
|
438
|
+
return self if defined?(@__async_initialized__) && @__async_initialized__
|
439
439
|
@__async_initialized__ = true
|
440
440
|
@__async_delegator__ = AsyncDelegator.new(self)
|
441
441
|
@__await_delegator__ = AwaitDelegator.new(@__async_delegator__)
|
data/lib/concurrent/atom.rb
CHANGED
@@ -3,7 +3,7 @@ require 'concurrent/collection/copy_on_notify_observer_set'
|
|
3
3
|
require 'concurrent/concern/observable'
|
4
4
|
require 'concurrent/synchronization'
|
5
5
|
|
6
|
-
# @!macro
|
6
|
+
# @!macro thread_safe_variable_comparison
|
7
7
|
#
|
8
8
|
# ## Thread-safe Variable Classes
|
9
9
|
#
|
@@ -96,8 +96,15 @@ module Concurrent
|
|
96
96
|
include Concern::Observable
|
97
97
|
|
98
98
|
safe_initialization!
|
99
|
-
|
99
|
+
attr_atomic(:value)
|
100
|
+
private :value=, :swap_value, :compare_and_set_value, :update_value
|
100
101
|
public :value
|
102
|
+
alias_method :deref, :value
|
103
|
+
|
104
|
+
# @!method value
|
105
|
+
# The current value of the atom.
|
106
|
+
#
|
107
|
+
# @return [Object] The current value.
|
101
108
|
|
102
109
|
# Create a new atom with the given initial value.
|
103
110
|
#
|
@@ -118,13 +125,6 @@ module Concurrent
|
|
118
125
|
self.value = value
|
119
126
|
end
|
120
127
|
|
121
|
-
# @!method value
|
122
|
-
# The current value of the atom.
|
123
|
-
#
|
124
|
-
# @return [Object] The current value.
|
125
|
-
|
126
|
-
alias_method :deref, :value
|
127
|
-
|
128
128
|
# Atomically swaps the value of atom using the given block. The current
|
129
129
|
# value will be passed to the block, as will any arguments passed as
|
130
130
|
# arguments to the function. The new value will be validated against the
|
@@ -5,19 +5,19 @@ module Concurrent
|
|
5
5
|
|
6
6
|
###################################################################
|
7
7
|
|
8
|
-
# @!macro
|
8
|
+
# @!macro atomic_boolean_method_initialize
|
9
9
|
#
|
10
10
|
# Creates a new `AtomicBoolean` with the given initial value.
|
11
11
|
#
|
12
12
|
# @param [Boolean] initial the initial value
|
13
13
|
|
14
|
-
# @!macro
|
14
|
+
# @!macro atomic_boolean_method_value_get
|
15
15
|
#
|
16
16
|
# Retrieves the current `Boolean` value.
|
17
17
|
#
|
18
18
|
# @return [Boolean] the current value
|
19
19
|
|
20
|
-
# @!macro
|
20
|
+
# @!macro atomic_boolean_method_value_set
|
21
21
|
#
|
22
22
|
# Explicitly sets the value.
|
23
23
|
#
|
@@ -25,25 +25,25 @@ module Concurrent
|
|
25
25
|
#
|
26
26
|
# @return [Boolean] the current value
|
27
27
|
|
28
|
-
# @!macro
|
28
|
+
# @!macro atomic_boolean_method_true_question
|
29
29
|
#
|
30
30
|
# Is the current value `true`
|
31
31
|
#
|
32
32
|
# @return [Boolean] true if the current value is `true`, else false
|
33
33
|
|
34
|
-
# @!macro
|
34
|
+
# @!macro atomic_boolean_method_false_question
|
35
35
|
#
|
36
36
|
# Is the current value `false`
|
37
37
|
#
|
38
38
|
# @return [Boolean] true if the current value is `false`, else false
|
39
39
|
|
40
|
-
# @!macro
|
40
|
+
# @!macro atomic_boolean_method_make_true
|
41
41
|
#
|
42
42
|
# Explicitly sets the value to true.
|
43
43
|
#
|
44
44
|
# @return [Boolean] true is value has changed, otherwise false
|
45
45
|
|
46
|
-
# @!macro
|
46
|
+
# @!macro atomic_boolean_method_make_false
|
47
47
|
#
|
48
48
|
# Explicitly sets the value to false.
|
49
49
|
#
|
@@ -51,7 +51,7 @@ module Concurrent
|
|
51
51
|
|
52
52
|
###################################################################
|
53
53
|
|
54
|
-
# @!macro
|
54
|
+
# @!macro atomic_boolean_public_api
|
55
55
|
#
|
56
56
|
# @!method initialize(initial = false)
|
57
57
|
# @!macro atomic_boolean_method_initialize
|
@@ -88,7 +88,7 @@ module Concurrent
|
|
88
88
|
end
|
89
89
|
private_constant :AtomicBooleanImplementation
|
90
90
|
|
91
|
-
# @!macro
|
91
|
+
# @!macro atomic_boolean
|
92
92
|
#
|
93
93
|
# A boolean value that can be updated atomically. Reads and writes to an atomic
|
94
94
|
# boolean and thread-safe and guaranteed to succeed. Reads and writes may block
|
@@ -96,17 +96,21 @@ module Concurrent
|
|
96
96
|
#
|
97
97
|
# @!macro thread_safe_variable_comparison
|
98
98
|
#
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
99
|
+
# Performance:
|
100
|
+
#
|
101
|
+
# ```
|
102
|
+
# Testing with ruby 2.1.2
|
103
|
+
# Testing with Concurrent::MutexAtomicBoolean...
|
104
|
+
# 2.790000 0.000000 2.790000 ( 2.791454)
|
105
|
+
# Testing with Concurrent::CAtomicBoolean...
|
106
|
+
# 0.740000 0.000000 0.740000 ( 0.740206)
|
104
107
|
#
|
105
|
-
#
|
106
|
-
#
|
107
|
-
#
|
108
|
-
#
|
109
|
-
#
|
108
|
+
# Testing with jruby 1.9.3
|
109
|
+
# Testing with Concurrent::MutexAtomicBoolean...
|
110
|
+
# 5.240000 2.520000 7.760000 ( 3.683000)
|
111
|
+
# Testing with Concurrent::JavaAtomicBoolean...
|
112
|
+
# 3.340000 0.010000 3.350000 ( 0.855000)
|
113
|
+
# ```
|
110
114
|
#
|
111
115
|
# @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicBoolean.html java.util.concurrent.atomic.AtomicBoolean
|
112
116
|
#
|
@@ -114,7 +118,7 @@ module Concurrent
|
|
114
118
|
class AtomicBoolean < AtomicBooleanImplementation
|
115
119
|
# @return [String] Short string representation.
|
116
120
|
def to_s
|
117
|
-
format '
|
121
|
+
format '%s value:%s>', super[0..-2], value
|
118
122
|
end
|
119
123
|
|
120
124
|
alias_method :inspect, :to_s
|
@@ -5,20 +5,20 @@ module Concurrent
|
|
5
5
|
|
6
6
|
###################################################################
|
7
7
|
|
8
|
-
# @!macro
|
8
|
+
# @!macro atomic_fixnum_method_initialize
|
9
9
|
#
|
10
10
|
# Creates a new `AtomicFixnum` with the given initial value.
|
11
11
|
#
|
12
12
|
# @param [Fixnum] initial the initial value
|
13
13
|
# @raise [ArgumentError] if the initial value is not a `Fixnum`
|
14
14
|
|
15
|
-
# @!macro
|
15
|
+
# @!macro atomic_fixnum_method_value_get
|
16
16
|
#
|
17
17
|
# Retrieves the current `Fixnum` value.
|
18
18
|
#
|
19
19
|
# @return [Fixnum] the current value
|
20
20
|
|
21
|
-
# @!macro
|
21
|
+
# @!macro atomic_fixnum_method_value_set
|
22
22
|
#
|
23
23
|
# Explicitly sets the value.
|
24
24
|
#
|
@@ -28,7 +28,7 @@ module Concurrent
|
|
28
28
|
#
|
29
29
|
# @raise [ArgumentError] if the new value is not a `Fixnum`
|
30
30
|
|
31
|
-
# @!macro
|
31
|
+
# @!macro atomic_fixnum_method_increment
|
32
32
|
#
|
33
33
|
# Increases the current value by the given amount (defaults to 1).
|
34
34
|
#
|
@@ -36,7 +36,7 @@ module Concurrent
|
|
36
36
|
#
|
37
37
|
# @return [Fixnum] the current value after incrementation
|
38
38
|
|
39
|
-
# @!macro
|
39
|
+
# @!macro atomic_fixnum_method_decrement
|
40
40
|
#
|
41
41
|
# Decreases the current value by the given amount (defaults to 1).
|
42
42
|
#
|
@@ -44,7 +44,7 @@ module Concurrent
|
|
44
44
|
#
|
45
45
|
# @return [Fixnum] the current value after decrementation
|
46
46
|
|
47
|
-
# @!macro
|
47
|
+
# @!macro atomic_fixnum_method_compare_and_set
|
48
48
|
#
|
49
49
|
# Atomically sets the value to the given updated value if the current
|
50
50
|
# value == the expected value.
|
@@ -52,9 +52,9 @@ module Concurrent
|
|
52
52
|
# @param [Fixnum] expect the expected value
|
53
53
|
# @param [Fixnum] update the new value
|
54
54
|
#
|
55
|
-
# @return [
|
55
|
+
# @return [Boolean] true if the value was updated else false
|
56
56
|
|
57
|
-
# @!macro
|
57
|
+
# @!macro atomic_fixnum_method_update
|
58
58
|
#
|
59
59
|
# Pass the current value to the given block, replacing it
|
60
60
|
# with the block's result. May retry if the value changes
|
@@ -68,7 +68,7 @@ module Concurrent
|
|
68
68
|
|
69
69
|
###################################################################
|
70
70
|
|
71
|
-
# @!macro
|
71
|
+
# @!macro atomic_fixnum_public_api
|
72
72
|
#
|
73
73
|
# @!method initialize(initial = 0)
|
74
74
|
# @!macro atomic_fixnum_method_initialize
|
@@ -79,10 +79,10 @@ module Concurrent
|
|
79
79
|
# @!method value=(value)
|
80
80
|
# @!macro atomic_fixnum_method_value_set
|
81
81
|
#
|
82
|
-
# @!method increment
|
82
|
+
# @!method increment(delta)
|
83
83
|
# @!macro atomic_fixnum_method_increment
|
84
84
|
#
|
85
|
-
# @!method decrement
|
85
|
+
# @!method decrement(delta)
|
86
86
|
# @!macro atomic_fixnum_method_decrement
|
87
87
|
#
|
88
88
|
# @!method compare_and_set(expect, update)
|
@@ -105,7 +105,7 @@ module Concurrent
|
|
105
105
|
end
|
106
106
|
private_constant :AtomicFixnumImplementation
|
107
107
|
|
108
|
-
# @!macro
|
108
|
+
# @!macro atomic_fixnum
|
109
109
|
#
|
110
110
|
# A numeric value that can be updated atomically. Reads and writes to an atomic
|
111
111
|
# fixnum and thread-safe and guaranteed to succeed. Reads and writes may block
|
@@ -113,17 +113,21 @@ module Concurrent
|
|
113
113
|
#
|
114
114
|
# @!macro thread_safe_variable_comparison
|
115
115
|
#
|
116
|
-
#
|
117
|
-
#
|
118
|
-
#
|
119
|
-
#
|
120
|
-
#
|
116
|
+
# Performance:
|
117
|
+
#
|
118
|
+
# ```
|
119
|
+
# Testing with ruby 2.1.2
|
120
|
+
# Testing with Concurrent::MutexAtomicFixnum...
|
121
|
+
# 3.130000 0.000000 3.130000 ( 3.136505)
|
122
|
+
# Testing with Concurrent::CAtomicFixnum...
|
123
|
+
# 0.790000 0.000000 0.790000 ( 0.785550)
|
121
124
|
#
|
122
|
-
#
|
123
|
-
#
|
124
|
-
#
|
125
|
-
#
|
126
|
-
#
|
125
|
+
# Testing with jruby 1.9.3
|
126
|
+
# Testing with Concurrent::MutexAtomicFixnum...
|
127
|
+
# 5.460000 2.460000 7.920000 ( 3.715000)
|
128
|
+
# Testing with Concurrent::JavaAtomicFixnum...
|
129
|
+
# 4.520000 0.030000 4.550000 ( 1.187000)
|
130
|
+
# ```
|
127
131
|
#
|
128
132
|
# @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicLong.html java.util.concurrent.atomic.AtomicLong
|
129
133
|
#
|
@@ -131,7 +135,7 @@ module Concurrent
|
|
131
135
|
class AtomicFixnum < AtomicFixnumImplementation
|
132
136
|
# @return [String] Short string representation.
|
133
137
|
def to_s
|
134
|
-
format '
|
138
|
+
format '%s value:%s>', super[0..-2], value
|
135
139
|
end
|
136
140
|
|
137
141
|
alias_method :inspect, :to_s
|
@@ -0,0 +1,164 @@
|
|
1
|
+
module Concurrent
|
2
|
+
# An atomic reference which maintains an object reference along with a mark bit
|
3
|
+
# that can be updated atomically.
|
4
|
+
#
|
5
|
+
# @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicMarkableReference.html
|
6
|
+
# java.util.concurrent.atomic.AtomicMarkableReference
|
7
|
+
class AtomicMarkableReference < ::Concurrent::Synchronization::Object
|
8
|
+
|
9
|
+
attr_atomic(:reference)
|
10
|
+
private :reference, :reference=, :swap_reference, :compare_and_set_reference, :update_reference
|
11
|
+
|
12
|
+
def initialize(value = nil, mark = false)
|
13
|
+
super()
|
14
|
+
self.reference = immutable_array(value, mark)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Atomically sets the value and mark to the given updated value and
|
18
|
+
# mark given both:
|
19
|
+
# - the current value == the expected value &&
|
20
|
+
# - the current mark == the expected mark
|
21
|
+
#
|
22
|
+
# @param [Object] expected_val the expected value
|
23
|
+
# @param [Object] new_val the new value
|
24
|
+
# @param [Boolean] expected_mark the expected mark
|
25
|
+
# @param [Boolean] new_mark the new mark
|
26
|
+
#
|
27
|
+
# @return [Boolean] `true` if successful. A `false` return indicates
|
28
|
+
# that the actual value was not equal to the expected value or the
|
29
|
+
# actual mark was not equal to the expected mark
|
30
|
+
def compare_and_set(expected_val, new_val, expected_mark, new_mark)
|
31
|
+
# Memoize a valid reference to the current AtomicReference for
|
32
|
+
# later comparison.
|
33
|
+
current = reference
|
34
|
+
curr_val, curr_mark = current
|
35
|
+
|
36
|
+
# Ensure that that the expected marks match.
|
37
|
+
return false unless expected_mark == curr_mark
|
38
|
+
|
39
|
+
if expected_val.is_a? Numeric
|
40
|
+
# If the object is a numeric, we need to ensure we are comparing
|
41
|
+
# the numerical values
|
42
|
+
return false unless expected_val == curr_val
|
43
|
+
else
|
44
|
+
# Otherwise, we need to ensure we are comparing the object identity.
|
45
|
+
# Theoretically, this could be incorrect if a user monkey-patched
|
46
|
+
# `Object#equal?`, but they should know that they are playing with
|
47
|
+
# fire at that point.
|
48
|
+
return false unless expected_val.equal? curr_val
|
49
|
+
end
|
50
|
+
|
51
|
+
prospect = immutable_array(new_val, new_mark)
|
52
|
+
|
53
|
+
compare_and_set_reference current, prospect
|
54
|
+
end
|
55
|
+
|
56
|
+
alias_method :compare_and_swap, :compare_and_set
|
57
|
+
|
58
|
+
# Gets the current reference and marked values.
|
59
|
+
#
|
60
|
+
# @return [Array] the current reference and marked values
|
61
|
+
def get
|
62
|
+
reference
|
63
|
+
end
|
64
|
+
|
65
|
+
# Gets the current value of the reference
|
66
|
+
#
|
67
|
+
# @return [Object] the current value of the reference
|
68
|
+
def value
|
69
|
+
reference[0]
|
70
|
+
end
|
71
|
+
|
72
|
+
# Gets the current marked value
|
73
|
+
#
|
74
|
+
# @return [Boolean] the current marked value
|
75
|
+
def mark
|
76
|
+
reference[1]
|
77
|
+
end
|
78
|
+
|
79
|
+
alias_method :marked?, :mark
|
80
|
+
|
81
|
+
# _Unconditionally_ sets to the given value of both the reference and
|
82
|
+
# the mark.
|
83
|
+
#
|
84
|
+
# @param [Object] new_val the new value
|
85
|
+
# @param [Boolean] new_mark the new mark
|
86
|
+
#
|
87
|
+
# @return [Array] both the new value and the new mark
|
88
|
+
def set(new_val, new_mark)
|
89
|
+
self.reference = immutable_array(new_val, new_mark)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Pass the current value and marked state to the given block, replacing it
|
93
|
+
# with the block's results. May retry if the value changes during the
|
94
|
+
# block's execution.
|
95
|
+
#
|
96
|
+
# @yield [Object] Calculate a new value and marked state for the atomic
|
97
|
+
# reference using given (old) value and (old) marked
|
98
|
+
# @yieldparam [Object] old_val the starting value of the atomic reference
|
99
|
+
# @yieldparam [Boolean] old_mark the starting state of marked
|
100
|
+
#
|
101
|
+
# @return [Array] the new value and new mark
|
102
|
+
def update
|
103
|
+
loop do
|
104
|
+
old_val, old_mark = reference
|
105
|
+
new_val, new_mark = yield old_val, old_mark
|
106
|
+
|
107
|
+
if compare_and_set old_val, new_val, old_mark, new_mark
|
108
|
+
return immutable_array(new_val, new_mark)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
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 and marked state for the atomic
|
118
|
+
# reference using given (old) value and (old) marked
|
119
|
+
# @yieldparam [Object] old_val the starting value of the atomic reference
|
120
|
+
# @yieldparam [Boolean] old_mark the starting state of marked
|
121
|
+
#
|
122
|
+
# @return [Array] the new value and marked state
|
123
|
+
#
|
124
|
+
# @raise [Concurrent::ConcurrentUpdateError] if the update fails
|
125
|
+
def try_update!
|
126
|
+
old_val, old_mark = reference
|
127
|
+
new_val, new_mark = yield old_val, old_mark
|
128
|
+
|
129
|
+
unless compare_and_set old_val, new_val, old_mark, new_mark
|
130
|
+
fail ::Concurrent::ConcurrentUpdateError,
|
131
|
+
'AtomicMarkableReference: Update failed due to race condition.',
|
132
|
+
'Note: If you would like to guarantee an update, please use ' +
|
133
|
+
'the `AtomicMarkableReference#update` method.'
|
134
|
+
end
|
135
|
+
|
136
|
+
immutable_array(new_val, new_mark)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Pass the current value to the given block, replacing it with the
|
140
|
+
# block's result. Simply return nil if update fails.
|
141
|
+
#
|
142
|
+
# @yield [Object] Calculate a new value and marked state for the atomic
|
143
|
+
# reference using given (old) value and (old) marked
|
144
|
+
# @yieldparam [Object] old_val the starting value of the atomic reference
|
145
|
+
# @yieldparam [Boolean] old_mark the starting state of marked
|
146
|
+
#
|
147
|
+
# @return [Array] the new value and marked state, or nil if
|
148
|
+
# the update failed
|
149
|
+
def try_update
|
150
|
+
old_val, old_mark = reference
|
151
|
+
new_val, new_mark = yield old_val, old_mark
|
152
|
+
|
153
|
+
return unless compare_and_set old_val, new_val, old_mark, new_mark
|
154
|
+
|
155
|
+
immutable_array(new_val, new_mark)
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def immutable_array(*args)
|
161
|
+
args.freeze
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|