Skip to content

Commit 9b8a280

Browse files
committed
Add volatile cas fields, few classes converted
fix usages of #ensure_ivar_visibility!
1 parent 1aeea1e commit 9b8a280

File tree

14 files changed

+127
-90
lines changed

14 files changed

+127
-90
lines changed

lib/concurrent/atom.rb

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@ module Concurrent
2020
# value validates.
2121
#
2222
# @see http://clojure.org/atoms Clojure Atoms
23-
class Atom < Synchronization::LockableObject
23+
class Atom < Synchronization::Object
2424
include Concern::Observable
2525

26+
private *attr_volatile_with_cas(:value)
27+
public :value
28+
2629
# Create a new atom with the given initial value.
2730
#
2831
# @param [Object] value The initial value
@@ -36,20 +39,16 @@ class Atom < Synchronization::LockableObject
3639
#
3740
# @raise [ArgumentError] if the validator is not a `Proc` (when given)
3841
def initialize(value, opts = {})
39-
super()
40-
synchronize do
41-
@validator = opts.fetch(:validator, ->(v){ true })
42-
@value = Concurrent::AtomicReference.new(value)
43-
self.observers = Collection::CopyOnNotifyObserverSet.new
44-
end
42+
@Validator = opts.fetch(:validator, -> (v) { true })
43+
self.observers = Collection::CopyOnNotifyObserverSet.new
44+
super(value) # ensures visibility
4545
end
4646

47-
# The current value of the atom.
47+
# @!method value
48+
# The current value of the atom.
4849
#
49-
# @return [Object] The current value.
50-
def value
51-
@value.value
52-
end
50+
# @return [Object] The current value.
51+
5352
alias_method :deref, :value
5453

5554
# Atomically swaps the value of atom using the given block. The current
@@ -85,7 +84,7 @@ def swap(*args)
8584
raise ArgumentError.new('no block given') unless block_given?
8685

8786
loop do
88-
old_value = @value.value
87+
old_value = value
8988
begin
9089
new_value = yield(old_value, *args)
9190
break old_value unless valid?(new_value)
@@ -106,7 +105,7 @@ def swap(*args)
106105
#
107106
# @return [Boolean] True if the value is changed else false.
108107
def compare_and_set(old_value, new_value)
109-
if valid?(new_value) && @value.compare_and_set(old_value, new_value)
108+
if valid?(new_value) && compare_and_set_value(old_value, new_value)
110109
observers.notify_observers(Time.now, new_value, nil)
111110
true
112111
else
@@ -123,9 +122,9 @@ def compare_and_set(old_value, new_value)
123122
# @return [Object] The final value of the atom after all operations and
124123
# validations are complete.
125124
def reset(new_value)
126-
old_value = @value.value
125+
old_value = value
127126
if valid?(new_value)
128-
@value.set(new_value)
127+
self.value = new_value
129128
observers.notify_observers(Time.now, new_value, nil)
130129
new_value
131130
else
@@ -141,7 +140,7 @@ def reset(new_value)
141140
# @return [Boolean] false if the validator function returns false or raises
142141
# an exception else true
143142
def valid?(new_value)
144-
@validator.call(new_value)
143+
@Validator.call(new_value)
145144
rescue
146145
false
147146
end

lib/concurrent/atomic/read_write_lock.rb

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ class ReadWriteLock < Synchronization::Object
4141
# @!visibility private
4242
MAX_WRITERS = RUNNING_WRITER - MAX_READERS - 1
4343

44-
# Implementation notes:
44+
# Implementation notes:
4545
# A goal is to make the uncontended path for both readers/writers lock-free
4646
# Only if there is reader-writer or writer-writer contention, should locks be used
47-
# Internal state is represented by a single integer ("counter"), and updated
47+
# Internal state is represented by a single integer ("counter"), and updated
4848
# using atomic compare-and-swap operations
4949
# When the counter is 0, the lock is free
5050
# Each reader increments the counter by 1 when acquiring a read lock
@@ -57,8 +57,7 @@ def initialize
5757
@Counter = AtomicFixnum.new(0) # single integer which represents lock state
5858
@ReadLock = Synchronization::Lock.new
5959
@WriteLock = Synchronization::Lock.new
60-
ensure_ivar_visibility!
61-
super()
60+
super() # ensures visibility
6261
end
6362

6463
# Execute a block operation within a read lock.

lib/concurrent/atomic/reentrant_read_write_lock.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def initialize
105105
@ReadQueue = Synchronization::Lock.new # used to queue waiting readers
106106
@WriteQueue = Synchronization::Lock.new # used to queue waiting writers
107107
@HeldCount = ThreadLocalVar.new(0) # indicates # of R & W locks held by this thread
108-
ensure_ivar_visibility!
108+
super() # ensures visibility
109109
end
110110

111111
# Execute a block operation within a read lock.

