concurrent-ruby 0.8.0.pre2-java → 0.9.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +114 -3
  3. data/README.md +111 -55
  4. data/lib/concurrent.rb +90 -14
  5. data/lib/concurrent/async.rb +143 -51
  6. data/lib/concurrent/atom.rb +131 -0
  7. data/lib/concurrent/atomic/atomic_boolean.rb +57 -107
  8. data/lib/concurrent/atomic/atomic_fixnum.rb +73 -101
  9. data/lib/concurrent/atomic/atomic_reference.rb +49 -0
  10. data/lib/concurrent/atomic/condition.rb +23 -12
  11. data/lib/concurrent/atomic/count_down_latch.rb +23 -21
  12. data/lib/concurrent/atomic/cyclic_barrier.rb +47 -47
  13. data/lib/concurrent/atomic/event.rb +33 -42
  14. data/lib/concurrent/atomic/read_write_lock.rb +252 -0
  15. data/lib/concurrent/atomic/semaphore.rb +64 -89
  16. data/lib/concurrent/atomic/thread_local_var.rb +130 -58
  17. data/lib/concurrent/atomic/thread_local_var/weak_key_map.rb +236 -0
  18. data/lib/concurrent/atomic_reference/direct_update.rb +34 -3
  19. data/lib/concurrent/atomic_reference/jruby.rb +6 -3
  20. data/lib/concurrent/atomic_reference/mutex_atomic.rb +17 -39
  21. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +3 -0
  22. data/lib/concurrent/atomic_reference/rbx.rb +4 -1
  23. data/lib/concurrent/atomic_reference/ruby.rb +6 -3
  24. data/lib/concurrent/atomics.rb +74 -4
  25. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +115 -0
  26. data/lib/concurrent/collection/copy_on_write_observer_set.rb +119 -0
  27. data/lib/concurrent/collection/priority_queue.rb +300 -245
  28. data/lib/concurrent/concern/deprecation.rb +34 -0
  29. data/lib/concurrent/concern/dereferenceable.rb +88 -0
  30. data/lib/concurrent/concern/logging.rb +27 -0
  31. data/lib/concurrent/concern/obligation.rb +228 -0
  32. data/lib/concurrent/concern/observable.rb +85 -0
  33. data/lib/concurrent/configuration.rb +234 -109
  34. data/lib/concurrent/dataflow.rb +2 -3
  35. data/lib/concurrent/delay.rb +141 -50
  36. data/lib/concurrent/edge.rb +30 -0
  37. data/lib/concurrent/errors.rb +19 -7
  38. data/lib/concurrent/exchanger.rb +25 -1
  39. data/lib/concurrent/executor/cached_thread_pool.rb +51 -33
  40. data/lib/concurrent/executor/executor.rb +46 -299
  41. data/lib/concurrent/executor/executor_service.rb +521 -0
  42. data/lib/concurrent/executor/fixed_thread_pool.rb +196 -23
  43. data/lib/concurrent/executor/immediate_executor.rb +9 -9
  44. data/lib/concurrent/executor/indirect_immediate_executor.rb +4 -3
  45. data/lib/concurrent/executor/java_single_thread_executor.rb +17 -16
  46. data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
  47. data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
  48. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
  49. data/lib/concurrent/executor/safe_task_executor.rb +5 -4
  50. data/lib/concurrent/executor/serialized_execution.rb +22 -18
  51. data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
  52. data/lib/concurrent/executor/single_thread_executor.rb +32 -21
  53. data/lib/concurrent/executor/thread_pool_executor.rb +73 -60
  54. data/lib/concurrent/executor/timer_set.rb +96 -84
  55. data/lib/concurrent/executors.rb +1 -1
  56. data/lib/concurrent/future.rb +71 -38
  57. data/lib/concurrent/immutable_struct.rb +89 -0
  58. data/lib/concurrent/ivar.rb +152 -60
  59. data/lib/concurrent/lazy_register.rb +40 -20
  60. data/lib/concurrent/maybe.rb +226 -0
  61. data/lib/concurrent/mutable_struct.rb +227 -0
  62. data/lib/concurrent/mvar.rb +44 -43
  63. data/lib/concurrent/promise.rb +229 -136
  64. data/lib/concurrent/scheduled_task.rb +341 -43
  65. data/lib/concurrent/settable_struct.rb +127 -0
  66. data/lib/concurrent/synchronization.rb +17 -0
  67. data/lib/concurrent/synchronization/abstract_object.rb +163 -0
  68. data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
  69. data/lib/concurrent/synchronization/condition.rb +53 -0
  70. data/lib/concurrent/synchronization/java_object.rb +34 -0
  71. data/lib/concurrent/synchronization/lock.rb +32 -0
  72. data/lib/concurrent/synchronization/monitor_object.rb +26 -0
  73. data/lib/concurrent/synchronization/mutex_object.rb +43 -0
  74. data/lib/concurrent/synchronization/object.rb +78 -0
  75. data/lib/concurrent/synchronization/rbx_object.rb +75 -0
  76. data/lib/concurrent/timer_task.rb +92 -103
  77. data/lib/concurrent/tvar.rb +42 -38
  78. data/lib/concurrent/utilities.rb +3 -1
  79. data/lib/concurrent/utility/at_exit.rb +97 -0
  80. data/lib/concurrent/utility/engine.rb +44 -0
  81. data/lib/concurrent/utility/monotonic_time.rb +59 -0
  82. data/lib/concurrent/utility/native_extension_loader.rb +56 -0
  83. data/lib/concurrent/utility/processor_counter.rb +156 -0
  84. data/lib/concurrent/utility/timeout.rb +18 -14
  85. data/lib/concurrent/utility/timer.rb +11 -6
  86. data/lib/concurrent/version.rb +2 -1
  87. data/lib/concurrent_ruby.rb +1 -0
  88. data/lib/concurrent_ruby_ext.jar +0 -0
  89. metadata +46 -66
  90. data/lib/concurrent/actor.rb +0 -103
  91. data/lib/concurrent/actor/behaviour.rb +0 -70
  92. data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
  93. data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
  94. data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
  95. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
  96. data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
  97. data/lib/concurrent/actor/behaviour/linking.rb +0 -45
  98. data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
  99. data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
  100. data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
  101. data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
  102. data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
  103. data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
  104. data/lib/concurrent/actor/behaviour/termination.rb +0 -54
  105. data/lib/concurrent/actor/context.rb +0 -154
  106. data/lib/concurrent/actor/core.rb +0 -217
  107. data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
  108. data/lib/concurrent/actor/envelope.rb +0 -41
  109. data/lib/concurrent/actor/errors.rb +0 -27
  110. data/lib/concurrent/actor/internal_delegations.rb +0 -49
  111. data/lib/concurrent/actor/public_delegations.rb +0 -40
  112. data/lib/concurrent/actor/reference.rb +0 -81
  113. data/lib/concurrent/actor/root.rb +0 -37
  114. data/lib/concurrent/actor/type_check.rb +0 -48
  115. data/lib/concurrent/actor/utils.rb +0 -10
  116. data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
  117. data/lib/concurrent/actor/utils/balancer.rb +0 -42
  118. data/lib/concurrent/actor/utils/broadcast.rb +0 -52
  119. data/lib/concurrent/actor/utils/pool.rb +0 -59
  120. data/lib/concurrent/actress.rb +0 -3
  121. data/lib/concurrent/agent.rb +0 -209
  122. data/lib/concurrent/atomic.rb +0 -92
  123. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
  124. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
  125. data/lib/concurrent/atomic/synchronization.rb +0 -51
  126. data/lib/concurrent/channel/buffered_channel.rb +0 -85
  127. data/lib/concurrent/channel/channel.rb +0 -41
  128. data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
  129. data/lib/concurrent/channel/waitable_list.rb +0 -40
  130. data/lib/concurrent/channels.rb +0 -5
  131. data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
  132. data/lib/concurrent/collection/ring_buffer.rb +0 -59
  133. data/lib/concurrent/collections.rb +0 -3
  134. data/lib/concurrent/dereferenceable.rb +0 -108
  135. data/lib/concurrent/executor/java_cached_thread_pool.rb +0 -32
  136. data/lib/concurrent/executor/java_fixed_thread_pool.rb +0 -31
  137. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +0 -29
  138. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +0 -32
  139. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
  140. data/lib/concurrent/logging.rb +0 -20
  141. data/lib/concurrent/obligation.rb +0 -171
  142. data/lib/concurrent/observable.rb +0 -73
  143. data/lib/concurrent/options_parser.rb +0 -48
  144. data/lib/concurrent/utility/processor_count.rb +0 -152
  145. data/lib/extension_helper.rb +0 -37
