concurrent-ruby 1.0.5 → 1.1.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +42 -0
  3. data/Gemfile +39 -0
  4. data/{LICENSE.txt → LICENSE.md} +2 -0
  5. data/README.md +203 -105
  6. data/Rakefile +278 -0
  7. data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
  9. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
  10. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
  11. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
  12. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +159 -0
  13. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +304 -0
  14. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
  15. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
  16. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
  17. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
  18. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
  19. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
  20. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
  21. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
  22. data/lib/concurrent-ruby.rb +1 -0
  23. data/lib/concurrent.rb +24 -20
  24. data/lib/concurrent/agent.rb +7 -7
  25. data/lib/concurrent/array.rb +59 -32
  26. data/lib/concurrent/async.rb +4 -4
  27. data/lib/concurrent/atom.rb +9 -9
  28. data/lib/concurrent/atomic/atomic_boolean.rb +24 -20
  29. data/lib/concurrent/atomic/atomic_fixnum.rb +27 -23
  30. data/lib/concurrent/atomic/atomic_markable_reference.rb +164 -0
  31. data/lib/concurrent/atomic/atomic_reference.rb +176 -33
  32. data/lib/concurrent/atomic/count_down_latch.rb +6 -6
  33. data/lib/concurrent/atomic/cyclic_barrier.rb +1 -1
  34. data/lib/concurrent/atomic/event.rb +1 -1
  35. data/lib/concurrent/atomic/java_count_down_latch.rb +6 -5
  36. data/lib/concurrent/atomic/mutex_count_down_latch.rb +1 -0
  37. data/lib/concurrent/atomic/read_write_lock.rb +2 -1
  38. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
  39. data/lib/concurrent/atomic/semaphore.rb +8 -8
  40. data/lib/concurrent/atomic/thread_local_var.rb +7 -7
  41. data/lib/concurrent/atomic_reference/mutex_atomic.rb +3 -8
  42. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +1 -1
  43. data/lib/concurrent/atomics.rb +0 -43
  44. data/lib/concurrent/collection/lock_free_stack.rb +127 -0
  45. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +3 -3
  46. data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +1 -2
  47. data/lib/concurrent/collection/non_concurrent_priority_queue.rb +29 -29
  48. data/lib/concurrent/concern/dereferenceable.rb +1 -1
  49. data/lib/concurrent/concern/logging.rb +6 -1
  50. data/lib/concurrent/concern/observable.rb +7 -7
  51. data/lib/concurrent/concurrent_ruby.jar +0 -0
  52. data/lib/concurrent/configuration.rb +1 -6
  53. data/lib/concurrent/constants.rb +1 -1
  54. data/lib/concurrent/dataflow.rb +2 -1
  55. data/lib/concurrent/delay.rb +9 -7
  56. data/lib/concurrent/exchanger.rb +13 -21
  57. data/lib/concurrent/executor/abstract_executor_service.rb +2 -2
  58. data/lib/concurrent/executor/cached_thread_pool.rb +1 -1
  59. data/lib/concurrent/executor/executor_service.rb +15 -15
  60. data/lib/concurrent/executor/fixed_thread_pool.rb +18 -18
  61. data/lib/concurrent/executor/java_thread_pool_executor.rb +10 -7
  62. data/lib/concurrent/executor/single_thread_executor.rb +2 -2
  63. data/lib/concurrent/executor/thread_pool_executor.rb +6 -6
  64. data/lib/concurrent/executor/timer_set.rb +1 -1
  65. data/lib/concurrent/future.rb +4 -1
  66. data/lib/concurrent/hash.rb +53 -30
  67. data/lib/concurrent/ivar.rb +5 -6
  68. data/lib/concurrent/map.rb +20 -25
  69. data/lib/concurrent/maybe.rb +1 -1
  70. data/lib/concurrent/mutable_struct.rb +15 -14
  71. data/lib/concurrent/mvar.rb +2 -2
  72. data/lib/concurrent/promise.rb +53 -21
  73. data/lib/concurrent/promises.rb +1938 -0
  74. data/lib/concurrent/re_include.rb +58 -0
  75. data/lib/concurrent/set.rb +66 -0
  76. data/lib/concurrent/settable_struct.rb +1 -0
  77. data/lib/concurrent/synchronization.rb +4 -5
  78. data/lib/concurrent/synchronization/abstract_lockable_object.rb +5 -5
  79. data/lib/concurrent/synchronization/abstract_struct.rb +6 -4
  80. data/lib/concurrent/synchronization/lockable_object.rb +6 -6
  81. data/lib/concurrent/synchronization/{mri_lockable_object.rb → mutex_lockable_object.rb} +19 -14
  82. data/lib/concurrent/synchronization/object.rb +8 -4
  83. data/lib/concurrent/synchronization/truffleruby_object.rb +46 -0
  84. data/lib/concurrent/synchronization/volatile.rb +11 -9
  85. data/lib/concurrent/thread_safe/util/data_structures.rb +55 -0
  86. data/lib/concurrent/thread_safe/util/striped64.rb +9 -4
  87. data/lib/concurrent/timer_task.rb +5 -2
  88. data/lib/concurrent/tuple.rb +1 -1
  89. data/lib/concurrent/tvar.rb +2 -2
  90. data/lib/concurrent/utility/at_exit.rb +1 -1
  91. data/lib/concurrent/utility/engine.rb +2 -2
  92. data/lib/concurrent/utility/monotonic_time.rb +3 -3
  93. data/lib/concurrent/utility/native_extension_loader.rb +31 -33
  94. data/lib/concurrent/utility/processor_counter.rb +0 -2
  95. data/lib/concurrent/version.rb +2 -2
  96. metadata +35 -21
  97. data/lib/concurrent/atomic_reference/concurrent_update_error.rb +0 -8
  98. data/lib/concurrent/atomic_reference/direct_update.rb +0 -81
  99. data/lib/concurrent/atomic_reference/jruby+truffle.rb +0 -2
  100. data/lib/concurrent/atomic_reference/jruby.rb +0 -16
  101. data/lib/concurrent/atomic_reference/rbx.rb +0 -22
  102. data/lib/concurrent/atomic_reference/ruby.rb +0 -32
  103. data/lib/concurrent/edge.rb +0 -26
  104. data/lib/concurrent/lazy_register.rb +0 -81
  105. data/lib/concurrent/synchronization/truffle_lockable_object.rb +0 -9
  106. data/lib/concurrent/synchronization/truffle_object.rb +0 -31
  107. data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +0 -30
