concurrent-ruby 1.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (143) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +478 -0
  3. data/Gemfile +41 -0
  4. data/LICENSE.md +23 -0
  5. data/README.md +381 -0
  6. data/Rakefile +327 -0
  7. data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
  9. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
  10. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
  11. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
  12. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +159 -0
  13. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +307 -0
  14. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
  15. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
  16. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
  17. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
  18. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
  19. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
  20. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
  21. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
  22. data/lib/concurrent-ruby.rb +1 -0
  23. data/lib/concurrent.rb +134 -0
  24. data/lib/concurrent/agent.rb +587 -0
  25. data/lib/concurrent/array.rb +66 -0
  26. data/lib/concurrent/async.rb +459 -0
  27. data/lib/concurrent/atom.rb +222 -0
  28. data/lib/concurrent/atomic/abstract_thread_local_var.rb +66 -0
  29. data/lib/concurrent/atomic/atomic_boolean.rb +126 -0
  30. data/lib/concurrent/atomic/atomic_fixnum.rb +143 -0
  31. data/lib/concurrent/atomic/atomic_markable_reference.rb +164 -0
  32. data/lib/concurrent/atomic/atomic_reference.rb +204 -0
  33. data/lib/concurrent/atomic/count_down_latch.rb +100 -0
  34. data/lib/concurrent/atomic/cyclic_barrier.rb +128 -0
  35. data/lib/concurrent/atomic/event.rb +109 -0
  36. data/lib/concurrent/atomic/java_count_down_latch.rb +42 -0
  37. data/lib/concurrent/atomic/java_thread_local_var.rb +37 -0
  38. data/lib/concurrent/atomic/mutex_atomic_boolean.rb +62 -0
  39. data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +75 -0
  40. data/lib/concurrent/atomic/mutex_count_down_latch.rb +44 -0
  41. data/lib/concurrent/atomic/mutex_semaphore.rb +115 -0
  42. data/lib/concurrent/atomic/read_write_lock.rb +254 -0
  43. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +379 -0
  44. data/lib/concurrent/atomic/ruby_thread_local_var.rb +161 -0
  45. data/lib/concurrent/atomic/semaphore.rb +145 -0
  46. data/lib/concurrent/atomic/thread_local_var.rb +104 -0
  47. data/lib/concurrent/atomic_reference/mutex_atomic.rb +56 -0
  48. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +28 -0
  49. data/lib/concurrent/atomics.rb +10 -0
  50. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +107 -0
  51. data/lib/concurrent/collection/copy_on_write_observer_set.rb +111 -0
  52. data/lib/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
  53. data/lib/concurrent/collection/lock_free_stack.rb +158 -0
  54. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
  55. data/lib/concurrent/collection/map/mri_map_backend.rb +66 -0
  56. data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +140 -0
  57. data/lib/concurrent/collection/map/synchronized_map_backend.rb +82 -0
  58. data/lib/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
  59. data/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb +150 -0
  60. data/lib/concurrent/concern/deprecation.rb +34 -0
  61. data/lib/concurrent/concern/dereferenceable.rb +73 -0
  62. data/lib/concurrent/concern/logging.rb +32 -0
  63. data/lib/concurrent/concern/obligation.rb +220 -0
  64. data/lib/concurrent/concern/observable.rb +110 -0
  65. data/lib/concurrent/concurrent_ruby.jar +0 -0
  66. data/lib/concurrent/configuration.rb +184 -0
  67. data/lib/concurrent/constants.rb +8 -0
  68. data/lib/concurrent/dataflow.rb +81 -0
  69. data/lib/concurrent/delay.rb +199 -0
  70. data/lib/concurrent/errors.rb +69 -0
  71. data/lib/concurrent/exchanger.rb +352 -0
  72. data/lib/concurrent/executor/abstract_executor_service.rb +134 -0
  73. data/lib/concurrent/executor/cached_thread_pool.rb +62 -0
  74. data/lib/concurrent/executor/executor_service.rb +185 -0
  75. data/lib/concurrent/executor/fixed_thread_pool.rb +206 -0
  76. data/lib/concurrent/executor/immediate_executor.rb +66 -0
  77. data/lib/concurrent/executor/indirect_immediate_executor.rb +44 -0
  78. data/lib/concurrent/executor/java_executor_service.rb +91 -0
  79. data/lib/concurrent/executor/java_single_thread_executor.rb +29 -0
  80. data/lib/concurrent/executor/java_thread_pool_executor.rb +123 -0
  81. data/lib/concurrent/executor/ruby_executor_service.rb +78 -0
  82. data/lib/concurrent/executor/ruby_single_thread_executor.rb +22 -0
  83. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +362 -0
  84. data/lib/concurrent/executor/safe_task_executor.rb +35 -0
  85. data/lib/concurrent/executor/serial_executor_service.rb +34 -0
  86. data/lib/concurrent/executor/serialized_execution.rb +107 -0
  87. data/lib/concurrent/executor/serialized_execution_delegator.rb +28 -0
  88. data/lib/concurrent/executor/simple_executor_service.rb +100 -0
  89. data/lib/concurrent/executor/single_thread_executor.rb +56 -0
  90. data/lib/concurrent/executor/thread_pool_executor.rb +87 -0
  91. data/lib/concurrent/executor/timer_set.rb +173 -0
  92. data/lib/concurrent/executors.rb +20 -0
  93. data/lib/concurrent/future.rb +141 -0
  94. data/lib/concurrent/hash.rb +59 -0
  95. data/lib/concurrent/immutable_struct.rb +93 -0
  96. data/lib/concurrent/ivar.rb +207 -0
  97. data/lib/concurrent/map.rb +337 -0
  98. data/lib/concurrent/maybe.rb +229 -0
  99. data/lib/concurrent/mutable_struct.rb +229 -0
  100. data/lib/concurrent/mvar.rb +242 -0
  101. data/lib/concurrent/options.rb +42 -0
  102. data/lib/concurrent/promise.rb +579 -0
  103. data/lib/concurrent/promises.rb +2167 -0
  104. data/lib/concurrent/re_include.rb +58 -0
  105. data/lib/concurrent/scheduled_task.rb +318 -0
  106. data/lib/concurrent/set.rb +66 -0
  107. data/lib/concurrent/settable_struct.rb +129 -0
  108. data/lib/concurrent/synchronization.rb +30 -0
  109. data/lib/concurrent/synchronization/abstract_lockable_object.rb +98 -0
  110. data/lib/concurrent/synchronization/abstract_object.rb +24 -0
  111. data/lib/concurrent/synchronization/abstract_struct.rb +160 -0
  112. data/lib/concurrent/synchronization/condition.rb +60 -0
  113. data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  114. data/lib/concurrent/synchronization/jruby_object.rb +45 -0
  115. data/lib/concurrent/synchronization/lock.rb +36 -0
  116. data/lib/concurrent/synchronization/lockable_object.rb +74 -0
  117. data/lib/concurrent/synchronization/mri_object.rb +44 -0
  118. data/lib/concurrent/synchronization/mutex_lockable_object.rb +76 -0
  119. data/lib/concurrent/synchronization/object.rb +183 -0
  120. data/lib/concurrent/synchronization/rbx_lockable_object.rb +65 -0
  121. data/lib/concurrent/synchronization/rbx_object.rb +49 -0
  122. data/lib/concurrent/synchronization/truffleruby_object.rb +47 -0
  123. data/lib/concurrent/synchronization/volatile.rb +36 -0
  124. data/lib/concurrent/thread_safe/synchronized_delegator.rb +50 -0
  125. data/lib/concurrent/thread_safe/util.rb +16 -0
  126. data/lib/concurrent/thread_safe/util/adder.rb +74 -0
  127. data/lib/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
  128. data/lib/concurrent/thread_safe/util/data_structures.rb +63 -0
  129. data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
  130. data/lib/concurrent/thread_safe/util/striped64.rb +246 -0
  131. data/lib/concurrent/thread_safe/util/volatile.rb +75 -0
  132. data/lib/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
  133. data/lib/concurrent/timer_task.rb +334 -0
  134. data/lib/concurrent/tuple.rb +86 -0
  135. data/lib/concurrent/tvar.rb +258 -0
  136. data/lib/concurrent/utility/at_exit.rb +97 -0
  137. data/lib/concurrent/utility/engine.rb +56 -0
  138. data/lib/concurrent/utility/monotonic_time.rb +58 -0
  139. data/lib/concurrent/utility/native_extension_loader.rb +79 -0
  140. data/lib/concurrent/utility/native_integer.rb +53 -0
  141. data/lib/concurrent/utility/processor_counter.rb +158 -0
  142. data/lib/concurrent/version.rb +3 -0
  143. metadata +193 -0
