concurrent-ruby 0.9.2-java → 1.0.0-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 +49 -1
- data/README.md +86 -120
- data/lib/concurrent.rb +14 -5
- data/lib/concurrent/agent.rb +587 -0
- data/lib/concurrent/array.rb +39 -0
- data/lib/concurrent/async.rb +296 -149
- data/lib/concurrent/atom.rb +135 -45
- data/lib/concurrent/atomic/abstract_thread_local_var.rb +38 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +83 -118
- data/lib/concurrent/atomic/atomic_fixnum.rb +101 -163
- data/lib/concurrent/atomic/atomic_reference.rb +1 -8
- data/lib/concurrent/atomic/count_down_latch.rb +62 -103
- data/lib/concurrent/atomic/cyclic_barrier.rb +3 -1
- data/lib/concurrent/atomic/event.rb +1 -1
- data/lib/concurrent/atomic/java_count_down_latch.rb +39 -0
- data/lib/concurrent/atomic/java_thread_local_var.rb +50 -0
- data/lib/concurrent/atomic/mutex_atomic_boolean.rb +60 -0
- data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +91 -0
- data/lib/concurrent/atomic/mutex_count_down_latch.rb +43 -0
- data/lib/concurrent/atomic/mutex_semaphore.rb +115 -0
- 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/ruby_thread_local_var.rb +172 -0
- data/lib/concurrent/atomic/semaphore.rb +84 -178
- data/lib/concurrent/atomic/thread_local_var.rb +65 -294
- data/lib/concurrent/atomic_reference/jruby+truffle.rb +1 -0
- data/lib/concurrent/atomic_reference/jruby.rb +1 -1
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +14 -8
- data/lib/concurrent/atomic_reference/ruby.rb +1 -1
- data/lib/concurrent/atomics.rb +7 -37
- 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/java_non_concurrent_priority_queue.rb +84 -0
- data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
- data/lib/concurrent/collection/map/mri_map_backend.rb +66 -0
- data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +144 -0
- data/lib/concurrent/collection/map/synchronized_map_backend.rb +86 -0
- data/lib/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
- data/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb +150 -0
- data/lib/concurrent/concern/dereferenceable.rb +9 -24
- data/lib/concurrent/concern/logging.rb +1 -1
- data/lib/concurrent/concern/obligation.rb +11 -20
- data/lib/concurrent/concern/observable.rb +38 -13
- data/lib/concurrent/configuration.rb +23 -152
- data/lib/concurrent/constants.rb +8 -0
- data/lib/concurrent/delay.rb +14 -12
- data/lib/concurrent/exchanger.rb +339 -41
- data/lib/concurrent/executor/abstract_executor_service.rb +134 -0
- data/lib/concurrent/executor/executor_service.rb +23 -359
- data/lib/concurrent/executor/immediate_executor.rb +3 -2
- data/lib/concurrent/executor/java_executor_service.rb +100 -0
- data/lib/concurrent/executor/java_single_thread_executor.rb +3 -3
- data/lib/concurrent/executor/java_thread_pool_executor.rb +3 -4
- data/lib/concurrent/executor/ruby_executor_service.rb +78 -0
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +10 -66
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +25 -22
- data/lib/concurrent/executor/safe_task_executor.rb +6 -7
- data/lib/concurrent/executor/serial_executor_service.rb +34 -0
- data/lib/concurrent/executor/serialized_execution.rb +10 -33
- data/lib/concurrent/executor/serialized_execution_delegator.rb +28 -0
- data/lib/concurrent/executor/simple_executor_service.rb +1 -10
- data/lib/concurrent/executor/single_thread_executor.rb +20 -10
- data/lib/concurrent/executor/timer_set.rb +8 -10
- data/lib/concurrent/executors.rb +12 -2
- data/lib/concurrent/future.rb +6 -4
- data/lib/concurrent/hash.rb +35 -0
- data/lib/concurrent/immutable_struct.rb +5 -1
- data/lib/concurrent/ivar.rb +12 -16
- data/lib/concurrent/lazy_register.rb +11 -8
- data/lib/concurrent/map.rb +180 -0
- data/lib/concurrent/maybe.rb +6 -3
- data/lib/concurrent/mutable_struct.rb +7 -6
- data/lib/concurrent/mvar.rb +26 -2
- data/lib/concurrent/{executor/executor.rb → options.rb} +5 -29
- data/lib/concurrent/promise.rb +7 -5
- data/lib/concurrent/scheduled_task.rb +13 -71
- data/lib/concurrent/settable_struct.rb +5 -4
- data/lib/concurrent/synchronization.rb +15 -3
- data/lib/concurrent/synchronization/abstract_lockable_object.rb +98 -0
- data/lib/concurrent/synchronization/abstract_object.rb +7 -146
- 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/jruby_object.rb +44 -0
- data/lib/concurrent/synchronization/lock.rb +3 -2
- data/lib/concurrent/synchronization/lockable_object.rb +72 -0
- data/lib/concurrent/synchronization/mri_lockable_object.rb +71 -0
- data/lib/concurrent/synchronization/mri_object.rb +43 -0
- data/lib/concurrent/synchronization/object.rb +140 -73
- data/lib/concurrent/synchronization/rbx_lockable_object.rb +65 -0
- data/lib/concurrent/synchronization/rbx_object.rb +30 -73
- data/lib/concurrent/synchronization/volatile.rb +34 -0
- data/lib/concurrent/thread_safe/synchronized_delegator.rb +50 -0
- data/lib/concurrent/thread_safe/util.rb +14 -0
- data/lib/concurrent/thread_safe/util/adder.rb +74 -0
- data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +30 -0
- data/lib/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
- data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
- data/lib/concurrent/thread_safe/util/striped64.rb +241 -0
- data/lib/concurrent/thread_safe/util/volatile.rb +75 -0
- data/lib/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
- data/lib/concurrent/timer_task.rb +3 -4
- data/lib/concurrent/tuple.rb +86 -0
- data/lib/concurrent/tvar.rb +5 -1
- data/lib/concurrent/utility/at_exit.rb +1 -1
- data/lib/concurrent/utility/engine.rb +4 -0
- data/lib/concurrent/utility/monotonic_time.rb +3 -4
- data/lib/concurrent/utility/native_extension_loader.rb +50 -30
- data/lib/concurrent/version.rb +2 -2
- data/lib/concurrent_ruby_ext.jar +0 -0
- metadata +47 -12
- data/lib/concurrent/atomic/condition.rb +0 -78
- data/lib/concurrent/collection/priority_queue.rb +0 -360
- data/lib/concurrent/synchronization/java_object.rb +0 -34
- data/lib/concurrent/synchronization/monitor_object.rb +0 -27
- data/lib/concurrent/synchronization/mutex_object.rb +0 -43
- data/lib/concurrent/utilities.rb +0 -5
- data/lib/concurrent/utility/timeout.rb +0 -39
- data/lib/concurrent/utility/timer.rb +0 -26
- data/lib/concurrent_ruby.rb +0 -2
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'concurrent/utility/engine'
|
2
|
+
require 'concurrent/thread_safe/util'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
if Concurrent.on_cruby?
|
6
|
+
|
7
|
+
# Because MRI never runs code in parallel, the existing
|
8
|
+
# non-thread-safe structures should usually work fine.
|
9
|
+
|
10
|
+
# @!macro [attach] concurrent_array
|
11
|
+
#
|
12
|
+
# A thread-safe subclass of Array. This version locks against the object
|
13
|
+
# itself for every method call, ensuring only one thread can be reading
|
14
|
+
# or writing at a time. This includes iteration methods like `#each`.
|
15
|
+
#
|
16
|
+
# @see http://ruby-doc.org/core-2.2.0/Array.html Ruby standard library `Array`
|
17
|
+
class Array < ::Array;
|
18
|
+
end
|
19
|
+
|
20
|
+
elsif Concurrent.on_jruby?
|
21
|
+
require 'jruby/synchronized'
|
22
|
+
|
23
|
+
# @!macro concurrent_array
|
24
|
+
class Array < ::Array
|
25
|
+
include JRuby::Synchronized
|
26
|
+
end
|
27
|
+
|
28
|
+
elsif Concurrent.on_rbx?
|
29
|
+
require 'monitor'
|
30
|
+
require 'concurrent/thread_safe/util/array_hash_rbx'
|
31
|
+
|
32
|
+
# @!macro concurrent_array
|
33
|
+
class Array < ::Array
|
34
|
+
end
|
35
|
+
|
36
|
+
ThreadSafe::Util.make_synchronized_on_rbx Array
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
data/lib/concurrent/async.rb
CHANGED
@@ -1,73 +1,239 @@
|
|
1
|
-
require 'thread'
|
2
1
|
require 'concurrent/configuration'
|
3
|
-
require 'concurrent/delay'
|
4
|
-
require 'concurrent/errors'
|
5
2
|
require 'concurrent/ivar'
|
6
|
-
require 'concurrent/
|
7
|
-
require 'concurrent/executor/serialized_execution'
|
8
|
-
require 'concurrent/concern/deprecation'
|
3
|
+
require 'concurrent/synchronization/lockable_object'
|
9
4
|
|
10
5
|
module Concurrent
|
11
6
|
|
12
|
-
# A mixin module that provides simple asynchronous behavior to
|
13
|
-
#
|
14
|
-
#
|
7
|
+
# A mixin module that provides simple asynchronous behavior to a class,
|
8
|
+
# turning it into a simple actor. Loosely based on Erlang's
|
9
|
+
# [gen_server](http://www.erlang.org/doc/man/gen_server.html), but without
|
10
|
+
# supervision or linking.
|
11
|
+
#
|
12
|
+
# A more feature-rich {Concurrent::Actor} is also available when the
|
13
|
+
# capabilities of `Async` are too limited.
|
14
|
+
#
|
15
15
|
# ```cucumber
|
16
16
|
# Feature:
|
17
|
-
# As a stateful, plain old Ruby class
|
17
|
+
# As a stateful, plain old Ruby class
|
18
18
|
# I want safe, asynchronous behavior
|
19
19
|
# So my long-running methods don't block the main thread
|
20
20
|
# ```
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
21
|
+
#
|
22
|
+
# The `Async` module is a way to mix simple yet powerful asynchronous
|
23
|
+
# capabilities into any plain old Ruby object or class, turning each object
|
24
|
+
# into a simple Actor. Method calls are processed on a background thread. The
|
25
|
+
# caller is free to perform other actions while processing occurs in the
|
26
|
+
# background.
|
27
|
+
#
|
28
|
+
# Method calls to the asynchronous object are made via two proxy methods:
|
29
|
+
# `async` (alias `cast`) and `await` (alias `call`). These proxy methods post
|
30
|
+
# the method call to the object's background thread and return a "future"
|
31
|
+
# which will eventually contain the result of the method call.
|
32
|
+
#
|
33
|
+
# This behavior is loosely patterned after Erlang's `gen_server` behavior.
|
34
|
+
# When an Erlang module implements the `gen_server` behavior it becomes
|
35
|
+
# inherently asynchronous. The `start` or `start_link` function spawns a
|
36
|
+
# process (similar to a thread but much more lightweight and efficient) and
|
37
|
+
# reurns the ID of the process. Using the process ID, other processes can
|
38
|
+
# send messages to the `gen_server` via the `cast` and `call` methods. Unlike
|
39
|
+
# Erlang's `gen_server`, however, `Async` classes do not support linking or
|
40
|
+
# supervision trees.
|
41
|
+
#
|
42
|
+
# ## Basic Usage
|
43
|
+
#
|
44
|
+
# When this module is mixed into a class, objects of the class become inherently
|
45
|
+
# asynchronous. Each object gets its own background thread on which to post
|
46
|
+
# asynchronous method calls. Asynchronous method calls are executed in the
|
47
|
+
# background one at a time in 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
|
36
89
|
# `:pending` `IVar` whereas `await` will return a `:complete` `IVar`.
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
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.
|
203
|
+
#
|
204
|
+
# ## An Important Note About Thread Safe Guarantees
|
205
|
+
#
|
42
206
|
# > Thread safe guarantees can only be made when asynchronous method calls
|
43
|
-
# > are not mixed with
|
207
|
+
# > are not mixed with direct method calls. Use only direct method calls
|
44
208
|
# > when the object is used exclusively on a single thread. Use only
|
45
209
|
# > `async` and `await` when the object is shared between threads. Once you
|
46
|
-
# > call a method using `async`, you should no longer call
|
210
|
+
# > call a method using `async` or `await`, you should no longer call methods
|
47
211
|
# > directly on the object. Use `async` and `await` exclusively from then on.
|
48
|
-
#
|
49
|
-
# > also very easy to create race conditions and break your application.
|
50
|
-
# > Basically, it's "async all the way down."
|
51
|
-
#
|
212
|
+
#
|
52
213
|
# @example
|
53
|
-
#
|
214
|
+
#
|
54
215
|
# class Echo
|
55
216
|
# include Concurrent::Async
|
56
|
-
#
|
217
|
+
#
|
57
218
|
# def echo(msg)
|
58
|
-
# sleep(rand)
|
59
219
|
# print "#{msg}\n"
|
60
|
-
# nil
|
61
220
|
# end
|
62
221
|
# end
|
63
|
-
#
|
222
|
+
#
|
64
223
|
# horn = Echo.new
|
65
224
|
# horn.echo('zero') # synchronous, not thread-safe
|
66
|
-
#
|
225
|
+
# # returns the actual return value of the method
|
226
|
+
#
|
67
227
|
# horn.async.echo('one') # asynchronous, non-blocking, thread-safe
|
228
|
+
# # returns an IVar in the :pending state
|
229
|
+
#
|
68
230
|
# horn.await.echo('two') # synchronous, blocking, thread-safe
|
231
|
+
# # returns an IVar in the :complete state
|
69
232
|
#
|
70
|
-
# @see Concurrent::
|
233
|
+
# @see Concurrent::Actor
|
234
|
+
# @see https://en.wikipedia.org/wiki/Actor_model "Actor Model" at Wikipedia
|
235
|
+
# @see http://www.erlang.org/doc/man/gen_server.html Erlang gen_server
|
236
|
+
# @see http://c2.com/cgi/wiki?LetItCrash "Let It Crash" at http://c2.com/
|
71
237
|
module Async
|
72
238
|
|
73
239
|
# @!method self.new(*args, &block)
|
@@ -77,7 +243,7 @@ module Concurrent
|
|
77
243
|
#
|
78
244
|
# @param [Array<Object>] args Zero or more arguments to be passed to the
|
79
245
|
# object's initializer.
|
80
|
-
# @param [Proc]
|
246
|
+
# @param [Proc] block Optional block to pass to the object's initializer.
|
81
247
|
# @return [Object] A properly initialized object of the asynchronous class.
|
82
248
|
|
83
249
|
# Check for the presence of a method on an object and determine if a given
|
@@ -132,26 +298,20 @@ module Concurrent
|
|
132
298
|
# Delegates asynchronous, thread-safe method calls to the wrapped object.
|
133
299
|
#
|
134
300
|
# @!visibility private
|
135
|
-
class AsyncDelegator
|
301
|
+
class AsyncDelegator < Synchronization::LockableObject
|
302
|
+
safe_initialization!
|
136
303
|
|
137
|
-
# Create a new delegator object wrapping the given delegate
|
138
|
-
# protecting it with the given serializer, and executing it on the
|
139
|
-
# given executor. Block if necessary.
|
304
|
+
# Create a new delegator object wrapping the given delegate.
|
140
305
|
#
|
141
306
|
# @param [Object] delegate the object to wrap and delegate method calls to
|
142
|
-
|
143
|
-
|
144
|
-
# @param [Boolean] blocking will block awaiting result when `true`
|
145
|
-
def initialize(delegate, executor, serializer, blocking = false)
|
307
|
+
def initialize(delegate)
|
308
|
+
super()
|
146
309
|
@delegate = delegate
|
147
|
-
@
|
148
|
-
@
|
149
|
-
@blocking = blocking
|
310
|
+
@queue = []
|
311
|
+
@executor = Concurrent.global_io_executor
|
150
312
|
end
|
151
313
|
|
152
|
-
# Delegates method calls to the wrapped object.
|
153
|
-
# dynamically defines the given method on the delegator so that
|
154
|
-
# all future calls to `method` will not be directed here.
|
314
|
+
# Delegates method calls to the wrapped object.
|
155
315
|
#
|
156
316
|
# @param [Symbol] method the method being called
|
157
317
|
# @param [Array] args zero or more arguments to the method
|
@@ -164,134 +324,121 @@ module Concurrent
|
|
164
324
|
super unless @delegate.respond_to?(method)
|
165
325
|
Async::validate_argc(@delegate, method, *args)
|
166
326
|
|
167
|
-
|
168
|
-
|
169
|
-
ivar
|
170
|
-
@
|
171
|
-
begin
|
172
|
-
ivar.set(@delegate.send(method, *args2, &block))
|
173
|
-
rescue => reason
|
174
|
-
ivar.fail(reason)
|
175
|
-
end
|
176
|
-
end
|
177
|
-
ivar.value if @blocking
|
178
|
-
ivar
|
327
|
+
ivar = Concurrent::IVar.new
|
328
|
+
synchronize do
|
329
|
+
@queue.push [ivar, method, args, block]
|
330
|
+
@executor.post { perform } if @queue.length == 1
|
179
331
|
end
|
180
332
|
|
181
|
-
|
333
|
+
ivar
|
334
|
+
end
|
335
|
+
|
336
|
+
# Perform all enqueued tasks.
|
337
|
+
#
|
338
|
+
# This method must be called from within the executor. It must not be
|
339
|
+
# called while already running. It will loop until the queue is empty.
|
340
|
+
def perform
|
341
|
+
loop do
|
342
|
+
ivar, method, args, block = synchronize { @queue.first }
|
343
|
+
break unless ivar # queue is empty
|
344
|
+
|
345
|
+
begin
|
346
|
+
ivar.set(@delegate.send(method, *args, &block))
|
347
|
+
rescue => error
|
348
|
+
ivar.fail(error)
|
349
|
+
end
|
350
|
+
|
351
|
+
synchronize do
|
352
|
+
@queue.shift
|
353
|
+
return if @queue.empty?
|
354
|
+
end
|
355
|
+
end
|
182
356
|
end
|
183
357
|
end
|
184
358
|
private_constant :AsyncDelegator
|
185
359
|
|
186
|
-
#
|
187
|
-
# global thread pool. The method called by this method will return a
|
188
|
-
# future object in the `:pending` state and the method call will have
|
189
|
-
# been scheduled on the global thread pool. The final disposition of the
|
190
|
-
# method call can be obtained by inspecting the returned future.
|
360
|
+
# Delegates synchronous, thread-safe method calls to the wrapped object.
|
191
361
|
#
|
192
|
-
#
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
362
|
+
# @!visibility private
|
363
|
+
class AwaitDelegator
|
364
|
+
|
365
|
+
# Create a new delegator object wrapping the given delegate.
|
366
|
+
#
|
367
|
+
# @param [AsyncDelegator] delegate the object to wrap and delegate method calls to
|
368
|
+
def initialize(delegate)
|
369
|
+
@delegate = delegate
|
370
|
+
end
|
371
|
+
|
372
|
+
# Delegates method calls to the wrapped object.
|
373
|
+
#
|
374
|
+
# @param [Symbol] method the method being called
|
375
|
+
# @param [Array] args zero or more arguments to the method
|
376
|
+
#
|
377
|
+
# @return [IVar] the result of the method call
|
378
|
+
#
|
379
|
+
# @raise [NameError] the object does not respond to `method` method
|
380
|
+
# @raise [ArgumentError] the given `args` do not match the arity of `method`
|
381
|
+
def method_missing(method, *args, &block)
|
382
|
+
ivar = @delegate.send(method, *args, &block)
|
383
|
+
ivar.wait
|
384
|
+
ivar
|
385
|
+
end
|
386
|
+
end
|
387
|
+
private_constant :AwaitDelegator
|
388
|
+
|
389
|
+
# Causes the chained method call to be performed asynchronously on the
|
390
|
+
# object's thread. The delegated method will return a future in the
|
391
|
+
# `:pending` state and the method call will have been scheduled on the
|
392
|
+
# object's thread. The final disposition of the method call can be obtained
|
393
|
+
# by inspecting the returned future.
|
198
394
|
#
|
199
395
|
# @!macro [attach] async_thread_safety_warning
|
200
396
|
# @note The method call is guaranteed to be thread safe with respect to
|
201
397
|
# all other method calls against the same object that are called with
|
202
398
|
# either `async` or `await`. The mutable nature of Ruby references
|
203
399
|
# (and object orientation in general) prevent any other thread safety
|
204
|
-
# guarantees. Do NOT mix
|
205
|
-
#
|
206
|
-
# between threads.
|
400
|
+
# guarantees. Do NOT mix direct method calls with delegated method calls.
|
401
|
+
# Use *only* delegated method calls when sharing the object between threads.
|
207
402
|
#
|
208
403
|
# @return [Concurrent::IVar] the pending result of the asynchronous operation
|
209
404
|
#
|
210
|
-
# @raise [NameError] the object does not respond to
|
211
|
-
# @raise [ArgumentError] the given `args` do not match the arity of
|
212
|
-
#
|
213
|
-
# @see Concurrent::IVar
|
405
|
+
# @raise [NameError] the object does not respond to the requested method
|
406
|
+
# @raise [ArgumentError] the given `args` do not match the arity of
|
407
|
+
# the requested method
|
214
408
|
def async
|
215
|
-
@__async_delegator__
|
409
|
+
@__async_delegator__
|
216
410
|
end
|
411
|
+
alias_method :cast, :async
|
217
412
|
|
218
413
|
# Causes the chained method call to be performed synchronously on the
|
219
|
-
# current thread. The
|
220
|
-
#
|
221
|
-
#
|
222
|
-
#
|
223
|
-
#
|
224
|
-
# Before scheduling the method on the global thread pool a best-effort
|
225
|
-
# attempt will be made to validate that the method exists on the object
|
226
|
-
# and that the given arguments match the arity of the requested function.
|
227
|
-
# Due to the dynamic nature of Ruby and limitations of its reflection
|
228
|
-
# library, some edge cases will be missed. For more information see
|
229
|
-
# the documentation for the `validate_argc` method.
|
414
|
+
# current thread. The delegated will return a future in either the
|
415
|
+
# `:fulfilled` or `:rejected` state and the delegated method will have
|
416
|
+
# completed. The final disposition of the delegated method can be obtained
|
417
|
+
# by inspecting the returned future.
|
230
418
|
#
|
231
419
|
# @!macro async_thread_safety_warning
|
232
420
|
#
|
233
421
|
# @return [Concurrent::IVar] the completed result of the synchronous operation
|
234
422
|
#
|
235
|
-
# @raise [NameError] the object does not respond to
|
236
|
-
# @raise [ArgumentError] the given `args` do not match the arity of
|
237
|
-
#
|
238
|
-
# @see Concurrent::IVar
|
423
|
+
# @raise [NameError] the object does not respond to the requested method
|
424
|
+
# @raise [ArgumentError] the given `args` do not match the arity of the
|
425
|
+
# requested method
|
239
426
|
def await
|
240
|
-
@__await_delegator__
|
241
|
-
end
|
242
|
-
|
243
|
-
# Set a new executor.
|
244
|
-
#
|
245
|
-
# @raise [ArgumentError] executor has already been set.
|
246
|
-
def executor=(executor)
|
247
|
-
@__async_executor__.reconfigure { executor } or
|
248
|
-
raise ArgumentError.new('executor has already been set')
|
427
|
+
@__await_delegator__
|
249
428
|
end
|
429
|
+
alias_method :call, :await
|
250
430
|
|
251
431
|
# Initialize the internal serializer and other stnchronization mechanisms.
|
252
432
|
#
|
253
433
|
# @note This method *must* be called immediately upon object construction.
|
254
434
|
# This is the only way thread-safe initialization can be guaranteed.
|
255
435
|
#
|
256
|
-
# @raise [Concurrent::InitializationError] when called more than once
|
257
|
-
#
|
258
|
-
# @!visibility private
|
259
|
-
# @deprecated
|
260
|
-
def init_mutex
|
261
|
-
deprecated 'mutex synchronization now happens automatically'
|
262
|
-
init_synchronization
|
263
|
-
rescue InitializationError
|
264
|
-
# suppress
|
265
|
-
end
|
266
|
-
|
267
|
-
private
|
268
|
-
|
269
|
-
# Initialize the internal serializer and other stnchronization mechanisms.
|
270
|
-
#
|
271
|
-
# @note This method *must* be called immediately upon object construction.
|
272
|
-
# This is the only way thread-safe initialization can be guaranteed.
|
273
|
-
#
|
274
|
-
# @raise [Concurrent::InitializationError] when called more than once
|
275
|
-
#
|
276
436
|
# @!visibility private
|
277
437
|
def init_synchronization
|
278
438
|
return self if @__async_initialized__
|
279
|
-
|
280
439
|
@__async_initialized__ = true
|
281
|
-
|
282
|
-
|
283
|
-
@__async_executor__ = Delay.new {
|
284
|
-
Concurrent.global_io_executor
|
285
|
-
}
|
286
|
-
|
287
|
-
@__await_delegator__ = Delay.new {
|
288
|
-
AsyncDelegator.new(self, Delay.new{ Concurrent::ImmediateExecutor.new }, serializer, true)
|
289
|
-
}
|
290
|
-
|
291
|
-
@__async_delegator__ = Delay.new {
|
292
|
-
AsyncDelegator.new(self, @__async_executor__, serializer, false)
|
293
|
-
}
|
294
|
-
|
440
|
+
@__async_delegator__ = AsyncDelegator.new(self)
|
441
|
+
@__await_delegator__ = AwaitDelegator.new(@__async_delegator__)
|
295
442
|
self
|
296
443
|
end
|
297
444
|
end
|