@@ -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, preferrably using one
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, preferrably
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 [attach] async_thread_safety_warning
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__)
@@ -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 [new] thread_safe_variable_comparison
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
- private(*attr_atomic(:value))
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 [new] atomic_boolean_method_initialize
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 [new] atomic_boolean_method_value_get
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 [new] atomic_boolean_method_value_set
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 [new] atomic_boolean_method_true_question
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 [new] atomic_boolean_method_false_question
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 [new] atomic_boolean_method_make_true
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 [new] atomic_boolean_method_make_false
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 [new] atomic_boolean_public_api
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 [attach] atomic_boolean
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
- # Testing with ruby 2.1.2
100
- # Testing with Concurrent::MutexAtomicBoolean...
101
- # 2.790000 0.000000 2.790000 ( 2.791454)
102
- # Testing with Concurrent::CAtomicBoolean...
103
- # 0.740000 0.000000 0.740000 ( 0.740206)
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
- # Testing with jruby 1.9.3
106
- # Testing with Concurrent::MutexAtomicBoolean...
107
- # 5.240000 2.520000 7.760000 ( 3.683000)
108
- # Testing with Concurrent::JavaAtomicBoolean...
109
- # 3.340000 0.010000 3.350000 ( 0.855000)
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 '<#%s:0x%x value:%s>', self.class, object_id << 1, value
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 [new] atomic_fixnum_method_initialize
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 [new] atomic_fixnum_method_value_get
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 [new] atomic_fixnum_method_value_set
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 [new] atomic_fixnum_method_increment
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 [new] atomic_fixnum_method_decrement
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 [new] atomic_fixnum_method_compare_and_set
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 [Fixnum] true if the value was updated else false
55
+ # @return [Boolean] true if the value was updated else false
56
56
 
57
- # @!macro [new] atomic_fixnum_method_update
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 [new] atomic_fixnum_public_api
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 [attach] atomic_fixnum
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
- # Testing with ruby 2.1.2
117
- # Testing with Concurrent::MutexAtomicFixnum...
118
- # 3.130000 0.000000 3.130000 ( 3.136505)
119
- # Testing with Concurrent::CAtomicFixnum...
120
- # 0.790000 0.000000 0.790000 ( 0.785550)
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
- # Testing with jruby 1.9.3
123
- # Testing with Concurrent::MutexAtomicFixnum...
124
- # 5.460000 2.460000 7.920000 ( 3.715000)
125
- # Testing with Concurrent::JavaAtomicFixnum...
126
- # 4.520000 0.030000 4.550000 ( 1.187000)
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 '<#%s:0x%x value:%s>', self.class, object_id << 1, value
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