lib/concurrent/edge/atomic_markable_reference.rb

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ module Edge
1111
# @api Edge
1212
class AtomicMarkableReference < ::Concurrent::Synchronization::Object
1313

14+
private *attr_volatile_with_cas(:reference)
15+
1416
# @!macro [attach] atomic_markable_reference_method_initialize
1517
def initialize(value = nil, mark = false)
16-
super()
17-
@Reference = AtomicReference.new ImmutableArray[value, mark]
18-
ensure_ivar_visibility!
18+
super(ImmutableArray[value, mark]) # ensures visibility
1919
end
2020

2121
# @!macro [attach] atomic_markable_reference_method_compare_and_set
@@ -36,7 +36,7 @@ def initialize(value = nil, mark = false)
3636
def compare_and_set(expected_val, new_val, expected_mark, new_mark)
3737
# Memoize a valid reference to the current AtomicReference for
3838
# later comparison.
39-
current = @Reference.get
39+
current = reference
4040
curr_val, curr_mark = current
4141

4242
# Ensure that that the expected marks match.
@@ -56,7 +56,7 @@ def compare_and_set(expected_val, new_val, expected_mark, new_mark)
5656

5757
prospect = ImmutableArray[new_val, new_mark]
5858

59-
@Reference.compare_and_set current, prospect
59+
compare_and_set_reference current, prospect
6060
end
6161
alias_method :compare_and_swap, :compare_and_set
6262

@@ -66,7 +66,7 @@ def compare_and_set(expected_val, new_val, expected_mark, new_mark)
6666
#
6767
# @return [ImmutableArray] the current reference and marked values
6868
def get
69-
@Reference.get
69+
reference
7070
end
7171

7272
# @!macro [attach] atomic_markable_reference_method_value
@@ -75,7 +75,7 @@ def get
7575
#
7676
# @return [Object] the current value of the reference
7777
def value
78-
@Reference.get[0]
78+
reference[0]
7979
end
8080

8181
# @!macro [attach] atomic_markable_reference_method_mark
@@ -84,7 +84,7 @@ def value
8484
#
8585
# @return [Boolean] the current marked value
8686
def mark
87-
@Reference.get[1]
87+
reference[1]
8888
end
8989
alias_method :marked?, :mark
9090

@@ -98,7 +98,7 @@ def mark
9898
#
9999
# @return [ImmutableArray] both the new value and the new mark
100100
def set(new_val, new_mark)
101-
@Reference.set ImmutableArray[new_val, new_mark]
101+
self.reference = ImmutableArray[new_val, new_mark]
102102
end
103103

104104
# @!macro [attach] atomic_markable_reference_method_update
@@ -115,7 +115,7 @@ def set(new_val, new_mark)
115115
# @return [ImmutableArray] the new value and new mark
116116
def update
117117
loop do
118-
old_val, old_mark = @Reference.get
118+
old_val, old_mark = reference
119119
new_val, new_mark = yield old_val, old_mark
120120

121121
if compare_and_set old_val, new_val, old_mark, new_mark
@@ -139,7 +139,7 @@ def update
139139
#
140140
# @raise [Concurrent::ConcurrentUpdateError] if the update fails
141141
def try_update!
142-
old_val, old_mark = @Reference.get
142+
old_val, old_mark = reference
143143
new_val, new_mark = yield old_val, old_mark
144144

145145
unless compare_and_set old_val, new_val, old_mark, new_mark
@@ -165,7 +165,7 @@ def try_update!
165165
# @return [ImmutableArray] the new value and marked state, or nil if
166166
# the update failed
167167
def try_update
168-
old_val, old_mark = @Reference.get
168+
old_val, old_mark = reference
169169
new_val, new_mark = yield old_val, old_mark
170170

171171
return unless compare_and_set old_val, new_val, old_mark, new_mark

lib/concurrent/edge/future.rb

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,7 @@ def initialize(promise, default_executor)
173173
@Callbacks = LockFreeStack.new
174174
@Waiters = LockFreeStack.new # TODO replace with AtomicFixnum, avoid aba problem
175175
@State = AtomicReference.new PENDING
176-
super()
177-
ensure_ivar_visibility!
176+
super() # ensures visibility
178177
end
179178

180179
# @return [:pending, :completed]
@@ -879,9 +878,8 @@ def hide_completable
879878
# @!visibility private
880879
class AbstractPromise < Synchronization::Object
881880
def initialize(future)
882-
super()
883881
@Future = future
884-
ensure_ivar_visibility!
882+
super()
885883
end
886884

887885
def future
@@ -1377,9 +1375,8 @@ def initialize(default_executor, intended_time)
13771375
class Channel < Synchronization::Object
13781376
# TODO make lock free
13791377
def initialize
1380-
super
13811378
@ProbeSet = Concurrent::Channel::WaitableList.new
1382-
ensure_ivar_visibility!
1379+
super()
13831380
end
13841381

