Class: Concurrent::Edge::AtomicMarkableReference

Inherits:
Synchronization::Object
  • 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.

Defined Under Namespace

Classes: ImmutableArray

Instance Method Summary collapse

Constructor Details

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

Returns a new instance of AtomicMarkableReference.



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

def initialize(value = nil, mark = false)
  super()
  self.reference = ImmutableArray[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



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

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

#getImmutableArray

Gets the current reference and marked values.



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

def get
  reference
end

#markBoolean Also known as: marked?

Gets the current marked value



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

def mark
  reference[1]
end

#set(new_val, new_mark) ⇒ ImmutableArray

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



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

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

#try_update {|Object| ... } ⇒ ImmutableArray

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



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

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

#try_update! {|Object| ... } ⇒ ImmutableArray

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

Raises:

  • (Concurrent::ConcurrentUpdateError)

    if the update fails



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

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

#update {|Object| ... } ⇒ ImmutableArray

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



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

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

#valueObject

Gets the current value of the reference



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

def value
  reference[0]
end