@@ -40,7 +40,7 @@ class Agent
40
40
# is given at initialization
41
41
TIMEOUT = 5
42
42
43
- attr_reader :timeout
43
+ attr_reader :timeout , :executor
44
44
45
45
# Initialize a new Agent with the given initial value and provided options.
46
46
#
@@ -60,12 +60,12 @@ class Agent
60
60
# @option opts [String] :copy_on_deref (nil) call the given `Proc` passing the internal value and
61
61
# returning the value returned from the proc
62
62
def initialize ( initial , opts = { } )
63
- @value = initial
64
- @rescuers = [ ]
65
- @validator = Proc . new { |result | true }
66
- @timeout = opts . fetch ( :timeout , TIMEOUT ) . freeze
63
+ @value = initial
64
+ @rescuers = [ ]
65
+ @validator = Proc . new { |result | true }
66
+ @timeout = opts . fetch ( :timeout , TIMEOUT ) . freeze
67
67
self . observers = CopyOnWriteObserverSet . new
68
- @executor = OptionsParser ::get_executor_from ( opts )
68
+ @executor = OneByOne . new OptionsParser ::get_executor_from ( opts )
69
69
init_mutex
70
70
set_deref_options ( opts )
71
71
end
@@ -111,7 +111,11 @@ def rescue(clazz = StandardError, &block)
111
111
# @yieldparam [Object] value the result of the last update operation
112
112
# @yieldreturn [Boolean] true if the value is valid else false
113
113
def validate ( &block )
114
- @validator = block unless block . nil?
114
+ unless block . nil?
115
+ mutex . lock
116
+ @validator = block
117
+ mutex . unlock
118
+ end
115
119
self
116
120
end
117
121
alias_method :validates , :validate
@@ -124,8 +128,11 @@ def validate(&block)
124
128
# the new value
125
129
# @yieldparam [Object] value the current value
126
130
# @yieldreturn [Object] the new value
131
+ # @return [true, nil] nil when no block is given
127
132
def post ( &block )
128
- @executor . post { work ( &block ) } unless block . nil?
133
+ return nil if block . nil?
134
+ @executor . post { work ( &block ) }
135
+ true
129
136
end
130
137
131
138
# Update the current value with the result of the given block operation
@@ -139,6 +146,16 @@ def <<(block)
139
146
self
140
147
end
141
148
149
+ # Waits/blocks until all the updates sent before this call are done.
150
+ #
151
+ # @param [Numeric] timeout the maximum time in second to wait.
152
+ # @return [Boolean] false on timeout, true otherwise
153
+ def await ( timeout = nil )
154
+ done = Event . new
155
+ post { done . set }
156
+ done . wait timeout
157
+ end
158
+
142
159
private
143
160
144
161
# @!visibility private
@@ -147,33 +164,41 @@ def <<(block)
147
164
# @!visibility private
148
165
def try_rescue ( ex ) # :nodoc:
149
166
rescuer = mutex . synchronize do
150
- @rescuers . find { |r | ex . is_a? ( r . clazz ) }
167
+ @rescuers . find { |r | ex . is_a? ( r . clazz ) }
151
168
end
152
169
rescuer . block . call ( ex ) if rescuer
153
170
rescue Exception => ex
171
+ # puts "#{ex} (#{ex.class})\n#{ex.backtrace.join("\n")}"
154
172
# supress
155
173
end
156
174
157
175
# @!visibility private
158
176
def work ( &handler ) # :nodoc:
177
+ validator , value = mutex . synchronize { [ @validator , @value ] }
178
+
159
179
begin
180
+ # FIXME creates second thread
181
+ result , valid = Concurrent ::timeout ( @timeout ) do
182
+ [ result = handler . call ( value ) ,
183
+ validator . call ( result ) ]
184
+ end
185
+ rescue Exception => ex
186
+ exception = ex
187
+ end
160
188
161
- should_notify = false
189
+ mutex . lock
190
+ should_notify = if !exception && valid
191
+ @value = result
192
+ true
193
+ end
194
+ mutex . unlock
162
195
163
- mutex . synchronize do
164
- result = Concurrent ::timeout ( @timeout ) do
165
- handler . call ( @value )
166
- end
167
- if @validator . call ( result )
168
- @value = result
169
- should_notify = true
170
- end
171
- end
196
+ if should_notify
172
197
time = Time . now
173
- observers . notify_observers { [ time , self . value ] } if should_notify
174
- rescue Exception => ex
175
- try_rescue ( ex )
198
+ observers . notify_observers { [ time , self . value ] }
176
199
end
200
+
201
+ try_rescue ( exception )
177
202
end
178
203
end
179
204
end
0 commit comments