@@ -5,16 +5,81 @@ require 'concurrent/errors'
5
5
  require 'concurrent/ivar'
6
6
  require 'concurrent/executor/immediate_executor'
7
7
  require 'concurrent/executor/serialized_execution'
8
+ require 'concurrent/concern/deprecation'
8
9
 
9
10
  module Concurrent
10
11
 
11
- # {include:file:doc/async.md}
12
+ # A mixin module that provides simple asynchronous behavior to any standard
13
+ # class/object or object.
14
+ #
15
+ # ```cucumber
16
+ # Feature:
17
+ # As a stateful, plain old Ruby class/object
18
+ # I want safe, asynchronous behavior
19
+ # So my long-running methods don't block the main thread
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
36
+ # `: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
+ #
42
+ # > Thread safe guarantees can only be made when asynchronous method calls
43
+ # > are not mixed with synchronous method calls. Use only synchronous calls
44
+ # > when the object is used exclusively on a single thread. Use only
45
+ # > `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
47
+ # > 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
+ #
52
+ # @example
53
+ #
54
+ # class Echo
55
+ # include Concurrent::Async
56
+ #
57
+ # def echo(msg)
58
+ # sleep(rand)
59
+ # print "#{msg}\n"
60
+ # nil
61
+ # end
62
+ # end
63
+ #
64
+ # horn = Echo.new
65
+ # horn.echo('zero') # synchronous, not thread-safe
66
+ #
67
+ # horn.async.echo('one') # asynchronous, non-blocking, thread-safe
68
+ # horn.await.echo('two') # synchronous, blocking, thread-safe
12
69
  #
