concurrent-ruby 1.0.0.pre1-java → 1.0.0.pre2-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -1
- data/README.md +16 -18
- data/lib/concurrent.rb +3 -3
- data/lib/concurrent/agent.rb +583 -0
- data/lib/concurrent/array.rb +1 -0
- data/lib/concurrent/async.rb +236 -111
- data/lib/concurrent/atom.rb +101 -46
- data/lib/concurrent/atomic/atomic_boolean.rb +2 -0
- data/lib/concurrent/atomic/atomic_fixnum.rb +2 -0
- data/lib/concurrent/atomic/cyclic_barrier.rb +1 -1
- data/lib/concurrent/atomic/event.rb +1 -1
- data/lib/concurrent/atomic/mutex_atomic_boolean.rb +1 -1
- data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +1 -1
- data/lib/concurrent/atomic/mutex_count_down_latch.rb +1 -1
- data/lib/concurrent/atomic/mutex_semaphore.rb +2 -2
- data/lib/concurrent/atomic/read_write_lock.rb +5 -4
- data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
- data/lib/concurrent/atomic/thread_local_var.rb +2 -0
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +1 -1
- data/lib/concurrent/atomics.rb +6 -4
- data/lib/concurrent/collection/copy_on_notify_observer_set.rb +7 -15
- data/lib/concurrent/collection/copy_on_write_observer_set.rb +7 -15
- data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +5 -0
- data/lib/concurrent/concern/observable.rb +38 -13
- data/lib/concurrent/configuration.rb +5 -4
- data/lib/concurrent/delay.rb +9 -8
- data/lib/concurrent/exchanger.rb +2 -0
- data/lib/concurrent/executor/abstract_executor_service.rb +2 -2
- data/lib/concurrent/executor/java_single_thread_executor.rb +0 -1
- data/lib/concurrent/executor/ruby_executor_service.rb +10 -4
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +10 -68
- data/lib/concurrent/executor/safe_task_executor.rb +7 -8
- data/lib/concurrent/executor/serialized_execution.rb +4 -4
- data/lib/concurrent/executor/single_thread_executor.rb +20 -10
- data/lib/concurrent/executor/timer_set.rb +4 -2
- data/lib/concurrent/executors.rb +0 -1
- data/lib/concurrent/future.rb +3 -2
- data/lib/concurrent/hash.rb +1 -1
- data/lib/concurrent/immutable_struct.rb +5 -1
- data/lib/concurrent/ivar.rb +1 -1
- data/lib/concurrent/mutable_struct.rb +7 -6
- data/lib/concurrent/{executor/executor.rb → options.rb} +4 -3
- data/lib/concurrent/promise.rb +3 -2
- data/lib/concurrent/scheduled_task.rb +3 -2
- data/lib/concurrent/settable_struct.rb +5 -4
- data/lib/concurrent/synchronization.rb +11 -3
- data/lib/concurrent/synchronization/abstract_lockable_object.rb +117 -0
- data/lib/concurrent/synchronization/abstract_object.rb +16 -129
- data/lib/concurrent/synchronization/abstract_struct.rb +2 -3
- data/lib/concurrent/synchronization/condition.rb +6 -4
- data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
- data/lib/concurrent/synchronization/{java_object.rb → jruby_object.rb} +5 -3
- data/lib/concurrent/synchronization/lock.rb +3 -2
- data/lib/concurrent/synchronization/lockable_object.rb +59 -0
- data/lib/concurrent/synchronization/mri_lockable_object.rb +71 -0
- data/lib/concurrent/synchronization/mri_object.rb +35 -0
- data/lib/concurrent/synchronization/object.rb +111 -39
- data/lib/concurrent/synchronization/rbx_lockable_object.rb +64 -0
- data/lib/concurrent/synchronization/rbx_object.rb +17 -68
- data/lib/concurrent/thread_safe/util.rb +0 -9
- data/lib/concurrent/thread_safe/util/adder.rb +3 -0
- data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +3 -1
- data/lib/concurrent/thread_safe/util/cheap_lockable.rb +3 -0
- data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +1 -0
- data/lib/concurrent/thread_safe/util/striped64.rb +6 -1
- data/lib/concurrent/thread_safe/util/volatile.rb +2 -0
- data/lib/concurrent/thread_safe/util/xor_shift_random.rb +2 -0
- data/lib/concurrent/tvar.rb +36 -0
- data/lib/concurrent/utility/at_exit.rb +1 -1
- data/lib/concurrent/utility/monotonic_time.rb +3 -4
- data/lib/concurrent/utility/native_extension_loader.rb +1 -1
- data/lib/concurrent/version.rb +2 -2
- data/lib/concurrent_ruby_ext.jar +0 -0
- metadata +11 -6
- data/lib/concurrent/synchronization/monitor_object.rb +0 -27
- data/lib/concurrent/synchronization/mutex_object.rb +0 -43
data/lib/concurrent/array.rb
CHANGED
data/lib/concurrent/async.rb
CHANGED
@@ -1,52 +1,214 @@
|
|
1
|
-
require 'thread'
|
2
|
-
require 'concurrent/configuration'
|
3
|
-
require 'concurrent/delay'
|
4
|
-
require 'concurrent/errors'
|
5
1
|
require 'concurrent/ivar'
|
6
|
-
require 'concurrent/executor/
|
7
|
-
require 'concurrent/executor/serialized_execution'
|
2
|
+
require 'concurrent/executor/single_thread_executor'
|
8
3
|
|
9
4
|
module Concurrent
|
10
5
|
|
11
|
-
# A mixin module that provides simple asynchronous behavior to
|
12
|
-
#
|
6
|
+
# A mixin module that provides simple asynchronous behavior to a class,
|
7
|
+
# turning it into a simple actor. Loosely based on Erlang's
|
8
|
+
# [gen_server](http://www.erlang.org/doc/man/gen_server.html), but without
|
9
|
+
# supervision or linking.
|
10
|
+
#
|
11
|
+
# A more feature-rich {Concurrent::Actor} is also available when the
|
12
|
+
# capabilities of `Async` are too limited.
|
13
13
|
#
|
14
14
|
# ```cucumber
|
15
15
|
# Feature:
|
16
|
-
# As a stateful, plain old Ruby class
|
16
|
+
# As a stateful, plain old Ruby class
|
17
17
|
# I want safe, asynchronous behavior
|
18
18
|
# So my long-running methods don't block the main thread
|
19
19
|
# ```
|
20
20
|
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
21
|
+
# The `Async` module is a way to mix simple yet powerful asynchronous
|
22
|
+
# capabilities into any plain old Ruby object or class, turning each object
|
23
|
+
# into a simple Actor. Method calls are processed on a background thread. The
|
24
|
+
# caller is free to perform other actions while processing occurs in the
|
25
|
+
# background.
|
26
|
+
#
|
27
|
+
# Method calls to the asynchronous object are made via two proxy methods:
|
28
|
+
# `async` (alias `cast`) and `await` (alias `call`). These proxy methods post
|
29
|
+
# the method call to the object's background thread and return a "future"
|
30
|
+
# which will eventually contain the result of the method call.
|
31
|
+
#
|
32
|
+
# This behavior is loosely patterned after Erlang's `gen_server` behavior.
|
33
|
+
# When an Erlang module implements the `gen_server` behavior it becomes
|
34
|
+
# inherently asynchronous. The `start` or `start_link` function spawns a
|
35
|
+
# process (similar to a thread but much more lightweight and efficient) and
|
36
|
+
# reurns the ID of the process. Using the process ID, other processes can
|
37
|
+
# send messages to the `gen_server` via the `cast` and `call` methods. Unlike
|
38
|
+
# Erlang's `gen_server`, however, `Async` classes do not support linking or
|
39
|
+
# supervision trees.
|
27
40
|
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
# on
|
33
|
-
# method calls
|
34
|
-
# the
|
41
|
+
# ## Basic Usage
|
42
|
+
#
|
43
|
+
# When this module is mixed into a class, objects of the class become inherently
|
44
|
+
# asynchronous. Each object gets its own background thread (specifically,
|
45
|
+
# `SingleThreadExecutor`) on which to post asynchronous method calls.
|
46
|
+
# Asynchronous method calls are executed in the background one at a time in
|
47
|
+
# the order they are received.
|
48
|
+
#
|
49
|
+
# To create an asynchronous class, simply mix in the `Concurrent::Async` module:
|
50
|
+
#
|
51
|
+
# ```
|
52
|
+
# class Hello
|
53
|
+
# include Concurrent::Async
|
54
|
+
#
|
55
|
+
# def hello(name)
|
56
|
+
# "Hello, #{name}!"
|
57
|
+
# end
|
58
|
+
# end
|
59
|
+
# ```
|
60
|
+
#
|
61
|
+
# When defining a constructor it is critica that the first line be a call to
|
62
|
+
# `super` with no arguments. The `super` method initializes the background
|
63
|
+
# thread and other asynchronous components.
|
64
|
+
#
|
65
|
+
# ```
|
66
|
+
# class BackgroundLogger
|
67
|
+
# include Concurrent::Async
|
68
|
+
#
|
69
|
+
# def initialize(level)
|
70
|
+
# super()
|
71
|
+
# @logger = Logger.new(STDOUT)
|
72
|
+
# @logger.level = level
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# def info(msg)
|
76
|
+
# @logger.info(msg)
|
77
|
+
# end
|
78
|
+
# end
|
79
|
+
# ```
|
80
|
+
#
|
81
|
+
# Mixing this module into a class provides each object two proxy methods:
|
82
|
+
# `async` and `await`. These methods are thread safe with respect to the
|
83
|
+
# enclosing object. The former proxy allows methods to be called
|
84
|
+
# asynchronously by posting to the object's internal thread. The latter proxy
|
85
|
+
# allows a method to be called synchronously but does so safely with respect
|
86
|
+
# to any pending asynchronous method calls and ensures proper ordering. Both
|
87
|
+
# methods return a {Concurrent::IVar} which can be inspected for the result
|
88
|
+
# of the proxied method call. Calling a method with `async` will return a
|
35
89
|
# `:pending` `IVar` whereas `await` will return a `:complete` `IVar`.
|
90
|
+
#
|
91
|
+
# ```
|
92
|
+
# class Echo
|
93
|
+
# include Concurrent::Async
|
94
|
+
#
|
95
|
+
# def echo(msg)
|
96
|
+
# print "#{msg}\n"
|
97
|
+
# end
|
98
|
+
# end
|
99
|
+
#
|
100
|
+
# horn = Echo.new
|
101
|
+
# horn.echo('zero') # synchronous, not thread-safe
|
102
|
+
# # returns the actual return value of the method
|
103
|
+
#
|
104
|
+
# horn.async.echo('one') # asynchronous, non-blocking, thread-safe
|
105
|
+
# # returns an IVar in the :pending state
|
106
|
+
#
|
107
|
+
# horn.await.echo('two') # synchronous, blocking, thread-safe
|
108
|
+
# # returns an IVar in the :complete state
|
109
|
+
# ```
|
110
|
+
#
|
111
|
+
# ## Let It Fail
|
112
|
+
#
|
113
|
+
# The `async` and `await` proxy methods have built-in error protection based
|
114
|
+
# on Erlang's famous "let it fail" philosophy. Instance methods should not be
|
115
|
+
# programmed defensively. When an exception is raised by a delegated method
|
116
|
+
# the proxy will rescue the exception, expose it to the caller as the `reason`
|
117
|
+
# attribute of the returned future, then process the next method call.
|
118
|
+
#
|
119
|
+
# ## Calling Methods Internally
|
120
|
+
#
|
121
|
+
# External method calls should *always* use the `async` and `await` proxy
|
122
|
+
# methods. When one method calls another method, the `async` proxy should
|
123
|
+
# rarely be used and the `await` proxy should *never* be used.
|
124
|
+
#
|
125
|
+
# When an object calls one of its own methods using the `await` proxy the
|
126
|
+
# second call will be enqueued *behind* the currently running method call.
|
127
|
+
# Any attempt to wait on the result will fail as the second call will never
|
128
|
+
# run until after the current call completes.
|
129
|
+
#
|
130
|
+
# Calling a method using the `await` proxy from within a method that was
|
131
|
+
# itself called using `async` or `await` will irreversibly deadlock the
|
132
|
+
# object. Do *not* do this, ever.
|
133
|
+
#
|
134
|
+
# ## Instance Variables and Attribute Accessors
|
135
|
+
#
|
136
|
+
# Instance variables do not need to be thread-safe so long as they are private.
|
137
|
+
# Asynchronous method calls are processed in the order they are received and
|
138
|
+
# are processed one at a time. Therefore private instance variables can only
|
139
|
+
# be accessed by one thread at a time. This is inherently thread-safe.
|
140
|
+
#
|
141
|
+
# When using private instance variables within asynchronous methods, the best
|
142
|
+
# practice is to read the instance variable into a local variable at the start
|
143
|
+
# of the method then update the instance variable at the *end* of the method.
|
144
|
+
# This way, should an exception be raised during method execution the internal
|
145
|
+
# state of the boject will not have been changed.
|
146
|
+
#
|
147
|
+
# ### Reader Attributes
|
148
|
+
#
|
149
|
+
# The use of `attr_reader` is discouraged. Internal state exposed externally,
|
150
|
+
# when necessary, should be done through accessor methods. The instance
|
151
|
+
# variables exposed by these methods *must* be thread-safe, or they must be
|
152
|
+
# called using the `async` and `await` proxy methods. These two approaches are
|
153
|
+
# subtly different.
|
154
|
+
#
|
155
|
+
# When internal state is accessed via the `async` and `await` proxy methods,
|
156
|
+
# the returned value represents the object's sate *at the time the call is
|
157
|
+
# processed*, which may *not* be the state of the object at the time the call
|
158
|
+
# is made.
|
159
|
+
#
|
160
|
+
# To get the state *at the current* time, irrespective of an enqueued method
|
161
|
+
# calls, a reader method must be called directly. This is inherently unsafe
|
162
|
+
# unless the instance variable is itself thread-safe, preferrably using one
|
163
|
+
# of the thread-safe classes within this library. Because the thread-safe
|
164
|
+
# classes within this library are internally-locking or non-locking, they can
|
165
|
+
# be safely used from within asynchronous methods without causing deadlocks.
|
166
|
+
#
|
167
|
+
# Generally speaking, the best practice is to *not* expose internal state via
|
168
|
+
# reader methods. The best practice is to simply use the method's return value.
|
169
|
+
#
|
170
|
+
# ### Writer Attributes
|
171
|
+
#
|
172
|
+
# Writer attributes should never be used with asynchronous classes. Changing
|
173
|
+
# the state externally, even when done in the thread-safe way, is not logically
|
174
|
+
# consistent. Changes to state need to be timed with respect to all asynchronous
|
175
|
+
# method calls which my be in-process or enqueued. The only safe practice is to
|
176
|
+
# pass all necessary data to each method as arguments and let the method update
|
177
|
+
# the internal state as necessary.
|
178
|
+
#
|
179
|
+
# ## Class Constants, Variables, and Methods
|
180
|
+
#
|
181
|
+
# ### Class Constants
|
182
|
+
#
|
183
|
+
# Class constants do not need to be thread-safe. Since they are read-only and
|
184
|
+
# immutable they may be safely read both externally and from within
|
185
|
+
# asynchronous methods.
|
186
|
+
#
|
187
|
+
# ### Class Variables
|
188
|
+
#
|
189
|
+
# Class variables should be avoided. Class variables represent shared state.
|
190
|
+
# Shared state is anathema to concurrency. Should there be a need to share
|
191
|
+
# state using class variables they *must* be thread-safe, preferrably
|
192
|
+
# using the thread-safe classes within this library. When updating class
|
193
|
+
# variables, never assign a new value/object to the variable itself. Assignment
|
194
|
+
# is not thread-safe in Ruby. Instead, use the thread-safe update functions
|
195
|
+
# of the variable itself to change the value.
|
196
|
+
#
|
197
|
+
# The best practice is to *never* use class variables with `Async` classes.
|
198
|
+
#
|
199
|
+
# ### Class Methods
|
200
|
+
#
|
201
|
+
# Class methods which are pure functions are safe. Class methods which modify
|
202
|
+
# class variables should be avoided, for all the reasons listed above.
|
36
203
|
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
# ### An Important Note About Thread Safe Guarantees
|
204
|
+
# ## An Important Note About Thread Safe Guarantees
|
40
205
|
#
|
41
206
|
# > Thread safe guarantees can only be made when asynchronous method calls
|
42
|
-
# > are not mixed with
|
207
|
+
# > are not mixed with direct method calls. Use only direct method calls
|
43
208
|
# > when the object is used exclusively on a single thread. Use only
|
44
209
|
# > `async` and `await` when the object is shared between threads. Once you
|
45
|
-
# > call a method using `async`, you should no longer call
|
210
|
+
# > call a method using `async` or `await`, you should no longer call methods
|
46
211
|
# > directly on the object. Use `async` and `await` exclusively from then on.
|
47
|
-
# > With careful programming it is possible to switch back and forth but it's
|
48
|
-
# > also very easy to create race conditions and break your application.
|
49
|
-
# > Basically, it's "async all the way down."
|
50
212
|
#
|
51
213
|
# @example
|
52
214
|
#
|
@@ -54,19 +216,25 @@ module Concurrent
|
|
54
216
|
# include Concurrent::Async
|
55
217
|
#
|
56
218
|
# def echo(msg)
|
57
|
-
# sleep(rand)
|
58
219
|
# print "#{msg}\n"
|
59
|
-
# nil
|
60
220
|
# end
|
61
221
|
# end
|
62
222
|
#
|
63
223
|
# horn = Echo.new
|
64
224
|
# horn.echo('zero') # synchronous, not thread-safe
|
225
|
+
# # returns the actual return value of the method
|
65
226
|
#
|
66
227
|
# horn.async.echo('one') # asynchronous, non-blocking, thread-safe
|
228
|
+
# # returns an IVar in the :pending state
|
229
|
+
#
|
67
230
|
# horn.await.echo('two') # synchronous, blocking, thread-safe
|
231
|
+
# # returns an IVar in the :complete state
|
68
232
|
#
|
69
|
-
# @see Concurrent::
|
233
|
+
# @see Concurrent::Actor
|
234
|
+
# @see Concurrent::SingleThreadExecutor
|
235
|
+
# @see https://en.wikipedia.org/wiki/Actor_model "Actor Model" at Wikipedia
|
236
|
+
# @see http://www.erlang.org/doc/man/gen_server.html Erlang gen_server
|
237
|
+
# @see http://c2.com/cgi/wiki?LetItCrash "Let It Crash" at http://c2.com/
|
70
238
|
module Async
|
71
239
|
|
72
240
|
# @!method self.new(*args, &block)
|
@@ -76,7 +244,7 @@ module Concurrent
|
|
76
244
|
#
|
77
245
|
# @param [Array<Object>] args Zero or more arguments to be passed to the
|
78
246
|
# object's initializer.
|
79
|
-
# @param [Proc]
|
247
|
+
# @param [Proc] block Optional block to pass to the object's initializer.
|
80
248
|
# @return [Object] A properly initialized object of the asynchronous class.
|
81
249
|
|
82
250
|
# Check for the presence of a method on an object and determine if a given
|
@@ -138,13 +306,11 @@ module Concurrent
|
|
138
306
|
# given executor. Block if necessary.
|
139
307
|
#
|
140
308
|
# @param [Object] delegate the object to wrap and delegate method calls to
|
141
|
-
# @param [Concurrent::
|
142
|
-
# @param [Concurrent::SerializedExecution] serializer the serializer to use when delegating method calls
|
309
|
+
# @param [Concurrent::ExecutorService] executor the executor on which to execute delegated method calls
|
143
310
|
# @param [Boolean] blocking will block awaiting result when `true`
|
144
|
-
def initialize(delegate, executor,
|
311
|
+
def initialize(delegate, executor, blocking)
|
145
312
|
@delegate = delegate
|
146
313
|
@executor = executor
|
147
|
-
@serializer = serializer
|
148
314
|
@blocking = blocking
|
149
315
|
end
|
150
316
|
|
@@ -163,89 +329,61 @@ module Concurrent
|
|
163
329
|
super unless @delegate.respond_to?(method)
|
164
330
|
Async::validate_argc(@delegate, method, *args)
|
165
331
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
rescue => reason
|
173
|
-
ivar.fail(reason)
|
174
|
-
end
|
332
|
+
ivar = Concurrent::IVar.new
|
333
|
+
@executor.post(args) do |arguments|
|
334
|
+
begin
|
335
|
+
ivar.set(@delegate.send(method, *arguments, &block))
|
336
|
+
rescue => error
|
337
|
+
ivar.fail(error)
|
175
338
|
end
|
176
|
-
ivar.value if @blocking
|
177
|
-
ivar
|
178
339
|
end
|
179
|
-
|
180
|
-
|
340
|
+
ivar.wait if @blocking
|
341
|
+
ivar
|
181
342
|
end
|
182
343
|
end
|
183
344
|
private_constant :AsyncDelegator
|
184
345
|
|
185
346
|
# Causes the chained method call to be performed asynchronously on the
|
186
|
-
#
|
187
|
-
#
|
188
|
-
#
|
189
|
-
#
|
190
|
-
#
|
191
|
-
# Before scheduling the method on the global thread pool a best-effort
|
192
|
-
# attempt will be made to validate that the method exists on the object
|
193
|
-
# and that the given arguments match the arity of the requested function.
|
194
|
-
# Due to the dynamic nature of Ruby and limitations of its reflection
|
195
|
-
# library, some edge cases will be missed. For more information see
|
196
|
-
# the documentation for the `validate_argc` method.
|
347
|
+
# object's thread. The delegated method will return a future in the
|
348
|
+
# `:pending` state and the method call will have been scheduled on the
|
349
|
+
# object's thread. The final disposition of the method call can be obtained
|
350
|
+
# by inspecting the returned future.
|
197
351
|
#
|
198
352
|
# @!macro [attach] async_thread_safety_warning
|
199
353
|
# @note The method call is guaranteed to be thread safe with respect to
|
200
354
|
# all other method calls against the same object that are called with
|
201
355
|
# either `async` or `await`. The mutable nature of Ruby references
|
202
356
|
# (and object orientation in general) prevent any other thread safety
|
203
|
-
# guarantees. Do NOT mix
|
204
|
-
#
|
205
|
-
# between threads.
|
357
|
+
# guarantees. Do NOT mix direct method calls with delegated method calls.
|
358
|
+
# Use *only* delegated method calls when sharing the object between threads.
|
206
359
|
#
|
207
360
|
# @return [Concurrent::IVar] the pending result of the asynchronous operation
|
208
361
|
#
|
209
|
-
# @raise [NameError] the object does not respond to
|
210
|
-
# @raise [ArgumentError] the given `args` do not match the arity of
|
211
|
-
#
|
212
|
-
# @see Concurrent::IVar
|
362
|
+
# @raise [NameError] the object does not respond to the requested method
|
363
|
+
# @raise [ArgumentError] the given `args` do not match the arity of
|
364
|
+
# the requested method
|
213
365
|
def async
|
214
|
-
@__async_delegator__
|
366
|
+
@__async_delegator__
|
215
367
|
end
|
368
|
+
alias_method :cast, :async
|
216
369
|
|
217
370
|
# Causes the chained method call to be performed synchronously on the
|
218
|
-
# current thread. The
|
219
|
-
#
|
220
|
-
#
|
221
|
-
#
|
222
|
-
#
|
223
|
-
# Before scheduling the method on the global thread pool a best-effort
|
224
|
-
# attempt will be made to validate that the method exists on the object
|
225
|
-
# and that the given arguments match the arity of the requested function.
|
226
|
-
# Due to the dynamic nature of Ruby and limitations of its reflection
|
227
|
-
# library, some edge cases will be missed. For more information see
|
228
|
-
# the documentation for the `validate_argc` method.
|
371
|
+
# current thread. The delegated will return a future in either the
|
372
|
+
# `:fulfilled` or `:rejected` state and the delegated method will have
|
373
|
+
# completed. The final disposition of the delegated method can be obtained
|
374
|
+
# by inspecting the returned future.
|
229
375
|
#
|
230
376
|
# @!macro async_thread_safety_warning
|
231
377
|
#
|
232
378
|
# @return [Concurrent::IVar] the completed result of the synchronous operation
|
233
379
|
#
|
234
|
-
# @raise [NameError] the object does not respond to
|
235
|
-
# @raise [ArgumentError] the given `args` do not match the arity of
|
236
|
-
#
|
237
|
-
# @see Concurrent::IVar
|
380
|
+
# @raise [NameError] the object does not respond to the requested method
|
381
|
+
# @raise [ArgumentError] the given `args` do not match the arity of the
|
382
|
+
# requested method
|
238
383
|
def await
|
239
|
-
@__await_delegator__
|
240
|
-
end
|
241
|
-
|
242
|
-
# Set a new executor.
|
243
|
-
#
|
244
|
-
# @raise [ArgumentError] executor has already been set.
|
245
|
-
def executor=(executor)
|
246
|
-
@__async_executor__.reconfigure { executor } or
|
247
|
-
raise ArgumentError.new('executor has already been set')
|
384
|
+
@__await_delegator__
|
248
385
|
end
|
386
|
+
alias_method :call, :await
|
249
387
|
|
250
388
|
private
|
251
389
|
|
@@ -254,27 +392,14 @@ module Concurrent
|
|
254
392
|
# @note This method *must* be called immediately upon object construction.
|
255
393
|
# This is the only way thread-safe initialization can be guaranteed.
|
256
394
|
#
|
257
|
-
# @raise [Concurrent::InitializationError] when called more than once
|
258
|
-
#
|
259
395
|
# @!visibility private
|
260
396
|
def init_synchronization
|
261
397
|
return self if @__async_initialized__
|
262
|
-
|
263
398
|
@__async_initialized__ = true
|
264
|
-
|
265
|
-
|
266
|
-
@
|
267
|
-
|
268
|
-
}
|
269
|
-
|
270
|
-
@__await_delegator__ = Delay.new {
|
271
|
-
AsyncDelegator.new(self, Delay.new{ Concurrent::ImmediateExecutor.new }, serializer, true)
|
272
|
-
}
|
273
|
-
|
274
|
-
@__async_delegator__ = Delay.new {
|
275
|
-
AsyncDelegator.new(self, @__async_executor__, serializer, false)
|
276
|
-
}
|
277
|
-
|
399
|
+
@__async_executor__ = Concurrent::SingleThreadExecutor.new(
|
400
|
+
fallback_policy: :caller_runs, auto_terminate: true)
|
401
|
+
@__await_delegator__ = AsyncDelegator.new(self, @__async_executor__, true)
|
402
|
+
@__async_delegator__ = AsyncDelegator.new(self, @__async_executor__, false)
|
278
403
|
self
|
279
404
|
end
|
280
405
|
end
|