concurrent-ruby 0.9.2-java → 1.0.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +49 -1
  3. data/README.md +86 -120
  4. data/lib/concurrent.rb +14 -5
  5. data/lib/concurrent/agent.rb +587 -0
  6. data/lib/concurrent/array.rb +39 -0
  7. data/lib/concurrent/async.rb +296 -149
  8. data/lib/concurrent/atom.rb +135 -45
  9. data/lib/concurrent/atomic/abstract_thread_local_var.rb +38 -0
  10. data/lib/concurrent/atomic/atomic_boolean.rb +83 -118
  11. data/lib/concurrent/atomic/atomic_fixnum.rb +101 -163
  12. data/lib/concurrent/atomic/atomic_reference.rb +1 -8
  13. data/lib/concurrent/atomic/count_down_latch.rb +62 -103
  14. data/lib/concurrent/atomic/cyclic_barrier.rb +3 -1
  15. data/lib/concurrent/atomic/event.rb +1 -1
  16. data/lib/concurrent/atomic/java_count_down_latch.rb +39 -0
  17. data/lib/concurrent/atomic/java_thread_local_var.rb +50 -0
  18. data/lib/concurrent/atomic/mutex_atomic_boolean.rb +60 -0
  19. data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +91 -0
  20. data/lib/concurrent/atomic/mutex_count_down_latch.rb +43 -0
  21. data/lib/concurrent/atomic/mutex_semaphore.rb +115 -0
  22. data/lib/concurrent/atomic/read_write_lock.rb +5 -4
  23. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
  24. data/lib/concurrent/atomic/ruby_thread_local_var.rb +172 -0
  25. data/lib/concurrent/atomic/semaphore.rb +84 -178
  26. data/lib/concurrent/atomic/thread_local_var.rb +65 -294
  27. data/lib/concurrent/atomic_reference/jruby+truffle.rb +1 -0
  28. data/lib/concurrent/atomic_reference/jruby.rb +1 -1
  29. data/lib/concurrent/atomic_reference/mutex_atomic.rb +14 -8
  30. data/lib/concurrent/atomic_reference/ruby.rb +1 -1
  31. data/lib/concurrent/atomics.rb +7 -37
  32. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +7 -15
  33. data/lib/concurrent/collection/copy_on_write_observer_set.rb +7 -15
  34. data/lib/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
  35. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
  36. data/lib/concurrent/collection/map/mri_map_backend.rb +66 -0
  37. data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +144 -0
  38. data/lib/concurrent/collection/map/synchronized_map_backend.rb +86 -0
  39. data/lib/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
  40. data/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb +150 -0
  41. data/lib/concurrent/concern/dereferenceable.rb +9 -24
  42. data/lib/concurrent/concern/logging.rb +1 -1
  43. data/lib/concurrent/concern/obligation.rb +11 -20
  44. data/lib/concurrent/concern/observable.rb +38 -13
  45. data/lib/concurrent/configuration.rb +23 -152
  46. data/lib/concurrent/constants.rb +8 -0
  47. data/lib/concurrent/delay.rb +14 -12
  48. data/lib/concurrent/exchanger.rb +339 -41
  49. data/lib/concurrent/executor/abstract_executor_service.rb +134 -0
  50. data/lib/concurrent/executor/executor_service.rb +23 -359
  51. data/lib/concurrent/executor/immediate_executor.rb +3 -2
  52. data/lib/concurrent/executor/java_executor_service.rb +100 -0
  53. data/lib/concurrent/executor/java_single_thread_executor.rb +3 -3
  54. data/lib/concurrent/executor/java_thread_pool_executor.rb +3 -4
  55. data/lib/concurrent/executor/ruby_executor_service.rb +78 -0
  56. data/lib/concurrent/executor/ruby_single_thread_executor.rb +10 -66
  57. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +25 -22
  58. data/lib/concurrent/executor/safe_task_executor.rb +6 -7
  59. data/lib/concurrent/executor/serial_executor_service.rb +34 -0
  60. data/lib/concurrent/executor/serialized_execution.rb +10 -33
  61. data/lib/concurrent/executor/serialized_execution_delegator.rb +28 -0
  62. data/lib/concurrent/executor/simple_executor_service.rb +1 -10
  63. data/lib/concurrent/executor/single_thread_executor.rb +20 -10
  64. data/lib/concurrent/executor/timer_set.rb +8 -10
  65. data/lib/concurrent/executors.rb +12 -2
  66. data/lib/concurrent/future.rb +6 -4
  67. data/lib/concurrent/hash.rb +35 -0
  68. data/lib/concurrent/immutable_struct.rb +5 -1
  69. data/lib/concurrent/ivar.rb +12 -16
  70. data/lib/concurrent/lazy_register.rb +11 -8
  71. data/lib/concurrent/map.rb +180 -0
  72. data/lib/concurrent/maybe.rb +6 -3
  73. data/lib/concurrent/mutable_struct.rb +7 -6
  74. data/lib/concurrent/mvar.rb +26 -2
  75. data/lib/concurrent/{executor/executor.rb → options.rb} +5 -29
  76. data/lib/concurrent/promise.rb +7 -5
  77. data/lib/concurrent/scheduled_task.rb +13 -71
  78. data/lib/concurrent/settable_struct.rb +5 -4
  79. data/lib/concurrent/synchronization.rb +15 -3
  80. data/lib/concurrent/synchronization/abstract_lockable_object.rb +98 -0
  81. data/lib/concurrent/synchronization/abstract_object.rb +7 -146
  82. data/lib/concurrent/synchronization/abstract_struct.rb +2 -3
  83. data/lib/concurrent/synchronization/condition.rb +6 -4
  84. data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  85. data/lib/concurrent/synchronization/jruby_object.rb +44 -0
  86. data/lib/concurrent/synchronization/lock.rb +3 -2
  87. data/lib/concurrent/synchronization/lockable_object.rb +72 -0
  88. data/lib/concurrent/synchronization/mri_lockable_object.rb +71 -0
  89. data/lib/concurrent/synchronization/mri_object.rb +43 -0
  90. data/lib/concurrent/synchronization/object.rb +140 -73
  91. data/lib/concurrent/synchronization/rbx_lockable_object.rb +65 -0
  92. data/lib/concurrent/synchronization/rbx_object.rb +30 -73
  93. data/lib/concurrent/synchronization/volatile.rb +34 -0
  94. data/lib/concurrent/thread_safe/synchronized_delegator.rb +50 -0
  95. data/lib/concurrent/thread_safe/util.rb +14 -0
  96. data/lib/concurrent/thread_safe/util/adder.rb +74 -0
  97. data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +30 -0
  98. data/lib/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
  99. data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
  100. data/lib/concurrent/thread_safe/util/striped64.rb +241 -0
  101. data/lib/concurrent/thread_safe/util/volatile.rb +75 -0
  102. data/lib/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
  103. data/lib/concurrent/timer_task.rb +3 -4
  104. data/lib/concurrent/tuple.rb +86 -0
  105. data/lib/concurrent/tvar.rb +5 -1
  106. data/lib/concurrent/utility/at_exit.rb +1 -1
  107. data/lib/concurrent/utility/engine.rb +4 -0
  108. data/lib/concurrent/utility/monotonic_time.rb +3 -4
  109. data/lib/concurrent/utility/native_extension_loader.rb +50 -30
  110. data/lib/concurrent/version.rb +2 -2
  111. data/lib/concurrent_ruby_ext.jar +0 -0
  112. metadata +47 -12
  113. data/lib/concurrent/atomic/condition.rb +0 -78
  114. data/lib/concurrent/collection/priority_queue.rb +0 -360
  115. data/lib/concurrent/synchronization/java_object.rb +0 -34
  116. data/lib/concurrent/synchronization/monitor_object.rb +0 -27
  117. data/lib/concurrent/synchronization/mutex_object.rb +0 -43
  118. data/lib/concurrent/utilities.rb +0 -5
  119. data/lib/concurrent/utility/timeout.rb +0 -39
  120. data/lib/concurrent/utility/timer.rb +0 -26
  121. 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