13
- # @since 0.6.0
14
- #
15
- # @see Concurrent::Obligation
70
+ # @see Concurrent::IVar
16
71
  module Async
17
72
 
73
+ # @!method self.new(*args, &block)
74
+ #
75
+ # Instanciate a new object and ensure proper initialization of the
76
+ # synchronization mechanisms.
77
+ #
78
+ # @param [Array<Object>] args Zero or more arguments to be passed to the
79
+ # object's initializer.
80
+ # @param [Proc] bloc Optional block to pass to the object's initializer.
81
+ # @return [Object] A properly initialized object of the asynchronous class.
82
+
18
83
  # Check for the presence of a method on an object and determine if a given
19
84
  # set of arguments matches the required arity.
20
85
  #
@@ -34,7 +99,9 @@ module Concurrent
34
99
  # @see http://www.ruby-doc.org/core-2.1.1/Method.html#method-i-arity Method#arity
35
100
  # @see http://ruby-doc.org/core-2.1.0/Object.html#method-i-respond_to-3F Object#respond_to?
36
101
  # @see http://www.ruby-doc.org/core-2.1.0/BasicObject.html#method-i-method_missing BasicObject#method_missing
37
- def validate_argc(obj, method, *args)
102
+ #
103
+ # @!visibility private
104
+ def self.validate_argc(obj, method, *args)
38
105
  argc = args.length
39
106
  arity = obj.method(method).arity
40
107
 
@@ -44,12 +111,28 @@ module Concurrent
44
111
  raise ArgumentError.new("wrong number of arguments (#{argc} for #{arity}..*)")
45
112
  end
