require'concurrent/errors'require'concurrent/synchronization/object'moduleConcurrent# An atomic reference which maintains an object reference along with a mark bit# that can be updated atomically.## @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicMarkableReference.html# java.util.concurrent.atomic.AtomicMarkableReferenceclassAtomicMarkableReference<::Concurrent::Synchronization::Objectattr_atomic(:reference)private:reference,:reference=,:swap_reference,:compare_and_set_reference,:update_referencedefinitialize(value=nil,mark=false)super()self.reference=immutable_array(value,mark)end# Atomically sets the value and mark to the given updated value and# mark given both:# - the current value == the expected value &&# - the current mark == the expected mark## @param [Object] expected_val the expected value# @param [Object] new_val the new value# @param [Boolean] expected_mark the expected mark# @param [Boolean] new_mark the new mark## @return [Boolean] `true` if successful. A `false` return indicates# that the actual value was not equal to the expected value or the# actual mark was not equal to the expected markdefcompare_and_set(expected_val,new_val,expected_mark,new_mark)# Memoize a valid reference to the current AtomicReference for# later comparison.current=referencecurr_val,curr_mark=current# Ensure that that the expected marks match.returnfalseunlessexpected_mark==curr_markifexpected_val.is_a?Numeric# If the object is a numeric, we need to ensure we are comparing# the numerical valuesreturnfalseunlessexpected_val==curr_valelse# Otherwise, we need to ensure we are comparing the object identity.# Theoretically, this could be incorrect if a user monkey-patched# `Object#equal?`, but they should know that they are playing with# fire at that point.returnfalseunlessexpected_val.equal?curr_valendprospect=immutable_array(new_val,new_mark)compare_and_set_referencecurrent,prospectendalias_method:compare_and_swap,:compare_and_set# Gets the current reference and marked values.## @return [Array] the current reference and marked valuesdefgetreferenceend# Gets the current value of the reference## @return [Object] the current value of the referencedefvaluereference[0]end# Gets the current marked value## @return [Boolean] the current marked valuedefmarkreference[1]endalias_method:marked?,:mark# _Unconditionally_ sets to the given value of both the reference and# the mark.## @param [Object] new_val the new value# @param [Boolean] new_mark the new mark## @return [Array] both the new value and the new markdefset(new_val,new_mark)self.reference=immutable_array(new_val,new_mark)end# Pass the current value and marked state to the given block, replacing it# with the block's results. May retry if the value changes during the# block's execution.## @yield [Object] Calculate a new value and marked state for the atomic# reference using given (old) value and (old) marked# @yieldparam [Object] old_val the starting value of the atomic reference# @yieldparam [Boolean] old_mark the starting state of marked## @return [Array] the new value and new markdefupdateloopdoold_val,old_mark=referencenew_val,new_mark=yieldold_val,old_markifcompare_and_setold_val,new_val,old_mark,new_markreturnimmutable_array(new_val,new_mark)endendend# 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 and marked state for the atomic# reference using given (old) value and (old) marked# @yieldparam [Object] old_val the starting value of the atomic reference# @yieldparam [Boolean] old_mark the starting state of marked## @return [Array] the new value and marked state## @raise [Concurrent::ConcurrentUpdateError] if the update failsdeftry_update!old_val,old_mark=referencenew_val,new_mark=yieldold_val,old_markunlesscompare_and_setold_val,new_val,old_mark,new_markfail::Concurrent::ConcurrentUpdateError,'AtomicMarkableReference: Update failed due to race condition.','Note: If you would like to guarantee an update, please use '+'the `AtomicMarkableReference#update` method.'endimmutable_array(new_val,new_mark)end# Pass the current value to the given block, replacing it with the# block's result. Simply return nil if update fails.## @yield [Object] Calculate a new value and marked state for the atomic# reference using given (old) value and (old) marked# @yieldparam [Object] old_val the starting value of the atomic reference# @yieldparam [Boolean] old_mark the starting state of marked## @return [Array] the new value and marked state, or nil if# the update faileddeftry_updateold_val,old_mark=referencenew_val,new_mark=yieldold_val,old_markreturnunlesscompare_and_setold_val,new_val,old_mark,new_markimmutable_array(new_val,new_mark)endprivatedefimmutable_array(*args)args.freezeendendend