Skip to content

Commit 2f031f1

Browse files
committed
Introduce AtomicMarkableReferenceState object
In order for the user to have a proper representation of the state of the object at the time of the update, we must wrap the state of the object in a new object. Imagine the scenario if a user is simply given back the reference of the actual AtomicMarkableReference object. The user cannot know what the values were are the exact time of updating, because another thread could conceivably alter that AtomicMarkableReference after the get returns.
1 parent f3f7292 commit 2f031f1

File tree

2 files changed

+21
-8
lines changed

2 files changed

+21
-8
lines changed

lib/concurrent/atomic_reference/mutex_markable_atomic.rb

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ module Concurrent
77
class MutexAtomicMarkableReference
88
include ::Concurrent::AtomicNumericCompareAndSetWrapper
99

10+
# TODO: Temporary until ImmutableStruct is merged into master.
11+
class AtomicMarkableReferenceState < Struct.new(:value, :marked)
12+
def self.new(*args)
13+
super(*args).freeze
14+
end
15+
end
16+
1017
# @!macro [attach] atomic_markable_reference_method_initialize
1118
def initialize(value = nil, marked = false)
1219
@mutex = Mutex.new
@@ -19,7 +26,7 @@ def initialize(value = nil, marked = false)
1926
#
2027
# @return [Array] the current reference and marked values
2128
def get
22-
@mutex.synchronize { [@value, @marked] }
29+
@mutex.synchronize { AtomicMarkableReferenceState.new @value, @marked }
2330
end
2431

2532
# @!macro [attach] atomic_markable_reference_method_value
@@ -53,7 +60,7 @@ def set(new_val, new_mark)
5360
@mutex.synchronize do
5461
@value, @marked = new_val, new_mark
5562

56-
[@value, @marked]
63+
AtomicMarkableReferenceState.new @value, @marked
5764
end
5865
end
5966

@@ -75,7 +82,7 @@ def update
7582
new_val, new_mark = yield old_val, old_mark
7683

7784
if compare_and_set old_val, new_val, old_mark, new_mark
78-
return [new_val, new_mark]
85+
return AtomicMarkableReferenceState.new new_val, new_mark
7986
end
8087
end
8188
end
@@ -105,7 +112,7 @@ def try_update
105112
fail(*err)
106113
end
107114

108-
[new_val, new_mark]
115+
AtomicMarkableReferenceState.new new_val, new_mark
109116
end
110117

111118
# @!macro [attach] atomic_markable_reference_method_compare_and_set

spec/concurrent/atomic/atomic_markable_reference_spec.rb

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
shared_examples :atomic_markable_reference do
33
# use a number outside JRuby's fixnum cache range, to ensure identity is
44
# preserved
5-
let(:atomic) { Edge::Concurrent::AtomicMarkableReference.new(1000, true) }
5+
let(:atomic) { Edge::Concurrent::AtomicMarkableReference.new 1000, true }
66

77
specify :test_construct do
88
expect(atomic.value).to eq 1000
@@ -14,22 +14,28 @@
1414

1515
expect(atomic.value).to eq 1001
1616
expect(atomic.marked?).to eq true
17-
expect(res).to eq [1001, true]
17+
18+
expect(res.value).to eq 1001
19+
expect(res.marked).to eq true
1820
end
1921

2022
specify :test_update do
2123
res = atomic.update { |v, m| [v + 1, !m] }
2224

2325
expect(atomic.marked?).to eq false
2426
expect(atomic.value).to eq 1001
25-
expect(res).to eq [1001, false]
27+
28+
expect(res.value).to eq 1001
29+
expect(res.marked).to eq false
2630
end
2731

2832
specify :test_try_update do
2933
res = atomic.try_update { |v, m| [v + 1, !m] }
3034

3135
expect(atomic.value).to eq 1001
32-
expect(res).to eq [1001, false]
36+
37+
expect(res.value).to eq 1001
38+
expect(res.marked).to eq false
3339
end
3440

3541
specify :test_try_update_fails do

0 commit comments

Comments
 (0)