o-concurrent-ruby 1.1.11

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 (142) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +542 -0
  3. data/Gemfile +37 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +404 -0
  6. data/Rakefile +307 -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 +189 -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/concurrent/agent.rb +587 -0
  23. data/lib/concurrent-ruby/concurrent/array.rb +66 -0
  24. data/lib/concurrent-ruby/concurrent/async.rb +449 -0
  25. data/lib/concurrent-ruby/concurrent/atom.rb +222 -0
  26. data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +66 -0
  27. data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +126 -0
  28. data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +143 -0
  29. data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +164 -0
  30. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +205 -0
  31. data/lib/concurrent-ruby/concurrent/atomic/count_down_latch.rb +100 -0
  32. data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +128 -0
  33. data/lib/concurrent-ruby/concurrent/atomic/event.rb +109 -0
  34. data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +42 -0
  35. data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +37 -0
  36. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +62 -0
  37. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +75 -0
  38. data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +44 -0
  39. data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +131 -0
  40. data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +254 -0
  41. data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +377 -0
  42. data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +181 -0
  43. data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +166 -0
  44. data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +104 -0
  45. data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +56 -0
  46. data/lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb +28 -0
  47. data/lib/concurrent-ruby/concurrent/atomics.rb +10 -0
  48. data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +107 -0
  49. data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +111 -0
  50. data/lib/concurrent-ruby/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
  51. data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +158 -0
  52. data/lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
  53. data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +66 -0
  54. data/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb +140 -0
  55. data/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb +82 -0
  56. data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
  57. data/lib/concurrent-ruby/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
  58. data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +160 -0
  59. data/lib/concurrent-ruby/concurrent/concern/deprecation.rb +34 -0
  60. data/lib/concurrent-ruby/concurrent/concern/dereferenceable.rb +73 -0
  61. data/lib/concurrent-ruby/concurrent/concern/logging.rb +32 -0
  62. data/lib/concurrent-ruby/concurrent/concern/obligation.rb +220 -0
  63. data/lib/concurrent-ruby/concurrent/concern/observable.rb +110 -0
  64. data/lib/concurrent-ruby/concurrent/configuration.rb +188 -0
  65. data/lib/concurrent-ruby/concurrent/constants.rb +8 -0
  66. data/lib/concurrent-ruby/concurrent/dataflow.rb +81 -0
  67. data/lib/concurrent-ruby/concurrent/delay.rb +199 -0
  68. data/lib/concurrent-ruby/concurrent/errors.rb +69 -0
  69. data/lib/concurrent-ruby/concurrent/exchanger.rb +352 -0
  70. data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +131 -0
  71. data/lib/concurrent-ruby/concurrent/executor/cached_thread_pool.rb +62 -0
  72. data/lib/concurrent-ruby/concurrent/executor/executor_service.rb +185 -0
  73. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +220 -0
  74. data/lib/concurrent-ruby/concurrent/executor/immediate_executor.rb +66 -0
  75. data/lib/concurrent-ruby/concurrent/executor/indirect_immediate_executor.rb +44 -0
  76. data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +103 -0
  77. data/lib/concurrent-ruby/concurrent/executor/java_single_thread_executor.rb +30 -0
  78. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +140 -0
  79. data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +82 -0
  80. data/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb +21 -0
  81. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +368 -0
  82. data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +35 -0
  83. data/lib/concurrent-ruby/concurrent/executor/serial_executor_service.rb +34 -0
  84. data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +107 -0
  85. data/lib/concurrent-ruby/concurrent/executor/serialized_execution_delegator.rb +28 -0
  86. data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +100 -0
  87. data/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb +57 -0
  88. data/lib/concurrent-ruby/concurrent/executor/thread_pool_executor.rb +88 -0
  89. data/lib/concurrent-ruby/concurrent/executor/timer_set.rb +172 -0
  90. data/lib/concurrent-ruby/concurrent/executors.rb +20 -0
  91. data/lib/concurrent-ruby/concurrent/future.rb +141 -0
  92. data/lib/concurrent-ruby/concurrent/hash.rb +59 -0
  93. data/lib/concurrent-ruby/concurrent/immutable_struct.rb +101 -0
  94. data/lib/concurrent-ruby/concurrent/ivar.rb +207 -0
  95. data/lib/concurrent-ruby/concurrent/map.rb +346 -0
  96. data/lib/concurrent-ruby/concurrent/maybe.rb +229 -0
  97. data/lib/concurrent-ruby/concurrent/mutable_struct.rb +239 -0
  98. data/lib/concurrent-ruby/concurrent/mvar.rb +242 -0
  99. data/lib/concurrent-ruby/concurrent/options.rb +42 -0
  100. data/lib/concurrent-ruby/concurrent/promise.rb +580 -0
  101. data/lib/concurrent-ruby/concurrent/promises.rb +2167 -0
  102. data/lib/concurrent-ruby/concurrent/re_include.rb +58 -0
  103. data/lib/concurrent-ruby/concurrent/scheduled_task.rb +331 -0
  104. data/lib/concurrent-ruby/concurrent/set.rb +74 -0
  105. data/lib/concurrent-ruby/concurrent/settable_struct.rb +139 -0
  106. data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +98 -0
  107. data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +24 -0
  108. data/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb +171 -0
  109. data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +60 -0
  110. data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  111. data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +45 -0
  112. data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +36 -0
  113. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +72 -0
  114. data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +44 -0
  115. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +88 -0
  116. data/lib/concurrent-ruby/concurrent/synchronization/object.rb +183 -0
  117. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +71 -0
  118. data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +49 -0
  119. data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +47 -0
  120. data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +36 -0
  121. data/lib/concurrent-ruby/concurrent/synchronization.rb +30 -0
  122. data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +50 -0
  123. data/lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb +74 -0
  124. data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
  125. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +88 -0
  126. data/lib/concurrent-ruby/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
  127. data/lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb +246 -0
  128. data/lib/concurrent-ruby/concurrent/thread_safe/util/volatile.rb +75 -0
  129. data/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
  130. data/lib/concurrent-ruby/concurrent/thread_safe/util.rb +16 -0
  131. data/lib/concurrent-ruby/concurrent/timer_task.rb +311 -0
  132. data/lib/concurrent-ruby/concurrent/tuple.rb +86 -0
  133. data/lib/concurrent-ruby/concurrent/tvar.rb +221 -0
  134. data/lib/concurrent-ruby/concurrent/utility/engine.rb +56 -0
  135. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +90 -0
  136. data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +79 -0
  137. data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +53 -0
  138. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +130 -0
  139. data/lib/concurrent-ruby/concurrent/version.rb +3 -0
  140. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -0
  141. data/lib/concurrent-ruby/concurrent.rb +134 -0
  142. metadata +192 -0