@@ -0,0 +1,66 @@
1
+ require 'concurrent/utility/engine'
2
+ require 'concurrent/thread_safe/util'
3
+
4
+ module Concurrent
5
+
6
+ # @!macro concurrent_array
7
+ #
8
+ # A thread-safe subclass of Array. This version locks against the object
9
+ # itself for every method call, ensuring only one thread can be reading
10
+ # or writing at a time. This includes iteration methods like `#each`.
11
+ #
12
+ # @note `a += b` is **not** a **thread-safe** operation on
13
+ # `Concurrent::Array`. It reads array `a`, then it creates new `Concurrent::Array`
14
+ # which is concatenation of `a` and `b`, then it writes the concatenation to `a`.
15
+ # The read and write are independent operations they do not form a single atomic
16
+ # operation therefore when two `+=` operations are executed concurrently updates
17
+ # may be lost. Use `#concat` instead.
18
+ #
19
+ # @see http://ruby-doc.org/core-2.2.0/Array.html Ruby standard library `Array`
20
+
21
+ # @!macro internal_implementation_note
22
+ ArrayImplementation = case
23
+ when Concurrent.on_cruby?
24
+ # Array is thread-safe in practice because CRuby runs
25
+ # threads one at a time and does not do context
26
+ # switching during the execution of C functions.
27
+ ::Array
28
+
29
+ when Concurrent.on_jruby?
30
+ require 'jruby/synchronized'
31
+
32
+ class JRubyArray < ::Array
33
+ include JRuby::Synchronized
34
+ end
35
+ JRubyArray
36
+
37
+ when Concurrent.on_rbx?
38
+ require 'monitor'
39
+ require 'concurrent/thread_safe/util/data_structures'
40
+
41
+ class RbxArray < ::Array
42
+ end
43
+
44
+ ThreadSafe::Util.make_synchronized_on_rbx RbxArray
45
+ RbxArray
46
+
47
+ when Concurrent.on_truffleruby?
48
+ require 'concurrent/thread_safe/util/data_structures'
49
+
50
+ class TruffleRubyArray < ::Array
51
+ end
52
+
53
+ ThreadSafe::Util.make_synchronized_on_truffleruby TruffleRubyArray
54
+ TruffleRubyArray
55
+
56
+ else
57
+ warn 'Possibly unsupported Ruby implementation'
58
+ ::Array
59
+ end
60
+ private_constant :ArrayImplementation
61
+
62
+ # @!macro concurrent_array
63
+ class Array < ArrayImplementation
64
+ end
65
+
66
+ end
@@ -0,0 +1,459 @@
1
+ require 'concurrent/configuration'
2
+ require 'concurrent/ivar'
3
+ require 'concurrent/synchronization/lockable_object'
4
+
5
+ module Concurrent
6
+
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
+ # ```cucumber
16
+ # Feature:
17
+ # As a stateful, plain old Ruby class
18
+ # I want safe, asynchronous behavior
19
+ # So my long-running methods don't block the main thread
20
+ # ```
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
+ # returns 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 critical 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
89
+ # `:pending` `IVar` whereas `await` will return a `:complete` `IVar`.
90
+ #
91
+ # ```
92
+ # class Echo
93
+ # include Concurrent::Async
94
+ #
95
+ # def echo(msg)
96
+ # print "#{msg}\n"
97
+ # end
98
+ # end
99
+ #
100
+ # horn = Echo.new
101
+ # horn.echo('zero') # synchronous, not thread-safe
102
+ # # returns the actual return value of the method
103
+ #
104
+ # horn.async.echo('one') # asynchronous, non-blocking, thread-safe
105
+ # # returns an IVar in the :pending state
106
+ #
107
+ # horn.await.echo('two') # synchronous, blocking, thread-safe
108
+ # # returns an IVar in the :complete state
109
+ # ```
110
+ #
111
+ # ## Let It Fail
112
+ #
113
+ # The `async` and `await` proxy methods have built-in error protection based
114
+ # on Erlang's famous "let it fail" philosophy. Instance methods should not be
115
+ # programmed defensively. When an exception is raised by a delegated method
116
+ # the proxy will rescue the exception, expose it to the caller as the `reason`
117
+ # attribute of the returned future, then process the next method call.
118
+ #
119
+ # ## Calling Methods Internally
120
+ #
121
+ # External method calls should *always* use the `async` and `await` proxy
122
+ # methods. When one method calls another method, the `async` proxy should
123
+ # rarely be used and the `await` proxy should *never* be used.
124
+ #
125
+ # When an object calls one of its own methods using the `await` proxy the
126
+ # second call will be enqueued *behind* the currently running method call.
127
+ # Any attempt to wait on the result will fail as the second call will never
128
+ # run until after the current call completes.
129
+ #
130
+ # Calling a method using the `await` proxy from within a method that was
131
+ # itself called using `async` or `await` will irreversibly deadlock the
132
+ # object. Do *not* do this, ever.
133
+ #
134
+ # ## Instance Variables and Attribute Accessors
135
+ #
136
+ # Instance variables do not need to be thread-safe so long as they are private.
137
+ # Asynchronous method calls are processed in the order they are received and
138
+ # are processed one at a time. Therefore private instance variables can only
139
+ # be accessed by one thread at a time. This is inherently thread-safe.
140
+ #
141
+ # When using private instance variables within asynchronous methods, the best
142
+ # practice is to read the instance variable into a local variable at the start
143
+ # of the method then update the instance variable at the *end* of the method.
144
+ # This way, should an exception be raised during method execution the internal
145
+ # state of the object 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 state *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, preferably 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, preferably
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
+ #
206
+ # > Thread safe guarantees can only be made when asynchronous method calls
207
+ # > are not mixed with direct method calls. Use only direct method calls
208
+ # > when the object is used exclusively on a single thread. Use only
209
+ # > `async` and `await` when the object is shared between threads. Once you
210
+ # > call a method using `async` or `await`, you should no longer call methods
211
+ # > directly on the object. Use `async` and `await` exclusively from then on.
212
+ #
213
+ # @example
214
+ #
215
+ # class Echo
216
+ # include Concurrent::Async
217
+ #
218
+ # def echo(msg)
219
+ # print "#{msg}\n"
220
+ # end
221
+ # end
222
+ #
223
+ # horn = Echo.new
224
+ # horn.echo('zero') # synchronous, not thread-safe
225
+ # # returns the actual return value of the method
226
+ #
227
+ # horn.async.echo('one') # asynchronous, non-blocking, thread-safe
228
+ # # returns an IVar in the :pending state
229
+ #
230
+ # horn.await.echo('two') # synchronous, blocking, thread-safe
231
+ # # returns an IVar in the :complete state
232
+ #
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/
237
+ module Async
238
+
239
+ # @!method self.new(*args, &block)
240
+ #
241
+ # Instanciate a new object and ensure proper initialization of the
242
+ # synchronization mechanisms.
243
+ #
244
+ # @param [Array<Object>] args Zero or more arguments to be passed to the
245
+ # object's initializer.
246
+ # @param [Proc] block Optional block to pass to the object's initializer.
247
+ # @return [Object] A properly initialized object of the asynchronous class.
248
+
249
+ # Check for the presence of a method on an object and determine if a given
250
+ # set of arguments matches the required arity.
251
+ #
252
+ # @param [Object] obj the object to check against
253
+ # @param [Symbol] method the method to check the object for
254
+ # @param [Array] args zero or more arguments for the arity check
255
+ #
256
+ # @raise [NameError] the object does not respond to `method` method
257
+ # @raise [ArgumentError] the given `args` do not match the arity of `method`
258
+ #
259
+ # @note This check is imperfect because of the way Ruby reports the arity of
260
+ # methods with a variable number of arguments. It is possible to determine
261
+ # if too few arguments are given but impossible to determine if too many
262
+ # arguments are given. This check may also fail to recognize dynamic behavior
263
+ # of the object, such as methods simulated with `method_missing`.
264
+ #
265
+ # @see http://www.ruby-doc.org/core-2.1.1/Method.html#method-i-arity Method#arity
266
+ # @see http://ruby-doc.org/core-2.1.0/Object.html#method-i-respond_to-3F Object#respond_to?
267
+ # @see http://www.ruby-doc.org/core-2.1.0/BasicObject.html#method-i-method_missing BasicObject#method_missing
268
+ #
269
+ # @!visibility private
270
+ def self.validate_argc(obj, method, *args)
271
+ argc = args.length
272
+ arity = obj.method(method).arity
273
+
274
+ if arity >= 0 && argc != arity
275
+ raise ArgumentError.new("wrong number of arguments (#{argc} for #{arity})")
276
+ elsif arity < 0 && (arity = (arity + 1).abs) > argc
277
+ raise ArgumentError.new("wrong number of arguments (#{argc} for #{arity}..*)")
278
+ end
279
+ end
280
+
281
+ # @!visibility private
282
+ def self.included(base)
283
+ base.singleton_class.send(:alias_method, :original_new, :new)
284
+ base.extend(ClassMethods)
285
+ super(base)
286
+ end
287
+
288
+ # @!visibility private
289
+ module ClassMethods
290
+ def new(*args, &block)
291
+ obj = original_new(*args, &block)
292
+ obj.send(:init_synchronization)
293
+ obj
294
+ end
295
+ end
296
+ private_constant :ClassMethods
297
+
298
+ # Delegates asynchronous, thread-safe method calls to the wrapped object.
299
+ #
300
+ # @!visibility private
301
+ class AsyncDelegator < Synchronization::LockableObject
302
+ safe_initialization!
303
+
304
+ # Create a new delegator object wrapping the given delegate.
305
+ #
306
+ # @param [Object] delegate the object to wrap and delegate method calls to
307
+ def initialize(delegate)
308
+ super()
309
+ @delegate = delegate
310
+ @queue = []
311
+ @executor = Concurrent.global_io_executor
312
+ end
313
+
314
+ # Delegates method calls to the wrapped object.
315
+ #
316
+ # @param [Symbol] method the method being called
317
+ # @param [Array] args zero or more arguments to the method
318
+ #
319
+ # @return [IVar] the result of the method call
320
+ #
321
+ # @raise [NameError] the object does not respond to `method` method
322
+ # @raise [ArgumentError] the given `args` do not match the arity of `method`
323
+ def method_missing(method, *args, &block)
324
+ super unless @delegate.respond_to?(method)
325
+ Async::validate_argc(@delegate, method, *args)
326
+
327
+ ivar = Concurrent::IVar.new
328
+ synchronize do
329
+ @queue.push [ivar, method, args, block]
330
+ @executor.post { perform } if @queue.length == 1
331
+ end
332
+
333
+ ivar
334
+ end
335
+
336
+ # Check whether the method is responsive
337
+ #
338
+ # @param [Symbol] method the method being called
339
+ def respond_to_missing?(method, include_private = false)
340
+ @delegate.respond_to?(method) || super
341
+ end
342
+
343
+ # Perform all enqueued tasks.
344
+ #
345
+ # This method must be called from within the executor. It must not be
346
+ # called while already running. It will loop until the queue is empty.
347
+ def perform
348
+ loop do
349
+ ivar, method, args, block = synchronize { @queue.first }
350
+ break unless ivar # queue is empty
351
+
352
+ begin
353
+ ivar.set(@delegate.send(method, *args, &block))
354
+ rescue => error
355
+ ivar.fail(error)
356
+ end
357
+
358
+ synchronize do
359
+ @queue.shift
360
+ return if @queue.empty?
361
+ end
362
+ end
363
+ end
364
+ end
365
+ private_constant :AsyncDelegator
366
+
367
+ # Delegates synchronous, thread-safe method calls to the wrapped object.
368
+ #
369
+ # @!visibility private
370
+ class AwaitDelegator
371
+
372
+ # Create a new delegator object wrapping the given delegate.
373
+ #
374
+ # @param [AsyncDelegator] delegate the object to wrap and delegate method calls to
375
+ def initialize(delegate)
376
+ @delegate = delegate
377
+ end
378
+
379
+ # Delegates method calls to the wrapped object.
380
+ #
381
+ # @param [Symbol] method the method being called
382
+ # @param [Array] args zero or more arguments to the method
383
+ #
384
+ # @return [IVar] the result of the method call
385
+ #
386
+ # @raise [NameError] the object does not respond to `method` method
387
+ # @raise [ArgumentError] the given `args` do not match the arity of `method`
388
+ def method_missing(method, *args, &block)
389
+ ivar = @delegate.send(method, *args, &block)
390
+ ivar.wait
391
+ ivar
392
+ end
393
+
394
+ # Check whether the method is responsive
395
+ #
396
+ # @param [Symbol] method the method being called
397
+ def respond_to_missing?(method, include_private = false)
398
+ @delegate.respond_to?(method) || super
399
+ end
400
+ end
401
+ private_constant :AwaitDelegator
402
+
403
+ # Causes the chained method call to be performed asynchronously on the
404
+ # object's thread. The delegated method will return a future in the
405
+ # `:pending` state and the method call will have been scheduled on the
406
+ # object's thread. The final disposition of the method call can be obtained
407
+ # by inspecting the returned future.
408
+ #
409
+ # @!macro async_thread_safety_warning
410
+ # @note The method call is guaranteed to be thread safe with respect to
411
+ # all other method calls against the same object that are called with
412
+ # either `async` or `await`. The mutable nature of Ruby references
413
+ # (and object orientation in general) prevent any other thread safety
414
+ # guarantees. Do NOT mix direct method calls with delegated method calls.
415
+ # Use *only* delegated method calls when sharing the object between threads.
416
+ #
417
+ # @return [Concurrent::IVar] the pending result of the asynchronous operation
418
+ #
419
+ # @raise [NameError] the object does not respond to the requested method
420
+ # @raise [ArgumentError] the given `args` do not match the arity of
421
+ # the requested method
422
+ def async
423
+ @__async_delegator__
424
+ end
425
+ alias_method :cast, :async
426
+
427
+ # Causes the chained method call to be performed synchronously on the
428
+ # current thread. The delegated will return a future in either the
429
+ # `:fulfilled` or `:rejected` state and the delegated method will have
430
+ # completed. The final disposition of the delegated method can be obtained
431
+ # by inspecting the returned future.
432
+ #
433
+ # @!macro async_thread_safety_warning
434
+ #
435
+ # @return [Concurrent::IVar] the completed result of the synchronous operation
436
+ #
437
+ # @raise [NameError] the object does not respond to the requested method
438
+ # @raise [ArgumentError] the given `args` do not match the arity of the
439
+ # requested method
440
+ def await
441
+ @__await_delegator__
442
+ end
443
+ alias_method :call, :await
444
+
445
+ # Initialize the internal serializer and other stnchronization mechanisms.
446
+ #
447
+ # @note This method *must* be called immediately upon object construction.
448
+ # This is the only way thread-safe initialization can be guaranteed.
449
+ #
450
+ # @!visibility private
451
+ def init_synchronization
452
+ return self if defined?(@__async_initialized__) && @__async_initialized__
453
+ @__async_initialized__ = true
454
+ @__async_delegator__ = AsyncDelegator.new(self)
455
+ @__await_delegator__ = AwaitDelegator.new(@__async_delegator__)
456
+ self
457
+ end
458
+ end
459
+ end