46
113
  end
47
- module_function :validate_argc
114
+
115
+ # @!visibility private
116
+ def self.included(base)
117
+ base.singleton_class.send(:alias_method, :original_new, :new)
118
+ base.extend(ClassMethods)
119
+ super(base)
120
+ end
121
+
122
+ # @!visibility private
123
+ module ClassMethods
124
+ def new(*args, &block)
125
+ obj = original_new(*args, &block)
126
+ obj.send(:init_synchronization)
127
+ obj
128
+ end
129
+ end
130
+ private_constant :ClassMethods
48
131
 
49
132
  # Delegates asynchronous, thread-safe method calls to the wrapped object.
50
133
  #
51
134
  # @!visibility private
52
- class AsyncDelegator # :nodoc:
135
+ class AsyncDelegator
53
136
 
54
137
  # Create a new delegator object wrapping the given delegate,
55
138
  # protecting it with the given serializer, and executing it on the
@@ -84,14 +167,11 @@ module Concurrent
84
167
  self.define_singleton_method(method) do |*args2|
85
168
  Async::validate_argc(@delegate, method, *args2)
86
169
  ivar = Concurrent::IVar.new
87
- value, reason = nil, nil
88
170
  @serializer.post(@executor.value) do
89
171
  begin
90
- value = @delegate.send(method, *args2, &block)
172
+ ivar.set(@delegate.send(method, *args2, &block))
91
173
  rescue => reason
92
- # caught
93
- ensure
94
- ivar.complete(reason.nil?, value, reason)
174
+ ivar.fail(reason)
95
175
  end
96
176
  end
97
177
  ivar.value if @blocking
@@ -101,6 +181,7 @@ module Concurrent
101
181
  self.send(method, *args)
102
182
  end
103
183
  end
184
+ private_constant :AsyncDelegator
104
185
 
105
186
  # Causes the chained method call to be performed asynchronously on the
106
187
  # global thread pool. The method called by this method will return a
@@ -115,26 +196,24 @@ module Concurrent
115
196
  # library, some edge cases will be missed. For more information see
116
197
  # the documentation for the `validate_argc` method.
117
198
  #
118
- # @note The method call is guaranteed to be thread safe with respect to
119
- # all other method calls against the same object that are called with
120
- # either `async` or `await`. The mutable nature of Ruby references
121
- # (and object orientation in general) prevent any other thread safety
122
- # guarantees. Do NOT mix non-protected method calls with protected
123
- # method call. Use *only* protected method calls when sharing the object
124
- # between threads.
199
+ # @!macro [attach] async_thread_safety_warning
200
+ # @note The method call is guaranteed to be thread safe with respect to
201
+ # all other method calls against the same object that are called with
202
+ # either `async` or `await`. The mutable nature of Ruby references
203
+ # (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.
125
207
  #
126
208
  # @return [Concurrent::IVar] the pending result of the asynchronous operation
127
209
  #
128
- # @raise [Concurrent::InitializationError] `#init_mutex` has not been called
129
210
  # @raise [NameError] the object does not respond to `method` method
130
211
  # @raise [ArgumentError] the given `args` do not match the arity of `method`
131
212
  #
132
213
  # @see Concurrent::IVar
133
214
  def async
134
- raise InitializationError.new('#init_mutex was never called') unless @__async_initialized__
135
215
  @__async_delegator__.value
136
216
  end
137
- alias_method :future, :async
138
217
 
139
218
  # Causes the chained method call to be performed synchronously on the
140
219
  # current thread. The method called by this method will return an
@@ -149,58 +228,71 @@ module Concurrent
149
228
  # library, some edge cases will be missed. For more information see
150
229
  # the documentation for the `validate_argc` method.
151
230
  #
152
- # @note The method call is guaranteed to be thread safe with respect to
153
- # all other method calls against the same object that are called with
154
- # either `async` or `await`. The mutable nature of Ruby references
155
- # (and object orientation in general) prevent any other thread safety
156
- # guarantees. Do NOT mix non-protected method calls with protected
157
- # method call. Use *only* protected method calls when sharing the object
158
- # between threads.
231
+ # @!macro async_thread_safety_warning
159
232
  #