@@ -0,0 +1,449 @@
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
+ # Mixing this module into a class provides each object two proxy methods:
62
+ # `async` and `await`. These methods are thread safe with respect to the
63
+ # enclosing object. The former proxy allows methods to be called
64
+ # asynchronously by posting to the object's internal thread. The latter proxy
65
+ # allows a method to be called synchronously but does so safely with respect
66
+ # to any pending asynchronous method calls and ensures proper ordering. Both
67
+ # methods return a {Concurrent::IVar} which can be inspected for the result
68
+ # of the proxied method call. Calling a method with `async` will return a
69
+ # `:pending` `IVar` whereas `await` will return a `:complete` `IVar`.
70
+ #
71
+ # ```
72
+ # class Echo
73
+ # include Concurrent::Async
74
+ #
75
+ # def echo(msg)
76
+ # print "#{msg}\n"
77
+ # end
78
+ # end
79
+ #
80
+ # horn = Echo.new
81
+ # horn.echo('zero') # synchronous, not thread-safe
82
+ # # returns the actual return value of the method
83
+ #
84
+ # horn.async.echo('one') # asynchronous, non-blocking, thread-safe
85
+ # # returns an IVar in the :pending state
86
+ #
87
+ # horn.await.echo('two') # synchronous, blocking, thread-safe
88
+ # # returns an IVar in the :complete state
89
+ # ```
90
+ #
91
+ # ## Let It Fail
92
+ #
93
+ # The `async` and `await` proxy methods have built-in error protection based
94
+ # on Erlang's famous "let it fail" philosophy. Instance methods should not be
95
+ # programmed defensively. When an exception is raised by a delegated method
96
+ # the proxy will rescue the exception, expose it to the caller as the `reason`
97
+ # attribute of the returned future, then process the next method call.
98
+ #
99
+ # ## Calling Methods Internally
100
+ #
101
+ # External method calls should *always* use the `async` and `await` proxy
102
+ # methods. When one method calls another method, the `async` proxy should
103
+ # rarely be used and the `await` proxy should *never* be used.
104
+ #
105
+ # When an object calls one of its own methods using the `await` proxy the
106
+ # second call will be enqueued *behind* the currently running method call.
107
+ # Any attempt to wait on the result will fail as the second call will never
108
+ # run until after the current call completes.
109
+ #
110
+ # Calling a method using the `await` proxy from within a method that was
111
+ # itself called using `async` or `await` will irreversibly deadlock the
112
+ # object. Do *not* do this, ever.
113
+ #
114
+ # ## Instance Variables and Attribute Accessors
115
+ #
116
+ # Instance variables do not need to be thread-safe so long as they are private.
117
+ # Asynchronous method calls are processed in the order they are received and
118
+ # are processed one at a time. Therefore private instance variables can only
119
+ # be accessed by one thread at a time. This is inherently thread-safe.
120
+ #
121
+ # When using private instance variables within asynchronous methods, the best
122
+ # practice is to read the instance variable into a local variable at the start
123
+ # of the method then update the instance variable at the *end* of the method.
124
+ # This way, should an exception be raised during method execution the internal
125
+ # state of the object will not have been changed.
126
+ #
127
+ # ### Reader Attributes
128
+ #
129
+ # The use of `attr_reader` is discouraged. Internal state exposed externally,
130
+ # when necessary, should be done through accessor methods. The instance
131
+ # variables exposed by these methods *must* be thread-safe, or they must be
132
+ # called using the `async` and `await` proxy methods. These two approaches are
133
+ # subtly different.
134
+ #
135
+ # When internal state is accessed via the `async` and `await` proxy methods,
136
+ # the returned value represents the object's state *at the time the call is
137
+ # processed*, which may *not* be the state of the object at the time the call
138
+ # is made.
139
+ #
140
+ # To get the state *at the current* time, irrespective of an enqueued method
141
+ # calls, a reader method must be called directly. This is inherently unsafe
142
+ # unless the instance variable is itself thread-safe, preferably using one
143
+ # of the thread-safe classes within this library. Because the thread-safe
144
+ # classes within this library are internally-locking or non-locking, they can
145
+ # be safely used from within asynchronous methods without causing deadlocks.
146
+ #
147
+ # Generally speaking, the best practice is to *not* expose internal state via
148
+ # reader methods. The best practice is to simply use the method's return value.
149
+ #
150
+ # ### Writer Attributes
151
+ #
152
+ # Writer attributes should never be used with asynchronous classes. Changing
153
+ # the state externally, even when done in the thread-safe way, is not logically
154
+ # consistent. Changes to state need to be timed with respect to all asynchronous
155
+ # method calls which my be in-process or enqueued. The only safe practice is to
156
+ # pass all necessary data to each method as arguments and let the method update
157
+ # the internal state as necessary.
158
+ #
159
+ # ## Class Constants, Variables, and Methods
160
+ #
161
+ # ### Class Constants
162
+ #
163
+ # Class constants do not need to be thread-safe. Since they are read-only and
164
+ # immutable they may be safely read both externally and from within
165
+ # asynchronous methods.
166
+ #
167
+ # ### Class Variables
168
+ #
169
+ # Class variables should be avoided. Class variables represent shared state.
170
+ # Shared state is anathema to concurrency. Should there be a need to share
171
+ # state using class variables they *must* be thread-safe, preferably
172
+ # using the thread-safe classes within this library. When updating class
173
+ # variables, never assign a new value/object to the variable itself. Assignment
174
+ # is not thread-safe in Ruby. Instead, use the thread-safe update functions
175
+ # of the variable itself to change the value.
176
+ #
177
+ # The best practice is to *never* use class variables with `Async` classes.
178
+ #
179
+ # ### Class Methods
180
+ #
181
+ # Class methods which are pure functions are safe. Class methods which modify
182
+ # class variables should be avoided, for all the reasons listed above.
183
+ #
184
+ # ## An Important Note About Thread Safe Guarantees
185
+ #
186
+ # > Thread safe guarantees can only be made when asynchronous method calls
187
+ # > are not mixed with direct method calls. Use only direct method calls
188
+ # > when the object is used exclusively on a single thread. Use only
189
+ # > `async` and `await` when the object is shared between threads. Once you
190
+ # > call a method using `async` or `await`, you should no longer call methods
191
+ # > directly on the object. Use `async` and `await` exclusively from then on.
192
+ #
193
+ # @example
194
+ #
195
+ # class Echo
196
+ # include Concurrent::Async
197
+ #
198
+ # def echo(msg)
199
+ # print "#{msg}\n"
200
+ # end
201
+ # end
202
+ #
203
+ # horn = Echo.new
204
+ # horn.echo('zero') # synchronous, not thread-safe
205
+ # # returns the actual return value of the method
206
+ #
207
+ # horn.async.echo('one') # asynchronous, non-blocking, thread-safe
208
+ # # returns an IVar in the :pending state
209
+ #
210
+ # horn.await.echo('two') # synchronous, blocking, thread-safe
211
+ # # returns an IVar in the :complete state
212
+ #
213
+ # @see Concurrent::Actor
214
+ # @see https://en.wikipedia.org/wiki/Actor_model "Actor Model" at Wikipedia
215
+ # @see http://www.erlang.org/doc/man/gen_server.html Erlang gen_server
216
+ # @see http://c2.com/cgi/wiki?LetItCrash "Let It Crash" at http://c2.com/
217
+ module Async
218
+
219
+ # @!method self.new(*args, &block)
220
+ #
221
+ # Instanciate a new object and ensure proper initialization of the
222
+ # synchronization mechanisms.
223
+ #
224
+ # @param [Array<Object>] args Zero or more arguments to be passed to the
225
+ # object's initializer.
226
+ # @param [Proc] block Optional block to pass to the object's initializer.
227
+ # @return [Object] A properly initialized object of the asynchronous class.
228
+
229
+ # Check for the presence of a method on an object and determine if a given
230
+ # set of arguments matches the required arity.
231
+ #
232
+ # @param [Object] obj the object to check against
233
+ # @param [Symbol] method the method to check the object for
234
+ # @param [Array] args zero or more arguments for the arity check
235
+ #
236
+ # @raise [NameError] the object does not respond to `method` method
237
+ # @raise [ArgumentError] the given `args` do not match the arity of `method`
238
+ #
239
+ # @note This check is imperfect because of the way Ruby reports the arity of
240
+ # methods with a variable number of arguments. It is possible to determine
241
+ # if too few arguments are given but impossible to determine if too many
242
+ # arguments are given. This check may also fail to recognize dynamic behavior
243
+ # of the object, such as methods simulated with `method_missing`.
244
+ #
245
+ # @see http://www.ruby-doc.org/core-2.1.1/Method.html#method-i-arity Method#arity
246
+ # @see http://ruby-doc.org/core-2.1.0/Object.html#method-i-respond_to-3F Object#respond_to?
247
+ # @see http://www.ruby-doc.org/core-2.1.0/BasicObject.html#method-i-method_missing BasicObject#method_missing
248
+ #
249
+ # @!visibility private
250
+ def self.validate_argc(obj, method, *args)
251
+ argc = args.length
252
+ arity = obj.method(method).arity
253
+
254
+ if arity >= 0 && argc != arity
255
+ raise ArgumentError.new("wrong number of arguments (#{argc} for #{arity})")
256
+ elsif arity < 0 && (arity = (arity + 1).abs) > argc
257
+ raise ArgumentError.new("wrong number of arguments (#{argc} for #{arity}..*)")
258
+ end
259
+ end
260
+
261
+ # @!visibility private
262
+ def self.included(base)
263
+ base.singleton_class.send(:alias_method, :original_new, :new)
264
+ base.extend(ClassMethods)
265
+ super(base)
266
+ end
267
+
268
+ # @!visibility private
269
+ module ClassMethods
270
+ def new(*args, &block)
271
+ obj = original_new(*args, &block)
272
+ obj.send(:init_synchronization)
273
+ obj
274
+ end
275
+ ruby2_keywords :new if respond_to?(:ruby2_keywords, true)
276
+ end
277
+ private_constant :ClassMethods
278
+
279
+ # Delegates asynchronous, thread-safe method calls to the wrapped object.
280
+ #
281
+ # @!visibility private
282
+ class AsyncDelegator < Synchronization::LockableObject
283
+ safe_initialization!
284
+
285
+ # Create a new delegator object wrapping the given delegate.
286
+ #
287
+ # @param [Object] delegate the object to wrap and delegate method calls to
288
+ def initialize(delegate)
289
+ super()
290
+ @delegate = delegate
291
+ @queue = []
292
+ @executor = Concurrent.global_io_executor
293
+ @ruby_pid = $$
294
+ end
295
+
296
+ # Delegates method calls to the wrapped object.
297
+ #
298
+ # @param [Symbol] method the method being called
299
+ # @param [Array] args zero or more arguments to the method
300
+ #
301
+ # @return [IVar] the result of the method call
302
+ #
303
+ # @raise [NameError] the object does not respond to `method` method
304
+ # @raise [ArgumentError] the given `args` do not match the arity of `method`
305
+ def method_missing(method, *args, &block)
306
+ super unless @delegate.respond_to?(method)
307
+ Async::validate_argc(@delegate, method, *args)
308
+
309
+ ivar = Concurrent::IVar.new
310
+ synchronize do
311
+ reset_if_forked
312
+ @queue.push [ivar, method, args, block]
313
+ @executor.post { perform } if @queue.length == 1
314
+ end
315
+
316
+ ivar
317
+ end
318
+
319
+ # Check whether the method is responsive
320
+ #
321
+ # @param [Symbol] method the method being called
322
+ def respond_to_missing?(method, include_private = false)
323
+ @delegate.respond_to?(method) || super
324
+ end
325
+
326
+ # Perform all enqueued tasks.
327
+ #
328
+ # This method must be called from within the executor. It must not be
329
+ # called while already running. It will loop until the queue is empty.
330
+ def perform
331
+ loop do
332
+ ivar, method, args, block = synchronize { @queue.first }
333
+ break unless ivar # queue is empty
334
+
335
+ begin
336
+ ivar.set(@delegate.send(method, *args, &block))
337
+ rescue => error
338
+ ivar.fail(error)
339
+ end
340
+
341
+ synchronize do
342
+ @queue.shift
343
+ return if @queue.empty?
344
+ end
345
+ end
346
+ end
347
+
348
+ def reset_if_forked
349
+ if $$ != @ruby_pid
350
+ @queue.clear
351
+ @ruby_pid = $$
352
+ end
353
+ end
354
+ end
355
+ private_constant :AsyncDelegator
356
+
357
+ # Delegates synchronous, thread-safe method calls to the wrapped object.
358
+ #
359
+ # @!visibility private
360
+ class AwaitDelegator
361
+
362
+ # Create a new delegator object wrapping the given delegate.
363
+ #
364
+ # @param [AsyncDelegator] delegate the object to wrap and delegate method calls to
365
+ def initialize(delegate)
366
+ @delegate = delegate
367
+ end
368
+
369
+ # Delegates method calls to the wrapped object.
370
+ #
371
+ # @param [Symbol] method the method being called
372
+ # @param [Array] args zero or more arguments to the method
373
+ #
374
+ # @return [IVar] the result of the method call
375
+ #
376
+ # @raise [NameError] the object does not respond to `method` method
377
+ # @raise [ArgumentError] the given `args` do not match the arity of `method`
378
+ def method_missing(method, *args, &block)
379
+ ivar = @delegate.send(method, *args, &block)
380
+ ivar.wait
381
+ ivar
382
+ end
383
+
384
+ # Check whether the method is responsive
385
+ #
386
+ # @param [Symbol] method the method being called
387
+ def respond_to_missing?(method, include_private = false)
388
+ @delegate.respond_to?(method) || super
389
+ end
390
+ end
391
+ private_constant :AwaitDelegator
392
+
393
+ # Causes the chained method call to be performed asynchronously on the
394
+ # object's thread. The delegated method will return a future in the
395
+ # `:pending` state and the method call will have been scheduled on the
396
+ # object's thread. The final disposition of the method call can be obtained
397
+ # by inspecting the returned future.
398
+ #
399
+ # @!macro async_thread_safety_warning
400
+ # @note The method call is guaranteed to be thread safe with respect to
401
+ # all other method calls against the same object that are called with
402
+ # either `async` or `await`. The mutable nature of Ruby references
403
+ # (and object orientation in general) prevent any other thread safety
404
+ # guarantees. Do NOT mix direct method calls with delegated method calls.
405
+ # Use *only* delegated method calls when sharing the object between threads.
406
+ #
407
+ # @return [Concurrent::IVar] the pending result of the asynchronous operation
408
+ #
409
+ # @raise [NameError] the object does not respond to the requested method
410
+ # @raise [ArgumentError] the given `args` do not match the arity of
411
+ # the requested method
412
+ def async
413
+ @__async_delegator__
414
+ end
415
+ alias_method :cast, :async
416
+
417
+ # Causes the chained method call to be performed synchronously on the
418
+ # current thread. The delegated will return a future in either the
419
+ # `:fulfilled` or `:rejected` state and the delegated method will have
420
+ # completed. The final disposition of the delegated method can be obtained
421
+ # by inspecting the returned future.
422
+ #
423
+ # @!macro async_thread_safety_warning
424
+ #
425
+ # @return [Concurrent::IVar] the completed result of the synchronous operation
426
+ #
427
+ # @raise [NameError] the object does not respond to the requested method
428
+ # @raise [ArgumentError] the given `args` do not match the arity of the
429
+ # requested method
430
+ def await
431
+ @__await_delegator__
432
+ end
433
+ alias_method :call, :await
434
+
435
+ # Initialize the internal serializer and other stnchronization mechanisms.
436
+ #
437
+ # @note This method *must* be called immediately upon object construction.
438
+ # This is the only way thread-safe initialization can be guaranteed.
439
+ #
440
+ # @!visibility private
441
+ def init_synchronization
442
+ return self if defined?(@__async_initialized__) && @__async_initialized__
443
+ @__async_initialized__ = true
444
+ @__async_delegator__ = AsyncDelegator.new(self)
445
+ @__await_delegator__ = AwaitDelegator.new(@__async_delegator__)
446
+ self
447
+ end
448
+ end
449
+ end
@@ -0,0 +1,222 @@
1
+ require 'concurrent/atomic/atomic_reference'
2
+ require 'concurrent/collection/copy_on_notify_observer_set'
3
+ require 'concurrent/concern/observable'
4
+ require 'concurrent/synchronization'
5
+
6
+ # @!macro thread_safe_variable_comparison
7
+ #
8
+ # ## Thread-safe Variable Classes
9
+ #
10
+ # Each of the thread-safe variable classes is designed to solve a different
11
+ # problem. In general:
12
+ #
13
+ # * *{Concurrent::Agent}:* Shared, mutable variable providing independent,
14
+ # uncoordinated, *asynchronous* change of individual values. Best used when
15
+ # the value will undergo frequent, complex updates. Suitable when the result
16
+ # of an update does not need to be known immediately.
17
+ # * *{Concurrent::Atom}:* Shared, mutable variable providing independent,
18
+ # uncoordinated, *synchronous* change of individual values. Best used when
19
+ # the value will undergo frequent reads but only occasional, though complex,
20
+ # updates. Suitable when the result of an update must be known immediately.
21
+ # * *{Concurrent::AtomicReference}:* A simple object reference that can be updated
22
+ # atomically. Updates are synchronous but fast. Best used when updates a
23
+ # simple set operations. Not suitable when updates are complex.
24
+ # {Concurrent::AtomicBoolean} and {Concurrent::AtomicFixnum} are similar
25
+ # but optimized for the given data type.
26
+ # * *{Concurrent::Exchanger}:* Shared, stateless synchronization point. Used
27
+ # when two or more threads need to exchange data. The threads will pair then
28
+ # block on each other until the exchange is complete.
29
+ # * *{Concurrent::MVar}:* Shared synchronization point. Used when one thread
30
+ # must give a value to another, which must take the value. The threads will
31
+ # block on each other until the exchange is complete.
32
+ # * *{Concurrent::ThreadLocalVar}:* Shared, mutable, isolated variable which
33
+ # holds a different value for each thread which has access. Often used as
34
+ # an instance variable in objects which must maintain different state
35
+ # for different threads.
36
+ # * *{Concurrent::TVar}:* Shared, mutable variables which provide
37
+ # *coordinated*, *synchronous*, change of *many* stated. Used when multiple
38
+ # value must change together, in an all-or-nothing transaction.
39
+
40
+
41
+ module Concurrent
42
+
43
+ # Atoms provide a way to manage shared, synchronous, independent state.
44
+ #
45
+ # An atom is initialized with an initial value and an optional validation
46
+ # proc. At any time the value of the atom can be synchronously and safely
47
+ # changed. If a validator is given at construction then any new value
48
+ # will be checked against the validator and will be rejected if the
49
+ # validator returns false or raises an exception.
50
+ #
51
+ # There are two ways to change the value of an atom: {#compare_and_set} and
52
+ # {#swap}. The former will set the new value if and only if it validates and
53
+ # the current value matches the new value. The latter will atomically set the
54
+ # new value to the result of running the given block if and only if that
55
+ # value validates.
56
+ #
57
+ # ## Example
58
+ #
59
+ # ```
60
+ # def next_fibonacci(set = nil)
61
+ # return [0, 1] if set.nil?
62
+ # set + [set[-2..-1].reduce{|sum,x| sum + x }]
63
+ # end
64
+ #
65
+ # # create an atom with an initial value
66
+ # atom = Concurrent::Atom.new(next_fibonacci)
67
+ #
68
+ # # send a few update requests
69
+ # 5.times do
70
+ # atom.swap{|set| next_fibonacci(set) }
71
+ # end
72
+ #
73
+ # # get the current value
74
+ # atom.value #=> [0, 1, 1, 2, 3, 5, 8]
75
+ # ```
76
+ #
77
+ # ## Observation
78
+ #
79
+ # Atoms support observers through the {Concurrent::Observable} mixin module.
80
+ # Notification of observers occurs every time the value of the Atom changes.
81
+ # When notified the observer will receive three arguments: `time`, `old_value`,
82
+ # and `new_value`. The `time` argument is the time at which the value change
83
+ # occurred. The `old_value` is the value of the Atom when the change began
84
+ # The `new_value` is the value to which the Atom was set when the change
85
+ # completed. Note that `old_value` and `new_value` may be the same. This is
86
+ # not an error. It simply means that the change operation returned the same
87
+ # value.
88
+ #
89
+ # Unlike in Clojure, `Atom` cannot participate in {Concurrent::TVar} transactions.
90
+ #
91
+ # @!macro thread_safe_variable_comparison
92
+ #
93
+ # @see http://clojure.org/atoms Clojure Atoms
94
+ # @see http://clojure.org/state Values and Change - Clojure's approach to Identity and State
95
+ class Atom < Synchronization::Object
96
+ include Concern::Observable
97
+
98
+ safe_initialization!
99
+ attr_atomic(:value)
100
+ private :value=, :swap_value, :compare_and_set_value, :update_value
101
+ public :value
102
+ alias_method :deref, :value
103
+
104
+ # @!method value
105
+ # The current value of the atom.
106
+ #
107
+ # @return [Object] The current value.
108
+
109
+ # Create a new atom with the given initial value.
110
+ #
111
+ # @param [Object] value The initial value
112
+ # @param [Hash] opts The options used to configure the atom
113
+ # @option opts [Proc] :validator (nil) Optional proc used to validate new
114
+ # values. It must accept one and only one argument which will be the
115
+ # intended new value. The validator will return true if the new value
116
+ # is acceptable else return false (preferrably) or raise an exception.
117
+ #
118
+ # @!macro deref_options
119
+ #
120
+ # @raise [ArgumentError] if the validator is not a `Proc` (when given)
121
+ def initialize(value, opts = {})
122
+ super()
123
+ @Validator = opts.fetch(:validator, -> v { true })
124
+ self.observers = Collection::CopyOnNotifyObserverSet.new
125
+ self.value = value
126
+ end
127
+
128
+ # Atomically swaps the value of atom using the given block. The current
129
+ # value will be passed to the block, as will any arguments passed as
130
+ # arguments to the function. The new value will be validated against the
131
+ # (optional) validator proc given at construction. If validation fails the
132
+ # value will not be changed.
133
+ #
134
+ # Internally, {#swap} reads the current value, applies the block to it, and
135
+ # attempts to compare-and-set it in. Since another thread may have changed
136
+ # the value in the intervening time, it may have to retry, and does so in a
137
+ # spin loop. The net effect is that the value will always be the result of
138
+ # the application of the supplied block to a current value, atomically.
139
+ # However, because the block might be called multiple times, it must be free
140
+ # of side effects.
141
+ #
142
+ # @note The given block may be called multiple times, and thus should be free
143
+ # of side effects.
144
+ #
145
+ # @param [Object] args Zero or more arguments passed to the block.
146
+ #
147
+ # @yield [value, args] Calculates a new value for the atom based on the
148
+ # current value and any supplied arguments.
149
+ # @yieldparam value [Object] The current value of the atom.
150
+ # @yieldparam args [Object] All arguments passed to the function, in order.
151
+ # @yieldreturn [Object] The intended new value of the atom.
152
+ #
153
+ # @return [Object] The final value of the atom after all operations and
154
+ # validations are complete.
155
+ #
156
+ # @raise [ArgumentError] When no block is given.
157
+ def swap(*args)
158
+ raise ArgumentError.new('no block given') unless block_given?
159
+
160
+ loop do
161
+ old_value = value
162
+ new_value = yield(old_value, *args)
163
+ begin
164
+ break old_value unless valid?(new_value)
165
+ break new_value if compare_and_set(old_value, new_value)
166
+ rescue
167
+ break old_value
168
+ end
169
+ end
170
+ end
171
+
172
+ # Atomically sets the value of atom to the new value if and only if the
173
+ # current value of the atom is identical to the old value and the new
174
+ # value successfully validates against the (optional) validator given
175
+ # at construction.
176
+ #
177
+ # @param [Object] old_value The expected current value.
178
+ # @param [Object] new_value The intended new value.
179
+ #
180
+ # @return [Boolean] True if the value is changed else false.
181
+ def compare_and_set(old_value, new_value)
182
+ if valid?(new_value) && compare_and_set_value(old_value, new_value)
183
+ observers.notify_observers(Time.now, old_value, new_value)
184
+ true
185
+ else
186
+ false
187
+ end
188
+ end
189
+
190
+ # Atomically sets the value of atom to the new value without regard for the
191
+ # current value so long as the new value successfully validates against the
192
+ # (optional) validator given at construction.
193
+ #
194
+ # @param [Object] new_value The intended new value.
195
+ #
196
+ # @return [Object] The final value of the atom after all operations and
197
+ # validations are complete.
198
+ def reset(new_value)
199
+ old_value = value
200
+ if valid?(new_value)
201
+ self.value = new_value
202
+ observers.notify_observers(Time.now, old_value, new_value)
203
+ new_value
204
+ else
205
+ old_value
206
+ end
207
+ end
208
+
209
+ private
210
+
211
+ # Is the new value valid?
212
+ #
213
+ # @param [Object] new_value The intended new value.
214
+ # @return [Boolean] false if the validator function returns false or raises
215
+ # an exception else true
216
+ def valid?(new_value)
217
+ @Validator.call(new_value)
218
+ rescue
219
+ false
220
+ end
221
+ end
222
+ end