# Class: Concurrent::AtomicMarkableReference

Inherits:
  Synchronization::Object
  - Object
  - Synchronization::Object
  - Concurrent::AtomicMarkableReference
  show all

Defined in:
  lib/concurrent/atomic/atomic_markable_reference.rb

## Overview

An atomic reference which maintains an object reference along with a mark bit that can be updated atomically.

See Also:

- java.util.concurrent.atomic.AtomicMarkableReference

## Instance Method Summary collapse

- #compare_and_set(expected_val, new_val, expected_mark, new_mark) ⇒ Boolean (also: #compare_and_swap)

  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.

- #get ⇒ Array

  Gets the current reference and marked values.

- #initialize(value = nil, mark = false) ⇒ AtomicMarkableReference constructor

  A new instance of AtomicMarkableReference.

- #mark ⇒ Boolean (also: #marked?)

  Gets the current marked value.

- #set(new_val, new_mark) ⇒ Array

  Unconditionally sets to the given value of both the reference and the mark.

- #try_update {|Object| ... } ⇒ Array

  Pass the current value to the given block, replacing it with the block's result.

- #try_update! {|Object| ... } ⇒ Array

  Pass the current value to the given block, replacing it with the block's result.

- #update {|Object| ... } ⇒ Array

  Pass the current value and marked state to the given block, replacing it with the block's results.

- #value ⇒ Object

  Gets the current value of the reference.

## Constructor Details

### #initialize(value = nil, mark = false) ⇒ `AtomicMarkableReference`

Returns a new instance of AtomicMarkableReference

    12
    13
    14
    15

    # File 'lib/concurrent/atomic/atomic_markable_reference.rb', line 12

    def initialize(value = nil, mark = false)
      super()
      self.reference = immutable_array(value, mark)
    end

## Instance Method Details

### #compare_and_set(expected_val, new_val, expected_mark, new_mark) ⇒ `Boolean` Also known as: compare_and_swap

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

that the actual value was not equal to the expected value or the actual mark was not equal to the expected mark

Parameters:

- expected_val (`Object`) —

  the expected value

- new_val (`Object`) —

  the new value

- expected_mark (`Boolean`) —

  the expected mark

- new_mark (`Boolean`) —

  the new mark

Returns:

- (`Boolean`) —

  `true` if successful. A `false` return indicates

    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

    # File 'lib/concurrent/atomic/atomic_markable_reference.rb', line 30

    def compare_and_set(expected_val, new_val, expected_mark, new_mark)
      # Memoize a valid reference to the current AtomicReference for
      # later comparison.
      current             = reference
      curr_val, curr_mark = current

      # Ensure that that the expected marks match.
      return false unless expected_mark == curr_mark

      if expected_val.is_a? Numeric
        # If the object is a numeric, we need to ensure we are comparing
        # the numerical values
        return false unless expected_val == curr_val
      else
        # 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.
        return false unless expected_val.equal? curr_val
      end

      prospect = immutable_array(new_val, new_mark)

      compare_and_set_reference current, prospect
    end

### #get ⇒ `Array`

Gets the current reference and marked values.

Returns:

- (`Array`) —

  the current reference and marked values

    61
    62
    63

    # File 'lib/concurrent/atomic/atomic_markable_reference.rb', line 61

    def get
      reference
    end

### #mark ⇒ `Boolean` Also known as: marked?

Gets the current marked value

Returns:

- (`Boolean`) —

  the current marked value

    75
    76
    77

    # File 'lib/concurrent/atomic/atomic_markable_reference.rb', line 75

    def mark
      reference[1]
    end

### #set(new_val, new_mark) ⇒ `Array`

Unconditionally sets to the given value of both the reference and the mark.

Parameters:

- new_val (`Object`) —

  the new value

- new_mark (`Boolean`) —

  the new mark

Returns:

- (`Array`) —

  both the new value and the new mark

    88
    89
    90

    # File 'lib/concurrent/atomic/atomic_markable_reference.rb', line 88

    def set(new_val, new_mark)
      self.reference = immutable_array(new_val, new_mark)
    end

### #try_update {|Object| ... } ⇒ `Array`

Pass the current value to the given block, replacing it with the block's result. Simply return nil if update fails.

the update failed

Yields:

- (`Object`) —

  Calculate a new value and marked state for the atomic reference using given (old) value and (old) marked

Yield Parameters:

- old_val (`Object`) —

  the starting value of the atomic reference

- old_mark (`Boolean`) —

  the starting state of marked

Returns:

- (`Array`) —

  the new value and marked state, or nil if

    149
    150
    151
    152
    153
    154
    155
    156

    # File 'lib/concurrent/atomic/atomic_markable_reference.rb', line 149

    def try_update
      old_val, old_mark = reference
      new_val, new_mark = yield old_val, old_mark

      return unless compare_and_set old_val, new_val, old_mark, new_mark

      immutable_array(new_val, new_mark)
    end

### #try_update! {|Object| ... } ⇒ `Array`

Pass the current value to the given block, replacing it with the block's result. Raise an exception if the update fails.

Yields:

- (`Object`) —

  Calculate a new value and marked state for the atomic reference using given (old) value and (old) marked

Yield Parameters:

- old_val (`Object`) —

  the starting value of the atomic reference

- old_mark (`Boolean`) —

  the starting state of marked

Returns:

- (`Array`) —

  the new value and marked state

Raises:

- (`Concurrent::ConcurrentUpdateError`) —

  if the update fails

    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137

    # File 'lib/concurrent/atomic/atomic_markable_reference.rb', line 125

    def try_update!
      old_val, old_mark = reference
      new_val, new_mark = yield old_val, old_mark

      unless compare_and_set old_val, new_val, old_mark, new_mark
        fail ::Concurrent::ConcurrentUpdateError,
             'AtomicMarkableReference: Update failed due to race condition.',
             'Note: If you would like to guarantee an update, please use ' +
                 'the `AtomicMarkableReference#update` method.'
      end

      immutable_array(new_val, new_mark)
    end

### #update {|Object| ... } ⇒ `Array`

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.

Yields:

- (`Object`) —

  Calculate a new value and marked state for the atomic reference using given (old) value and (old) marked

Yield Parameters:

- old_val (`Object`) —

  the starting value of the atomic reference

- old_mark (`Boolean`) —

  the starting state of marked

Returns:

- (`Array`) —

  the new value and new mark

    102
    103
    104
    105
    106
    107
    108
    109
    110
    111

    # File 'lib/concurrent/atomic/atomic_markable_reference.rb', line 102

    def update
      loop do
        old_val, old_mark = reference
        new_val, new_mark = yield old_val, old_mark

        if compare_and_set old_val, new_val, old_mark, new_mark
          return immutable_array(new_val, new_mark)
        end
      end
    end

### #value ⇒ `Object`

Gets the current value of the reference

Returns:

- (`Object`) —

  the current value of the reference

    68
    69
    70

    # File 'lib/concurrent/atomic/atomic_markable_reference.rb', line 68

    def value
      reference[0]
    end