160
233
  # @return [Concurrent::IVar] the completed result of the synchronous operation
161
234
  #
162
- # @raise [Concurrent::InitializationError] `#init_mutex` has not been called
163
235
  # @raise [NameError] the object does not respond to `method` method
164
236
  # @raise [ArgumentError] the given `args` do not match the arity of `method`
165
237
  #
166
238
  # @see Concurrent::IVar
167
239
  def await
168
- raise InitializationError.new('#init_mutex was never called') unless @__async_initialized__
169
240
  @__await_delegator__.value
170
241
  end
171
- alias_method :delay, :await
172
242
 
173
- # Set a new executor
243
+ # Set a new executor.
174
244
  #
175
- # @raise [Concurrent::InitializationError] `#init_mutex` has not been called
176
- # @raise [ArgumentError] executor has already been set
245
+ # @raise [ArgumentError] executor has already been set.
177
246
  def executor=(executor)
178
- raise InitializationError.new('#init_mutex was never called') unless @__async_initialized__
179
247
  @__async_executor__.reconfigure { executor } or
180
248
  raise ArgumentError.new('executor has already been set')
181
249
  end
182
250
 
183
- # Initialize the internal serializer and other synchronization objects. This method
184
- # *must* be called from the constructor of the including class or explicitly
185
- # by the caller prior to calling any other methods. If `init_mutex` is *not*
186
- # called explicitly the async/await/executor methods will raize a
187
- # `Concurrent::InitializationError`. This is the only way thread-safe
188
- # initialization can be guaranteed.
251
+ # Initialize the internal serializer and other stnchronization mechanisms.
189
252
  #
190
- # @note This method *must* be called from the constructor of the including
191
- # class or explicitly by the caller prior to calling any other methods.
192
- # This is the only way thread-safe initialization can be guaranteed.
253
+ # @note This method *must* be called immediately upon object construction.
254
+ # This is the only way thread-safe initialization can be guaranteed.
193
255
  #
194
256
  # @raise [Concurrent::InitializationError] when called more than once
257
+ #
258
+ # @!visibility private
259
+ # @deprecated
195
260
  def init_mutex
196
- raise InitializationError.new('#init_mutex was already called') if @__async_initialized__
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
+ # @!visibility private
277
+ def init_synchronization
278
+ return self if @__async_initialized__
279
+
197
280
  @__async_initialized__ = true
198
281
  serializer = Concurrent::SerializedExecution.new
199
- @__async_executor__ = Delay.new{ Concurrent.configuration.global_operation_pool }
200
- @__await_delegator__ = Delay.new{ AsyncDelegator.new(
201
- self, Delay.new{ Concurrent::ImmediateExecutor.new }, serializer, true) }
202
- @__async_delegator__ = Delay.new{ AsyncDelegator.new(
203
- self, @__async_executor__, serializer, false) }
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
+
295
+ self
204
296
  end
205
297
  end
206
298
  end
