This commit is contained in:
+37
@@ -0,0 +1,37 @@
|
||||
require 'concurrent/errors'
|
||||
|
||||
module Concurrent
|
||||
|
||||
# Define update methods that use direct paths
|
||||
#
|
||||
# @!visibility private
|
||||
# @!macro internal_implementation_note
|
||||
module AtomicDirectUpdate
|
||||
def update
|
||||
true until compare_and_set(old_value = get, new_value = yield(old_value))
|
||||
new_value
|
||||
end
|
||||
|
||||
def try_update
|
||||
old_value = get
|
||||
new_value = yield old_value
|
||||
|
||||
return unless compare_and_set old_value, new_value
|
||||
|
||||
new_value
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
require 'concurrent/atomic_reference/atomic_direct_update'
|
||||
require 'concurrent/atomic_reference/numeric_cas_wrapper'
|
||||
require 'concurrent/synchronization/safe_initialization'
|
||||
|
||||
module Concurrent
|
||||
|
||||
# @!visibility private
|
||||
# @!macro internal_implementation_note
|
||||
class MutexAtomicReference
|
||||
extend Concurrent::Synchronization::SafeInitialization
|
||||
include AtomicDirectUpdate
|
||||
include AtomicNumericCompareAndSetWrapper
|
||||
|
||||
# @!macro atomic_reference_method_initialize
|
||||
def initialize(value = nil)
|
||||
super()
|
||||
@Lock = ::Mutex.new
|
||||
@value = value
|
||||
end
|
||||
|
||||
# @!macro atomic_reference_method_get
|
||||
def get
|
||||
synchronize { @value }
|
||||
end
|
||||
alias_method :value, :get
|
||||
|
||||
# @!macro atomic_reference_method_set
|
||||
def set(new_value)
|
||||
synchronize { @value = new_value }
|
||||
end
|
||||
alias_method :value=, :set
|
||||
|
||||
# @!macro atomic_reference_method_get_and_set
|
||||
def get_and_set(new_value)
|
||||
synchronize do
|
||||
old_value = @value
|
||||
@value = new_value
|
||||
old_value
|
||||
end
|
||||
end
|
||||
alias_method :swap, :get_and_set
|
||||
|
||||
# @!macro atomic_reference_method_compare_and_set
|
||||
private def _compare_and_set(old_value, new_value)
|
||||
synchronize do
|
||||
if @value.equal? old_value
|
||||
@value = new_value
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# @!visibility private
|
||||
def synchronize
|
||||
if @Lock.owned?
|
||||
yield
|
||||
else
|
||||
@Lock.synchronize { yield }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
module Concurrent
|
||||
|
||||
# Special "compare and set" handling of numeric values.
|
||||
#
|
||||
# @!visibility private
|
||||
# @!macro internal_implementation_note
|
||||
module AtomicNumericCompareAndSetWrapper
|
||||
|
||||
# @!macro atomic_reference_method_compare_and_set
|
||||
def compare_and_set(old_value, new_value)
|
||||
if old_value.kind_of? Numeric
|
||||
# NaN is never == to itself; match it explicitly so #update can terminate.
|
||||
expected_nan = old_value.respond_to?(:nan?) && old_value.nan?
|
||||
while true
|
||||
old = get
|
||||
|
||||
return false unless old.kind_of? Numeric
|
||||
|
||||
if expected_nan
|
||||
return false unless old.respond_to?(:nan?) && old.nan?
|
||||
else
|
||||
return false unless old == old_value
|
||||
end
|
||||
|
||||
result = _compare_and_set(old, new_value)
|
||||
return result if result
|
||||
end
|
||||
else
|
||||
_compare_and_set(old_value, new_value)
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :compare_and_swap, :compare_and_set
|
||||
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user