1- require 'concurrent/concern/dereferenceable'
21require 'concurrent/atomic/atomic_reference'
2+ require 'concurrent/collection/copy_on_notify_observer_set'
3+ require 'concurrent/concern/observable'
34require 'concurrent/synchronization/object'
45
56module Concurrent
@@ -18,11 +19,9 @@ module Concurrent
1819 # new value to the result of running the given block if and only if that
1920 # value validates.
2021 #
21- # @!macro copy_options
22- #
2322 # @see http://clojure.org/atoms Clojure Atoms
2423 class Atom < Synchronization ::Object
25- include Concern ::Dereferenceable
24+ include Concern ::Observable
2625
2726 # Create a new atom with the given initial value.
2827 #
@@ -38,20 +37,18 @@ class Atom < Synchronization::Object
3837 # @raise [ArgumentError] if the validator is not a `Proc` (when given)
3938 def initialize ( value , opts = { } )
4039 super ( )
41-
42- @validator = opts . fetch ( :validator , -> ( v ) { true } )
43- raise ArgumentError . new ( 'validator must be a proc' ) unless @validator . is_a? Proc
44-
45- @value = Concurrent ::AtomicReference . new ( value )
46- ns_set_deref_options ( opts )
47- ensure_ivar_visibility!
40+ synchronize do
41+ @validator = opts . fetch ( :validator , -> ( v ) { true } )
42+ @value = Concurrent ::AtomicReference . new ( value )
43+ self . observers = Collection ::CopyOnNotifyObserverSet . new
44+ end
4845 end
4946
5047 # The current value of the atom.
5148 #
5249 # @return [Object] The current value.
5350 def value
54- apply_deref_options ( @value . value )
51+ @value . value
5552 end
5653 alias_method :deref , :value
5754
@@ -87,45 +84,66 @@ def value
8784 def swap ( *args )
8885 raise ArgumentError . new ( 'no block given' ) unless block_given?
8986
90- begin
91- loop do
92- old_value = @value . value
87+ loop do
88+ old_value = @value . value
89+ begin
9390 new_value = yield ( old_value , *args )
94- return old_value unless @validator . call ( new_value )
95- return new_value if compare_and_set! ( old_value , new_value )
91+ break old_value unless valid? ( new_value )
92+ break new_value if compare_and_set ( old_value , new_value )
93+ rescue
94+ break old_value
9695 end
97- rescue
98- return @value . value
9996 end
10097 end
10198
102- # @!macro [attach] atom_compare_and_set
103- # Atomically sets the value of atom to the new value if and only if the
104- # current value of the atom is identical to the old value and the new
105- # value successfully validates against the (optional) validator given
106- # at construction.
99+ # Atomically sets the value of atom to the new value if and only if the
100+ # current value of the atom is identical to the old value and the new
101+ # value successfully validates against the (optional) validator given
102+ # at construction.
107103 #
108- # @param [Object] old_value The expected current value.
109- # @param [Object] new_value The intended new value.
104+ # @param [Object] old_value The expected current value.
105+ # @param [Object] new_value The intended new value.
110106 #
111- # @return [Boolean] True if the value is changed else false.
107+ # @return [Boolean] True if the value is changed else false.
112108 def compare_and_set ( old_value , new_value )
113- compare_and_set! ( old_value , new_value )
114- rescue
115- false
109+ if valid? ( new_value ) && @value . compare_and_set ( old_value , new_value )
110+ observers . notify_observers ( Time . now , new_value , nil )
111+ true
112+ else
113+ false
114+ end
116115 end
117116
118- private
119-
120- # @!macro atom_compare_and_set
121- # @raise [Exception] if the validator proc raises an exception
122- # @!visibility private
123- def compare_and_set! ( old_value , new_value )
124- if @validator . call ( new_value ) # may raise exception
125- @value . compare_and_set ( old_value , new_value )
117+ # Atomically sets the value of atom to the new value without regard for the
118+ # current value so long as the new value successfully validates against the
119+ # (optional) validator given at construction.
120+ #
121+ # @param [Object] new_value The intended new value.
122+ #
123+ # @return [Object] The final value of the atom after all operations and
124+ # validations are complete.
125+ def reset ( new_value )
126+ old_value = @value . value
127+ if valid? ( new_value )
128+ @value . set ( new_value )
129+ observers . notify_observers ( Time . now , new_value , nil )
130+ new_value
126131 else
127- false
132+ old_value
128133 end
129134 end
135+
136+ private
137+
138+ # Is the new value valid?
139+ #
140+ # @param [Object] new_value The intended new value.
141+ # @return [Boolean] false if the validator function returns false or raises
142+ # an exception else true
143+ def valid? ( new_value )
144+ @validator . call ( new_value )
145+ rescue
146+ false
147+ end
130148 end
131149end
0 commit comments