@@ -0,0 +1,131 @@
1
+ require 'concurrent/concern/dereferenceable'
2
+ require 'concurrent/atomic/atomic_reference'
3
+ require 'concurrent/synchronization/object'
4
+
5
+ module Concurrent
6
+
7
+ # Atoms provide a way to manage shared, synchronous, independent state.
8
+ #
9
+ # An atom is initialized with an initial value and an optional validation
10
+ # proc. At any time the value of the atom can be synchronously and safely
11
+ # changed. If a validator is given at construction then any new value
12
+ # will be checked against the validator and will be rejected if the
13
+ # validator returns false or raises an exception.
14
+ #
15
+ # There are two ways to change the value of an atom: {#compare_and_set} and
16
+ # {#swap}. The former will set the new value if and only if it validates and
17
+ # the current value matches the new value. The latter will atomically set the
18
+ # new value to the result of running the given block if and only if that
19
+ # value validates.
20
+ #
21
+ # @!macro copy_options
22
+ #
23
+ # @see http://clojure.org/atoms Clojure Atoms
24
+ class Atom < Synchronization::Object
25
+ include Concern::Dereferenceable
26
+
27
+ # Create a new atom with the given initial value.
28
+ #
29
+ # @param [Object] value The initial value
30
+ # @param [Hash] opts The options used to configure the atom
31
+ # @option opts [Proc] :validator (nil) Optional proc used to validate new
32
+ # values. It must accept one and only one argument which will be the
33
+ # intended new value. The validator will return true if the new value
34
+ # is acceptable else return false (preferrably) or raise an exception.
35
+ #
36
+ # @!macro deref_options
37
+ #
38
+ # @raise [ArgumentError] if the validator is not a `Proc` (when given)
39
+ def initialize(value, opts = {})
40
+ super()
41
+
42
+ @validator = opts.fetch(:validator, ->(v){ true })
43
+ raise ArgumentError.new('validator must be a proc') unless @validator.is_a? Proc
44
+
45
+ @value = Concurrent::AtomicReference.new(value)
46
+ ns_set_deref_options(opts)
47
+ ensure_ivar_visibility!
48
+ end
49
+
50
+ # The current value of the atom.
51
+ #
52
+ # @return [Object] The current value.
53
+ def value
54
+ apply_deref_options(@value.value)
55
+ end
56
+ alias_method :deref, :value
57
+
58
+ # Atomically swaps the value of atom using the given block. The current
59
+ # value will be passed to the block, as will any arguments passed as
60
+ # arguments to the function. The new value will be validated against the
61
+ # (optional) validator proc given at construction. If validation fails the
62
+ # value will not be changed.
63
+ #
64
+ # Internally, {#swap} reads the current value, applies the block to it, and
65
+ # attempts to compare-and-set it in. Since another thread may have changed
66
+ # the value in the intervening time, it may have to retry, and does so in a
67
+ # spin loop. The net effect is that the value will always be the result of
68
+ # the application of the supplied block to a current value, atomically.
69
+ # However, because the block might be called multiple times, it must be free
70
+ # of side effects.
71
+ #
72
+ # @note The given block may be called multiple times, and thus should be free
73
+ # of side effects.
74
+ #
75
+ # @param [Object] args Zero or more arguments passed to the block.
76
+ #
77
+ # @yield [value, args] Calculates a new value for the atom based on the
78
+ # current value and any supplied agruments.
79
+ # @yieldparam value [Object] The current value of the atom.
80
+ # @yieldparam args [Object] All arguments passed to the function, in order.
81
+ # @yieldreturn [Object] The intended new value of the atom.
82
+ #
83
+ # @return [Object] The final value of the atom after all operations and
84
+ # validations are complete.
85
+ #
86
+ # @raise [ArgumentError] When no block is given.
87
+ def swap(*args)
88
+ raise ArgumentError.new('no block given') unless block_given?
89
+
90
+ begin
91
+ loop do
92
+ old_value = @value.value
93
+ new_value = yield(old_value, *args)
94
+ return old_value unless @validator.call(new_value)
95
+ return new_value if compare_and_set!(old_value, new_value)
96
+ end
97
+ rescue
98
+ return @value.value
99
+ end
100
+ end
101
+
102
+ # @!macro [attach] atom_compare_and_set
103
+ # Atomically sets the value of atom to the new value if and only if the
104
+ # current value of the atom is identical to the old value and the new
105
+ # value successfully validates against the (optional) validator given
106
+ # at construction.
107
+ #
108
+ # @param [Object] old_value The expected current value.
109
+ # @param [Object] new_value The intended new value.
110
+ #
111
+ # @return [Boolean] True if the value is changed else false.
112
+ def compare_and_set(old_value, new_value)
113
+ compare_and_set!(old_value, new_value)
114
+ rescue
115
+ false
116
+ end
117
+
118
+ private
119
+
120
+ # @!macro atom_compare_and_set
121
+ # @raise [Exception] if the validator proc raises an exception
122
+ # @!visibility private
123
+ def compare_and_set!(old_value, new_value)
124
+ if @validator.call(new_value) # may raise exception
125
+ @value.compare_and_set(old_value, new_value)
126
+ else
127
+ false
128
+ end
129
+ end
130
+ end
131
+ end
@@ -1,4 +1,5 @@
1
- require_relative '../../extension_helper'
1
+ require 'concurrent/utility/native_extension_loader'
2
+ require 'concurrent/synchronization'
2
3
 
