Class: Concurrent::Edge::AtomicMarkableReference

Inherits:
Synchronization::Object show all
Defined in:
lib/concurrent/edge/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:

Defined Under Namespace

Classes: ImmutableArray

Instance Method Summary (collapse)

Constructor Details

- (AtomicMarkableReference) initialize(value = nil, mark = false)

Returns a new instance of AtomicMarkableReference



16
17
18
19
# File 'lib/concurrent/edge/atomic_markable_reference.rb', line 16

def initialize(value = nil, mark = false)
  super()
  self.reference = ImmutableArray[value, mark]
end

Instance Method Details

- (Boolean) compare_and_set(expected_val, new_val, expected_mark, new_mark) 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



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
# File 'lib/concurrent/edge/atomic_markable_reference.rb', line 36

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 = ImmutableArray[new_val, new_mark]

  compare_and_set_reference current, prospect
end

- (ImmutableArray) get

Gets the current reference and marked values.

Returns:



68
69
70
# File 'lib/concurrent/edge/atomic_markable_reference.rb', line 68

def get
  reference
end

- (Boolean) mark Also known as: marked?

Gets the current marked value

Returns:

  • (Boolean)

    the current marked value



86
87
88
# File 'lib/concurrent/edge/atomic_markable_reference.rb', line 86

def mark
  reference[1]
end

- (ImmutableArray) set(new_val, new_mark)

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:



100
101
102
# File 'lib/concurrent/edge/atomic_markable_reference.rb', line 100

def set(new_val, new_mark)
  self.reference = ImmutableArray[new_val, new_mark]
end

- (ImmutableArray) try_update {|Object| ... }

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:



167
168
169
170
171
172
173
174
# File 'lib/concurrent/edge/atomic_markable_reference.rb', line 167

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

  ImmutableArray[new_val, new_mark]
end

- (ImmutableArray) try_update! {|Object| ... }

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:

Raises:



141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/concurrent/edge/atomic_markable_reference.rb', line 141

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

  ImmutableArray[new_val, new_mark]
end

- (ImmutableArray) update {|Object| ... }

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:



116
117
118
119
120
121
122
123
124
125
# File 'lib/concurrent/edge/atomic_markable_reference.rb', line 116

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 ImmutableArray[new_val, new_mark]
    end
  end
end

- (Object) value

Gets the current value of the reference

Returns:

  • (Object)

    the current value of the reference



77
78
79
# File 'lib/concurrent/edge/atomic_markable_reference.rb', line 77

def value
  reference[0]
end