3
3
require 'concurrent/delay'
4
4
require 'concurrent/errors'
5
5
require 'concurrent/ivar'
6
- require 'concurrent/future '
7
- require 'concurrent/executor/thread_pool_executor '
6
+ require 'concurrent/executor/immediate_executor '
7
+ require 'concurrent/executor/serialized_execution '
8
8
9
9
module Concurrent
10
10
@@ -124,69 +124,24 @@ def validate_argc(obj, method, *args)
124
124
end
125
125
module_function :validate_argc
126
126
127
- # Delegates synchronous, thread-safe method calls to the wrapped object.
128
- #
129
- # @!visibility private
130
- class AwaitDelegator # :nodoc:
131
-
132
- # Create a new delegator object wrapping the given `delegate` and
133
- # protecting it with the given `mutex`.
134
- #
135
- # @param [Object] delegate the object to wrap and delegate method calls to
136
- # @param [Mutex] mutex the mutex lock to use when delegating method calls
137
- def initialize ( delegate , mutex )
138
- @delegate = delegate
139
- @mutex = mutex
140
- end
141
-
142
- # Delegates method calls to the wrapped object. For performance,
143
- # dynamically defines the given method on the delegator so that
144
- # all future calls to `method` will not be directed here.
145
- #
146
- # @param [Symbol] method the method being called
147
- # @param [Array] args zero or more arguments to the method
148
- #
149
- # @return [IVar] the result of the method call
150
- #
151
- # @raise [NameError] the object does not respond to `method` method
152
- # @raise [ArgumentError] the given `args` do not match the arity of `method`
153
- def method_missing ( method , *args , &block )
154
- super unless @delegate . respond_to? ( method )
155
- Async ::validate_argc ( @delegate , method , *args )
156
-
157
- self . define_singleton_method ( method ) do |*args |
158
- Async ::validate_argc ( @delegate , method , *args )
159
- ivar = Concurrent ::IVar . new
160
- value , reason = nil , nil
161
- begin
162
- @mutex . synchronize do
163
- value = @delegate . send ( method , *args , &block )
164
- end
165
- rescue => reason
166
- # caught
167
- ensure
168
- return ivar . complete ( reason . nil? , value , reason )
169
- end
170
- end
171
-
172
- self . send ( method , *args )
173
- end
174
- end
175
-
176
127
# Delegates asynchronous, thread-safe method calls to the wrapped object.
177
128
#
178
129
# @!visibility private
179
130
class AsyncDelegator # :nodoc:
180
131
181
- # Create a new delegator object wrapping the given `delegate` and
182
- # protecting it with the given `mutex`.
132
+ # Create a new delegator object wrapping the given delegate,
133
+ # protecting it with the given serializer, and executing it on the
134
+ # given executor. Block if necessary.
183
135
#
184
136
# @param [Object] delegate the object to wrap and delegate method calls to
185
- # @param [Mutex] mutex the mutex lock to use when delegating method calls
186
- def initialize ( delegate , executor , mutex )
137
+ # @param [Concurrent::Delay] executor a `Delay` wrapping the executor on which to execute delegated method calls
138
+ # @param [Concurrent::SerializedExecution] serializer the serializer to use when delegating method calls
139
+ # @param [Boolean] blocking will block awaiting result when `true`
140
+ def initialize ( delegate , executor , serializer , blocking = false )
187
141
@delegate = delegate
188
142
@executor = executor
189
- @mutex = mutex
143
+ @serializer = serializer
144
+ @blocking = blocking
190
145
end
191
146
192
147
# Delegates method calls to the wrapped object. For performance,
@@ -206,11 +161,19 @@ def method_missing(method, *args, &block)
206
161
207
162
self . define_singleton_method ( method ) do |*args |
208
163
Async ::validate_argc ( @delegate , method , *args )
209
- Concurrent ::Future . execute ( executor : @executor . value ) do
210
- @mutex . synchronize do
211
- @delegate . send ( method , *args , &block )
164
+ ivar = Concurrent ::IVar . new
165
+ value , reason = nil , nil
166
+ @serializer . post ( @executor . value ) do
167
+ begin
168
+ value = @delegate . send ( method , *args , &block )
169
+ rescue => reason
170
+ # caught
171
+ ensure
172
+ ivar . complete ( reason . nil? , value , reason )
212
173
end
213
174
end
175
+ ivar . value if @blocking
176
+ ivar
214
177
end
215
178
216
179
self . send ( method , *args )
@@ -219,9 +182,9 @@ def method_missing(method, *args, &block)
219
182
220
183
# Causes the chained method call to be performed asynchronously on the
221
184
# global thread pool. The method called by this method will return a
222
- # `Future` object in the `:pending` state and the method call will have
185
+ # future object in the `:pending` state and the method call will have
223
186
# been scheduled on the global thread pool. The final disposition of the
224
- # method call can be obtained by inspecting the returned `Future` .
187
+ # method call can be obtained by inspecting the returned future .
225
188
#
226
189
# Before scheduling the method on the global thread pool a best-effort
227
190
# attempt will be made to validate that the method exists on the object
@@ -238,15 +201,15 @@ def method_missing(method, *args, &block)
238
201
# method call. Use *only* protected method calls when sharing the object
239
202
# between threads.
240
203
#
241
- # @return [Concurrent::Future ] the pending result of the asynchronous operation
204
+ # @return [Concurrent::IVar ] the pending result of the asynchronous operation
242
205
#
243
206
# @raise [Concurrent::InitializationError] `#init_mutex` has not been called
244
207
# @raise [NameError] the object does not respond to `method` method
245
208
# @raise [ArgumentError] the given `args` do not match the arity of `method`
246
209
#
247
- # @see Concurrent::Future
210
+ # @see Concurrent::IVar
248
211
def async
249
- raise InitializationError . new ( '#init_mutex was never called' ) unless @__async__mutex__
212
+ raise InitializationError . new ( '#init_mutex was never called' ) unless @__async_initialized__
250
213
@__async_delegator__ . value
251
214
end
252
215
alias_method :future , :async
@@ -280,7 +243,7 @@ def async
280
243
#
281
244
# @see Concurrent::IVar
282
245
def await
283
- raise InitializationError . new ( '#init_mutex was never called' ) unless @__async__mutex__
246
+ raise InitializationError . new ( '#init_mutex was never called' ) unless @__async_initialized__
284
247
@__await_delegator__ . value
285
248
end
286
249
alias_method :delay , :await
@@ -290,12 +253,12 @@ def await
290
253
# @raise [Concurrent::InitializationError] `#init_mutex` has not been called
291
254
# @raise [ArgumentError] executor has already been set
292
255
def executor = ( executor )
293
- raise InitializationError . new ( '#init_mutex was never called' ) unless @__async__mutex__
294
- @__async__executor__ . reconfigure { executor } or
256
+ raise InitializationError . new ( '#init_mutex was never called' ) unless @__async_initialized__
257
+ @__async_executor__ . reconfigure { executor } or
295
258
raise ArgumentError . new ( 'executor has already been set' )
296
259
end
297
260
298
- # Initialize the internal mutex and other synchronization objects. This method
261
+ # Initialize the internal serializer and other synchronization objects. This method
299
262
# *must* be called from the constructor of the including class or explicitly
300
263
# by the caller prior to calling any other methods. If `init_mutex` is *not*
301
264
# called explicitly the async/await/executor methods will raize a
@@ -308,12 +271,14 @@ def executor=(executor)
308
271
#
309
272
# @raise [Concurrent::InitializationError] when called more than once
310
273
def init_mutex
311
- raise InitializationError . new ( '#init_mutex was already called' ) if @__async__mutex__
312
- ( @__async__mutex__ = Mutex . new ) . lock
313
- @__async__executor__ = Delay . new { Concurrent . configuration . global_operation_pool }
314
- @__await_delegator__ = Delay . new { AwaitDelegator . new ( self , @__async__mutex__ ) }
315
- @__async_delegator__ = Delay . new { AsyncDelegator . new ( self , @__async__executor__ , @__async__mutex__ ) }
316
- @__async__mutex__ . unlock
274
+ raise InitializationError . new ( '#init_mutex was already called' ) if @__async_initialized__
275
+ @__async_initialized__ = true
276
+ serializer = Concurrent ::SerializedExecution . new
277
+ @__async_executor__ = Delay . new { Concurrent . configuration . global_operation_pool }
278
+ @__await_delegator__ = Delay . new { AsyncDelegator . new (
279
+ self , Delay . new { Concurrent ::ImmediateExecutor . new } , serializer , true ) }
280
+ @__async_delegator__ = Delay . new { AsyncDelegator . new (
281
+ self , @__async_executor__ , serializer , false ) }
317
282
end
318
283
end
319
284
end
0 commit comments