3
4
  module Concurrent
4
5
 
@@ -21,7 +22,11 @@ module Concurrent
21
22
  # 3.340000 0.010000 3.350000 ( 0.855000)
22
23
  #
23
24
  # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicBoolean.html java.util.concurrent.atomic.AtomicBoolean
24
- class MutexAtomicBoolean
25
+ #
26
+ # @!visibility private
27
+ #
28
+ # @!macro internal_implementation_note
29
+ class MutexAtomicBoolean < Synchronization::Object
25
30
 
26
31
  # @!macro [attach] atomic_boolean_method_initialize
27
32
  #
@@ -29,8 +34,8 @@ module Concurrent
29
34
  #
30
35
  # @param [Boolean] initial the initial value
31
36
  def initialize(initial = false)
32
- @value = !!initial
33
- @mutex = Mutex.new
37
+ super()
38
+ synchronize { ns_initialize(initial) }
34
39
  end
35
40
 
36
41
  # @!macro [attach] atomic_boolean_method_value_get
@@ -39,10 +44,7 @@ module Concurrent
39
44
  #
40
45
  # @return [Boolean] the current value
41
46
  def value
42
- @mutex.lock
43
- @value
44
- ensure
45
- @mutex.unlock
47
+ synchronize { @value }
46
48
  end
47
49
 
48
50
  # @!macro [attach] atomic_boolean_method_value_set
@@ -53,11 +55,7 @@ module Concurrent
53
55
  #
54
56
  # @return [Boolean] the current value
55
57
  def value=(value)
56
- @mutex.lock
57
- @value = !!value
58
- @value
59
- ensure
60
- @mutex.unlock
58
+ synchronize { @value = !!value }
61
59
  end
62
60
 
63
61
  # @!macro [attach] atomic_boolean_method_true_question
@@ -66,22 +64,16 @@ module Concurrent
66
64
  #
67
65
  # @return [Boolean] true if the current value is `true`, else false
68
66
  def true?
69
- @mutex.lock
70
- @value
71
- ensure
72
- @mutex.unlock
67
+ synchronize { @value }
73
68
  end
74
69
 
75
- # @!macro atomic_boolean_method_false_question
70
+ # @!macro [attach] atomic_boolean_method_false_question
76
71
  #
77
72
  # Is the current value `false`
78
73
  #
79
74
  # @return [Boolean] true if the current value is `false`, else false
80
75
  def false?
81
- @mutex.lock
82
- !@value
83
- ensure
84
- @mutex.unlock
76
+ synchronize { !@value }
85
77
  end
86
78
 
87
79
  # @!macro [attach] atomic_boolean_method_make_true
@@ -90,12 +82,7 @@ module Concurrent
90
82
  #
91
83
  # @return [Boolean] true is value has changed, otherwise false
92
84
  def make_true
93
- @mutex.lock
94
- old = @value
95
- @value = true
96
- !old
97
- ensure
98
- @mutex.unlock
85
+ synchronize { ns_make_value(true) }
99
86
  end
100
87
 
101
88
  # @!macro [attach] atomic_boolean_method_make_false
@@ -104,98 +91,61 @@ module Concurrent
104
91
  #
105
92
  # @return [Boolean] true is value has changed, otherwise false
106
93
  def make_false
107
- @mutex.lock
108
- old = @value
109
- @value = false
110
- old
111
- ensure
112
- @mutex.unlock
94
+ synchronize { ns_make_value(false) }
113
95
  end
114
- end
115
96
 