+
@@ -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/executor/immediate_executor'
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 any standard
13
- # class/object or object.
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/object
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
- # Stateful, mutable objects must be managed carefully when used asynchronously.
23
- # But Ruby is an object-oriented language so designing with objects and classes
24
- # plays to Ruby's strengths and is often more natural to many Ruby programmers.
25
- # The `Async` module is a way to mix simple yet powerful asynchronous capabilities
26
- # into any plain old Ruby object or class. These capabilities provide a reasonable
27
- # level of thread safe guarantees when used correctly.
28
- #
29
- # When this module is mixed into a class or object it provides to new methods:
30
- # `async` and `await`. These methods are thread safe with respect to the enclosing
31
- # object. The former method allows methods to be called asynchronously by posting
32
- # to the global thread pool. The latter allows a method to be called synchronously
33
- # on the current thread but does so safely with respect to any pending asynchronous
34
- # method calls. Both methods return an `IVar` which can be inspected for
35
- # the result of the method call. Calling a method with `async` will return a
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
- # Very loosely based on the `async` and `await` keywords in C#.
39
- #
40
- # ### An Important Note About Thread Safe Guarantees
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 synchronous method calls. Use only synchronous calls
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 any methods
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
- # > With careful programming it is possible to switch back and forth but it's
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::IVar
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] bloc Optional block to pass to the object's initializer.
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
- # @param [Concurrent::Delay] executor a `Delay` wrapping the executor on which to execute delegated method calls
143
- # @param [Concurrent::SerializedExecution] serializer the serializer to use when delegating method calls
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
- @executor = executor
148
- @serializer = serializer
149
- @blocking = blocking
310
+ @queue = []
311
+ @executor = Concurrent.global_io_executor
150
312
  end
151
313
 
152
- # Delegates method calls to the wrapped object. For performance,
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
- self.define_singleton_method(method) do |*args2|
168
- Async::validate_argc(@delegate, method, *args2)
169
- ivar = Concurrent::IVar.new
170
- @serializer.post(@executor.value) do
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
- self.send(method, *args)
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
- # Causes the chained method call to be performed asynchronously on the
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
- # Before scheduling the method on the global thread pool a best-effort
193
- # attempt will be made to validate that the method exists on the object
194
- # and that the given arguments match the arity of the requested function.
195
- # Due to the dynamic nature of Ruby and limitations of its reflection
196
- # library, some edge cases will be missed. For more information see
197
- # the documentation for the `validate_argc` method.
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 non-protected method calls with protected
205
- # method call. Use *only* protected method calls when sharing the object
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 `method` method
211
- # @raise [ArgumentError] the given `args` do not match the arity of `method`
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__.value
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 method called by this method will return an
220
- # `IVar` object in either the `:fulfilled` or `rejected` state and the
221
- # method call will have completed. The final disposition of the
222
- # method call can be obtained by inspecting the returned `IVar`.
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 `method` method
236
- # @raise [ArgumentError] the given `args` do not match the arity of `method`
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__.value
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
- serializer = Concurrent::SerializedExecution.new
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