1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
|
require 'concurrent/synchronization'
require 'concurrent/utility/engine'
require 'concurrent/atomic_reference/numeric_cas_wrapper'
# Shim for TruffleRuby::AtomicReference
if Concurrent.on_truffleruby? && !defined?(TruffleRuby::AtomicReference)
# @!visibility private
module TruffleRuby
AtomicReference = Truffle::AtomicReference
end
end
module Concurrent
# Define update methods that use direct paths
#
# @!visibility private
# @!macro internal_implementation_note
module AtomicDirectUpdate
# @!macro atomic_reference_method_update
#
# Pass the current value to the given block, replacing it
# with the block's result. May retry if the value changes
# during the block's execution.
#
# @yield [Object] Calculate a new value for the atomic reference using
# given (old) value
# @yieldparam [Object] old_value the starting value of the atomic reference
# @return [Object] the new value
def update
true until compare_and_set(old_value = get, new_value = yield(old_value))
new_value
end
# @!macro atomic_reference_method_try_update
#
# Pass the current value to the given block, replacing it
# with the block's result. Return nil if the update fails.
#
# @yield [Object] Calculate a new value for the atomic reference using
# given (old) value
# @yieldparam [Object] old_value the starting value of the atomic reference
# @note This method was altered to avoid raising an exception by default.
# Instead, this method now returns `nil` in case of failure. For more info,
# please see: https://github.com/ruby-concurrency/concurrent-ruby/pull/336
# @return [Object] the new value, or nil if update failed
def try_update
old_value = get
new_value = yield old_value
return unless compare_and_set old_value, new_value
new_value
end
# @!macro atomic_reference_method_try_update!
#
# Pass the current value to the given block, replacing it
# with the block's result. Raise an exception if the update
# fails.
#
# @yield [Object] Calculate a new value for the atomic reference using
# given (old) value
# @yieldparam [Object] old_value the starting value of the atomic reference
# @note This behavior mimics the behavior of the original
# `AtomicReference#try_update` API. The reason this was changed was to
# avoid raising exceptions (which are inherently slow) by default. For more
# info: https://github.com/ruby-concurrency/concurrent-ruby/pull/336
# @return [Object] the new value
# @raise [Concurrent::ConcurrentUpdateError] if the update fails
def try_update!
old_value = get
new_value = yield old_value
unless compare_and_set(old_value, new_value)
if $VERBOSE
raise ConcurrentUpdateError, "Update failed"
else
raise ConcurrentUpdateError, "Update failed", ConcurrentUpdateError::CONC_UP_ERR_BACKTRACE
end
end
new_value
end
end
require 'concurrent/atomic_reference/mutex_atomic'
# @!macro atomic_reference
#
# An object reference that may be updated atomically. All read and write
# operations have java volatile semantic.
#
# @!macro thread_safe_variable_comparison
#
# @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
# @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html
#
# @!method initialize(value = nil)
# @!macro atomic_reference_method_initialize
# @param [Object] value The initial value.
#
# @!method get
# @!macro atomic_reference_method_get
# Gets the current value.
# @return [Object] the current value
#
# @!method set(new_value)
# @!macro atomic_reference_method_set
# Sets to the given value.
# @param [Object] new_value the new value
# @return [Object] the new value
#
# @!method get_and_set(new_value)
# @!macro atomic_reference_method_get_and_set
# Atomically sets to the given value and returns the old value.
# @param [Object] new_value the new value
# @return [Object] the old value
#
# @!method compare_and_set(old_value, new_value)
# @!macro atomic_reference_method_compare_and_set
#
# Atomically sets the value to the given updated value if
# the current value == the expected value.
#
# @param [Object] old_value the expected value
# @param [Object] new_value the new value
#
# @return [Boolean] `true` if successful. A `false` return indicates
# that the actual value was not equal to the expected value.
#
# @!method update
# @!macro atomic_reference_method_update
#
# @!method try_update
# @!macro atomic_reference_method_try_update
#
# @!method try_update!
# @!macro atomic_reference_method_try_update!
# @!macro internal_implementation_note
class ConcurrentUpdateError < ThreadError
# frozen pre-allocated backtrace to speed ConcurrentUpdateError
CONC_UP_ERR_BACKTRACE = ['backtrace elided; set verbose to enable'].freeze
end
# @!macro internal_implementation_note
AtomicReferenceImplementation = case
when Concurrent.on_cruby? && Concurrent.c_extensions_loaded?
# @!visibility private
# @!macro internal_implementation_note
class CAtomicReference
include AtomicDirectUpdate
include AtomicNumericCompareAndSetWrapper
alias_method :compare_and_swap, :compare_and_set
end
CAtomicReference
when Concurrent.on_jruby?
# @!visibility private
# @!macro internal_implementation_note
class JavaAtomicReference
include AtomicDirectUpdate
end
JavaAtomicReference
when Concurrent.on_truffleruby?
class TruffleRubyAtomicReference < TruffleRuby::AtomicReference
include AtomicDirectUpdate
alias_method :value, :get
alias_method :value=, :set
alias_method :compare_and_swap, :compare_and_set
alias_method :swap, :get_and_set
end
when Concurrent.on_rbx?
# @note Extends `Rubinius::AtomicReference` version adding aliases
# and numeric logic.
#
# @!visibility private
# @!macro internal_implementation_note
class RbxAtomicReference < Rubinius::AtomicReference
alias_method :_compare_and_set, :compare_and_set
include AtomicDirectUpdate
include AtomicNumericCompareAndSetWrapper
alias_method :value, :get
alias_method :value=, :set
alias_method :swap, :get_and_set
alias_method :compare_and_swap, :compare_and_set
end
RbxAtomicReference
else
MutexAtomicReference
end
private_constant :AtomicReferenceImplementation
# @!macro atomic_reference
class AtomicReference < AtomicReferenceImplementation
# @return [String] Short string representation.
def to_s
format '%s value:%s>', super[0..-2], get
end
alias_method :inspect, :to_s
end
end
|