13851382
def probe_set_size

lib/concurrent/edge/lock_free_linked_set/node.rb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@ class Node < Synchronization::Object
99
attr_reader :Data, :Successor_reference, :Key
1010

1111
def initialize(data = nil, successor = nil)
12-
super()
13-
1412
@Successor_reference = AtomicMarkableReference.new(successor || Tail.new)
1513
@Data = data
1614
@Key = key_for data
1715

18-
ensure_ivar_visibility!
16+
super() # ensures visibility
1917
end
2018

2119
# Check to see if the node is the last in the list.

lib/concurrent/edge/lock_free_stack.rb

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,51 +21,51 @@ def next_node
2121

2222
EMPTY = Empty[nil, nil]
2323

24+
private *attr_volatile_with_cas(:head)
25+
2426
def initialize
25-
super
26-
@Head = AtomicReference.new EMPTY
27-
ensure_ivar_visibility!
27+
super(EMPTY)
2828
end
2929

3030
def empty?
31-
@Head.get.equal? EMPTY
31+
head.equal? EMPTY
3232
end
3333

3434
def compare_and_push(head, value)
35-
@Head.compare_and_set head, Node[value, head]
35+
compare_and_set_head head, Node[value, head]
3636
end
3737

3838
def push(value)
3939
while true
40-
head = @Head.get
41-
return self if @Head.compare_and_set head, Node[value, head]
40+
current_head = head
41+
return self if compare_and_set_head current_head, Node[value, current_head]
4242
end
4343
end
4444

4545
def peek
46-
@Head.get
46+
head
4747
end
4848

4949
def compare_and_pop(head)
50-
@Head.compare_and_set head, head.next_node
50+
compare_and_set_head head, head.next_node
5151
end
5252

5353
def pop
5454
while true
55-
head = @Head.get
56-
return head.value if @Head.compare_and_set head, head.next_node
55+
current_head = head
56+
return current_head.value if compare_and_set_head current_head, current_head.next_node
5757
end
5858
end
5959

6060
def compare_and_clear(head)
61-
@Head.compare_and_set head, EMPTY
61+
compare_and_set_head head, EMPTY
6262
end
6363

6464
def clear
6565
while true
66-
head = @Head.get
67-
return false if head == EMPTY
68-
return true if @Head.compare_and_set head, EMPTY
66+
current_head = head
67+
return false if current_head == EMPTY
68+
return true if compare_and_set_head current_head, EMPTY
6969
end
7070
end
7171

lib/concurrent/executor/safe_task_executor.rb

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,22 @@ module Concurrent
99
class SafeTaskExecutor < Synchronization::LockableObject
1010

1111
def initialize(task, opts = {})
12-
super()
13-
@task = task
12+
@task = task
1413
@exception_class = opts.fetch(:rescue_exception, false) ? Exception : StandardError
15-
ensure_ivar_visibility!
14+
super() # ensures visibility
1615
end
1716

1817
# @return [Array]
1918
def execute(*args)
2019
synchronize do
2120
success = false
22-
value = reason = nil
21+
value = reason = nil
2322

2423
begin
25-
value = @task.call(*args)
24+
value = @task.call(*args)
2625
success = true
2726
rescue @exception_class => ex
28-
reason = ex
27+
reason = ex
2928
success = false
3029
end
3130

lib/concurrent/synchronization/condition.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ class Condition < LockableObject
99

1010
def initialize(lock)
1111
@Lock = lock
12-
ensure_ivar_visibility!
1312
super()
1413
end
1514

lib/concurrent/synchronization/lockable_object.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ module Synchronization
1919
private_constant :LockableObjectImplementation
2020

2121
class LockableObject < LockableObjectImplementation
22-
def self.allow_only_direct_descendants! # FIXME interne dedime docela dost :/
22+
23+
# TODO make private for c-r
24+
25+
def self.allow_only_direct_descendants! # FIXME we inherit too much ourselves :/
2326
this = self
2427
singleton_class.send :define_method, :inherited do |child|
2528
# super child

lib/concurrent/synchronization/mri_lockable_object.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ def ns_broadcast
2121
# @!visibility private
2222
# @!macro internal_implementation_note
2323
class MriMutexLockableObject < MriLockableObject
24-
def initialize
25-
super
24+
def initialize(*defaults)
25+
super(*defaults)
2626
@__lock__ = ::Mutex.new
2727
@__condition__ = ::ConditionVariable.new
2828
end
@@ -46,8 +46,8 @@ def ns_wait(timeout = nil)
4646
# @!visibility private
4747
# @!macro internal_implementation_note
4848
class MriMonitorLockableObject < MriLockableObject
49-
def initialize
50-
super
49+
def initialize(*defaults)
50+
super(*defaults)
5151
@__lock__ = ::Monitor.new
5252
@__condition__ = @__lock__.new_cond
5353
end

0 commit comments

Comments
 (0)