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.
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