116
- if RUBY_PLATFORM == 'java'
117
-
118
- # @!macro atomic_boolean
119
- class JavaAtomicBoolean
120
-
121
- # @!macro atomic_boolean_method_initialize
122
- #
123
- def initialize(initial = false)
124
- @atomic = java.util.concurrent.atomic.AtomicBoolean.new(!!initial)
125
- end
126
-
127
- # @!macro atomic_boolean_method_value_get
128
- #
129
- def value
130
- @atomic.get
131
- end
132
-
133
- # @!macro atomic_boolean_method_value_set
134
- #
135
- def value=(value)
136
- @atomic.set(!!value)
137
- end
138
-
139
- # @!macro atomic_boolean_method_true_question
140
- def true?
141
- @atomic.get
142
- end
143
-
144
- # @!macro atomic_boolean_method_false_question
145
- def false?
146
- !@atomic.get
147
- end
148
-
149
- # @!macro atomic_boolean_method_make_true
150
- def make_true
151
- @atomic.compareAndSet(false, true)
152
- end
153
-
154
- # @!macro atomic_boolean_method_make_false
155
- def make_false
156
- @atomic.compareAndSet(true, false)
157
- end
158
- end
97
+ protected
159
98
 
160
- # @!macro atomic_boolean
161
- class AtomicBoolean < JavaAtomicBoolean
99
+ # @!visibility private
100
+ def ns_initialize(initial)
101
+ @value = !!initial
162
102
  end
163
103
 
164
- elsif defined?(CAtomicBoolean)
165
-
166
- # @!macro atomic_boolean
167
- class CAtomicBoolean
168
-
169
- # @!method initialize
170
- # @!macro atomic_boolean_method_initialize
104
+ # @!visibility private
105
+ def ns_make_value(value)
106
+ old = @value
107
+ @value = value
108
+ old != @value
109
+ end
110
+ end
171
111
 
172
- # @!method value
173
- # @!macro atomic_boolean_method_value_get
112
+ # @!visibility private
113
+ # @!macro internal_implementation_note
114
+ AtomicBooleanImplementation = case
115
+ when Concurrent.on_jruby?
116
+ JavaAtomicBoolean
117
+ when defined?(CAtomicBoolean)
118
+ CAtomicBoolean
119
+ else
120
+ MutexAtomicBoolean
121
+ end
122
+ private_constant :AtomicBooleanImplementation
123
+
124
+ # @!macro atomic_boolean
125
+ #
126
+ # @see Concurrent::MutexAtomicBoolean
127
+ class AtomicBoolean < AtomicBooleanImplementation
174
128
 
175
- # @!method value=
176
- # @!macro atomic_boolean_method_value_set
129
+ # @!method initialize(initial = false)
130
+ # @!macro atomic_boolean_method_initialize
177
131
 
178
- # @!method true?
179
- # @!macro atomic_boolean_method_true_question
132
+ # @!method value
133
+ # @!macro atomic_boolean_method_value_get
180
134
 
181
- # @!method false?
182
- # @!macro atomic_boolean_method_false_question
135
+ # @!method value=(value)
136
+ # @!macro atomic_boolean_method_value_set
183
137
 
184
- # @!method make_true
185
- # @!macro atomic_boolean_method_make_true
138
+ # @!method true?
139
+ # @!macro atomic_boolean_method_true_question
186
140
 
187
- # @!method make_false
188
- # @!macro atomic_boolean_method_make_false
189
- end
141
+ # @!method false?
142
+ # @!macro atomic_boolean_method_false_question
190
143
 
191
- # @!macro atomic_boolean
192
- class AtomicBoolean < CAtomicBoolean
193
- end
144
+ # @!method make_true
145
+ # @!macro atomic_boolean_method_make_true
194
146
 
195
- else
147
+ # @!method make_false
148
+ # @!macro atomic_boolean_method_make_false
196
149
 
197
- # @!macro atomic_boolean
198
- class AtomicBoolean < MutexAtomicBoolean
199
- end
200
150
  end
201
151
  end