concurrent-ruby 1.0.0.pre4-java → 1.0.0.pre5-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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -1
  3. data/lib/concurrent/agent.rb +5 -1
  4. data/lib/concurrent/async.rb +77 -38
  5. data/lib/concurrent/atom.rb +2 -1
  6. data/lib/concurrent/atomic/atomic_boolean.rb +1 -1
  7. data/lib/concurrent/atomic/atomic_fixnum.rb +1 -1
  8. data/lib/concurrent/atomic/atomic_reference.rb +1 -1
  9. data/lib/concurrent/atomic/semaphore.rb +1 -1
  10. data/lib/concurrent/atomic_reference/jruby+truffle.rb +1 -0
  11. data/lib/concurrent/atomic_reference/jruby.rb +1 -1
  12. data/lib/concurrent/atomic_reference/ruby.rb +1 -1
  13. data/lib/concurrent/concern/dereferenceable.rb +9 -24
  14. data/lib/concurrent/concern/obligation.rb +11 -8
  15. data/lib/concurrent/delay.rb +1 -1
  16. data/lib/concurrent/exchanger.rb +30 -17
  17. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +6 -1
  18. data/lib/concurrent/ivar.rb +1 -1
  19. data/lib/concurrent/lazy_register.rb +11 -8
  20. data/lib/concurrent/map.rb +1 -1
  21. data/lib/concurrent/maybe.rb +6 -3
  22. data/lib/concurrent/mvar.rb +26 -2
  23. data/lib/concurrent/promise.rb +1 -1
  24. data/lib/concurrent/synchronization.rb +3 -0
  25. data/lib/concurrent/synchronization/abstract_lockable_object.rb +1 -20
  26. data/lib/concurrent/synchronization/abstract_object.rb +1 -27
  27. data/lib/concurrent/synchronization/jruby_object.rb +26 -18
  28. data/lib/concurrent/synchronization/lockable_object.rb +29 -16
  29. data/lib/concurrent/synchronization/mri_object.rb +27 -19
  30. data/lib/concurrent/synchronization/object.rb +48 -53
  31. data/lib/concurrent/synchronization/rbx_lockable_object.rb +9 -8
  32. data/lib/concurrent/synchronization/rbx_object.rb +29 -21
  33. data/lib/concurrent/synchronization/volatile.rb +34 -0
  34. data/lib/concurrent/timer_task.rb +0 -1
  35. data/lib/concurrent/tvar.rb +3 -1
  36. data/lib/concurrent/utility/engine.rb +4 -0
  37. data/lib/concurrent/utility/native_extension_loader.rb +6 -3
  38. data/lib/concurrent/version.rb +2 -2
  39. data/lib/concurrent_ruby_ext.jar +0 -0
  40. metadata +7 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 47a7c20bd54b864a772fe6a21fc2b0cb34ba5a61
4
- data.tar.gz: 5e1cddf585294a1152094a5062c110d788fd7577
3
+ metadata.gz: 7d7b579740ce1202c660c37eb72e05a0097c49ab
4
+ data.tar.gz: 37f19da839100f7d8002f64edcbcada96b751ccc
5
5
  SHA512:
6
- metadata.gz: 0383528884a29a4765f663fa3189ba0498871d54d98ef7967e64d545051b8c32a7a697ddad633566664aec7efb7fd4234487c42ed3f71a525459cccda5378c49
7
- data.tar.gz: aafa4b3db601bca8675e0078f99b9a962e7ccff12d032e1aaf27fcba483e81d1ae819fac20ddb3257bfd38de319a684925a82734e1d0a758c1970b1a4a2c972b
6
+ metadata.gz: ef72505a341a9a6b984b4946f754540df39fdba7a052c6dbdd6830ce3e9fedd5856b1e7563592557c3044ec6f30a92e845402bae1aa6447a6f7c93fb5ccb458d
7
+ data.tar.gz: 29ac27d5f288f2fb6f7fb86894fa83b79a3e837cf3a0623a03d85cc4d00f0076f7fb49ea30ae313fc4d547e935ed2a7eb6903eb0cea1316c280e086631d750c8
@@ -1,6 +1,16 @@
1
1
  ### Upcoming Release v1.0.0 (TBD)
2
2
 
3
- ## Current Release v1.0.0.pre4 (08 October 2015)
3
+ ## Current Release v1.0.0.pre5 (04 November 2015)
4
+
5
+ * Further updates and improvements to the synchronization layer.
6
+ * Performance and memory usage performance with `Actor` logging.
7
+ * Fixed `ThreadPoolExecutor` task count methods.
8
+ * Improved `Async` performance for both short and long-lived objects.
9
+ * Fixed bug in `LockFreeLinkedSet`.
10
+ * Fixed bug in which `Agent#await` triggered a validation failure.
11
+ * Further `Channel` updates.
12
+
13
+ ### Release v1.0.0.pre4 (08 October 2015)
4
14
 
5
15
  * Adopted a project Code of Conduct
6
16
  * Cleared interpreter warnings
@@ -166,6 +166,7 @@ module Concurrent
166
166
  class Error < StandardError
167
167
  def initialize(message = nil)
168
168
  message ||= 'agent must be restarted before jobs can post'
169
+ super(message)
169
170
  end
170
171
  end
171
172
 
@@ -174,6 +175,7 @@ module Concurrent
174
175
  class ValidationError < Error
175
176
  def initialize(message = nil)
176
177
  message ||= 'invalid value'
178
+ super(message)
177
179
  end
178
180
  end
179
181
 
@@ -545,7 +547,9 @@ module Concurrent
545
547
  new_value = job.action.call(old_value, *job.args)
546
548
  @caller.value = nil
547
549
 
548
- if new_value != AWAIT_FLAG && ns_validate(new_value)
550
+ return if new_value == AWAIT_FLAG
551
+
552
+ if ns_validate(new_value)
549
553
  @current.value = new_value
550
554
  observers.notify_observers(Time.now, old_value, new_value)
551
555
  else
@@ -1,5 +1,6 @@
1
+ require 'concurrent/configuration'
1
2
  require 'concurrent/ivar'
2
- require 'concurrent/executor/single_thread_executor'
3
+ require 'concurrent/synchronization/lockable_object'
3
4
 
4
5
  module Concurrent
5
6
 
@@ -10,14 +11,14 @@ module Concurrent
10
11
  #
11
12
  # A more feature-rich {Concurrent::Actor} is also available when the
12
13
  # capabilities of `Async` are too limited.
13
- #
14
+ #
14
15
  # ```cucumber
15
16
  # Feature:
16
17
  # As a stateful, plain old Ruby class
17
18
  # I want safe, asynchronous behavior
18
19
  # So my long-running methods don't block the main thread
19
20
  # ```
20
- #
21
+ #
21
22
  # The `Async` module is a way to mix simple yet powerful asynchronous
22
23
  # capabilities into any plain old Ruby object or class, turning each object
23
24
  # into a simple Actor. Method calls are processed on a background thread. The
@@ -37,14 +38,13 @@ module Concurrent
37
38
  # send messages to the `gen_server` via the `cast` and `call` methods. Unlike
38
39
  # Erlang's `gen_server`, however, `Async` classes do not support linking or
39
40
  # supervision trees.
40
- #
41
+ #
41
42
  # ## Basic Usage
42
43
  #
43
44
  # When this module is mixed into a class, objects of the class become inherently
44
- # asynchronous. Each object gets its own background thread (specifically,
45
- # `SingleThreadExecutor`) on which to post asynchronous method calls.
46
- # Asynchronous method calls are executed in the background one at a time in
47
- # the order they are received.
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
48
  #
49
49
  # To create an asynchronous class, simply mix in the `Concurrent::Async` module:
50
50
  #
@@ -200,30 +200,30 @@ module Concurrent
200
200
  #
201
201
  # Class methods which are pure functions are safe. Class methods which modify
202
202
  # class variables should be avoided, for all the reasons listed above.
203
- #
203
+ #
204
204
  # ## An Important Note About Thread Safe Guarantees
205
- #
205
+ #
206
206
  # > Thread safe guarantees can only be made when asynchronous method calls
207
207
  # > are not mixed with direct method calls. Use only direct method calls
208
208
  # > when the object is used exclusively on a single thread. Use only
209
209
  # > `async` and `await` when the object is shared between threads. Once you
210
210
  # > call a method using `async` or `await`, you should no longer call methods
211
211
  # > directly on the object. Use `async` and `await` exclusively from then on.
212
- #
212
+ #
213
213
  # @example
214
- #
214
+ #
215
215
  # class Echo
216
216
  # include Concurrent::Async
217
- #
217
+ #
218
218
  # def echo(msg)
219
219
  # print "#{msg}\n"
220
220
  # end
221
221
  # end
222
- #
222
+ #
223
223
  # horn = Echo.new
224
224
  # horn.echo('zero') # synchronous, not thread-safe
225
225
  # # returns the actual return value of the method
226
- #
226
+ #
227
227
  # horn.async.echo('one') # asynchronous, non-blocking, thread-safe
228
228
  # # returns an IVar in the :pending state
229
229
  #
@@ -231,7 +231,6 @@ module Concurrent
231
231
  # # returns an IVar in the :complete state
232
232
  #
233
233
  # @see Concurrent::Actor
234
- # @see Concurrent::SingleThreadExecutor
235
234
  # @see https://en.wikipedia.org/wiki/Actor_model "Actor Model" at Wikipedia
236
235
  # @see http://www.erlang.org/doc/man/gen_server.html Erlang gen_server
237
236
  # @see http://c2.com/cgi/wiki?LetItCrash "Let It Crash" at http://c2.com/
@@ -299,24 +298,20 @@ module Concurrent
299
298
  # Delegates asynchronous, thread-safe method calls to the wrapped object.
300
299
  #
301
300
  # @!visibility private
302
- class AsyncDelegator
301
+ class AsyncDelegator < Synchronization::LockableObject
302
+ safe_initialization!
303
303
 
304
- # Create a new delegator object wrapping the given delegate,
305
- # protecting it with the given serializer, and executing it on the
306
- # given executor. Block if necessary.
304
+ # Create a new delegator object wrapping the given delegate.
307
305
  #
308
306
  # @param [Object] delegate the object to wrap and delegate method calls to
309
- # @param [Concurrent::ExecutorService] executor the executor on which to execute delegated method calls
310
- # @param [Boolean] blocking will block awaiting result when `true`
311
- def initialize(delegate, executor, blocking)
307
+ def initialize(delegate)
308
+ super()
312
309
  @delegate = delegate
313
- @executor = executor
314
- @blocking = blocking
310
+ @queue = []
311
+ @executor = Concurrent.global_io_executor
315
312
  end
316
313
 
317
- # Delegates method calls to the wrapped object. For performance,
318
- # dynamically defines the given method on the delegator so that
319
- # all future calls to `method` will not be directed here.
314
+ # Delegates method calls to the wrapped object.
320
315
  #
321
316
  # @param [Symbol] method the method being called
322
317
  # @param [Array] args zero or more arguments to the method
@@ -330,19 +325,67 @@ module Concurrent
330
325
  Async::validate_argc(@delegate, method, *args)
331
326
 
332
327
  ivar = Concurrent::IVar.new
333
- @executor.post(args) do |arguments|
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
+ # Perform all enqueued tasks.
337
+ #
338
+ # This method must be called from within the executor. It must not be
339
+ # called while already running. It will loop until the queue is empty.
340
+ def perform
341
+ loop do
342
+ ivar, method, args, block = synchronize { @queue.first }
343
+ break unless ivar # queue is empty
344
+
334
345
  begin
335
- ivar.set(@delegate.send(method, *arguments, &block))
346
+ ivar.set(@delegate.send(method, *args, &block))
336
347
  rescue => error
337
348
  ivar.fail(error)
338
349
  end
350
+
351
+ synchronize do
352
+ @queue.shift
353
+ return if @queue.empty?
354
+ end
339
355
  end
340
- ivar.wait if @blocking
341
- ivar
342
356
  end
343
357
  end
344
358
  private_constant :AsyncDelegator
345
359
 
360
+ # Delegates synchronous, thread-safe method calls to the wrapped object.
361
+ #
362
+ # @!visibility private
363
+ class AwaitDelegator
364
+
365
+ # Create a new delegator object wrapping the given delegate.
366
+ #
367
+ # @param [AsyncDelegator] delegate the object to wrap and delegate method calls to
368
+ def initialize(delegate)
369
+ @delegate = delegate
370
+ end
371
+
372
+ # Delegates method calls to the wrapped object.
373
+ #
374
+ # @param [Symbol] method the method being called
375
+ # @param [Array] args zero or more arguments to the method
376
+ #
377
+ # @return [IVar] the result of the method call
378
+ #
379
+ # @raise [NameError] the object does not respond to `method` method
380
+ # @raise [ArgumentError] the given `args` do not match the arity of `method`
381
+ def method_missing(method, *args, &block)
382
+ ivar = @delegate.send(method, *args, &block)
383
+ ivar.wait
384
+ ivar
385
+ end
386
+ end
387
+ private_constant :AwaitDelegator
388
+
346
389
  # Causes the chained method call to be performed asynchronously on the
347
390
  # object's thread. The delegated method will return a future in the
348
391
  # `:pending` state and the method call will have been scheduled on the
@@ -385,8 +428,6 @@ module Concurrent
385
428
  end
386
429
  alias_method :call, :await
387
430
 
388
- private
389
-
390
431
  # Initialize the internal serializer and other stnchronization mechanisms.
391
432
  #
392
433
  # @note This method *must* be called immediately upon object construction.
@@ -396,10 +437,8 @@ module Concurrent
396
437
  def init_synchronization
397
438
  return self if @__async_initialized__
398
439
  @__async_initialized__ = true
399
- @__async_executor__ = Concurrent::SingleThreadExecutor.new(
400
- fallback_policy: :caller_runs, auto_terminate: true)
401
- @__await_delegator__ = AsyncDelegator.new(self, @__async_executor__, true)
402
- @__async_delegator__ = AsyncDelegator.new(self, @__async_executor__, false)
440
+ @__async_delegator__ = AsyncDelegator.new(self)
441
+ @__await_delegator__ = AwaitDelegator.new(@__async_delegator__)
403
442
  self
404
443
  end
405
444
  end
@@ -111,9 +111,10 @@ module Concurrent
111
111
  #
112
112
  # @raise [ArgumentError] if the validator is not a `Proc` (when given)
113
113
  def initialize(value, opts = {})
114
+ super()
114
115
  @Validator = opts.fetch(:validator, -> v { true })
115
116
  self.observers = Collection::CopyOnNotifyObserverSet.new
116
- super(value)
117
+ self.value = value
117
118
  end
118
119
 
119
120
  # @!method value
@@ -1,5 +1,5 @@
1
1
  require 'concurrent/atomic/mutex_atomic_boolean'
2
- require 'concurrent/utility/native_extension_loader'
2
+ require 'concurrent/synchronization'
3
3
 
4
4
  module Concurrent
5
5
 
@@ -1,5 +1,5 @@
1
1
  require 'concurrent/atomic/mutex_atomic_fixnum'
2
- require 'concurrent/utility/native_extension_loader'
2
+ require 'concurrent/synchronization'
3
3
 
4
4
  module Concurrent
5
5
 
@@ -1,4 +1,4 @@
1
- require 'concurrent/utility/native_extension_loader'
1
+ require 'concurrent/synchronization'
2
2
  require 'concurrent/utility/engine'
3
3
  require 'concurrent/atomic_reference/concurrent_update_error'
4
4
  require 'concurrent/atomic_reference/mutex_atomic'
@@ -1,5 +1,5 @@
1
1
  require 'concurrent/atomic/mutex_semaphore'
2
- require 'concurrent/utility/native_extension_loader'
2
+ require 'concurrent/synchronization'
3
3
 
4
4
  module Concurrent
5
5
 
@@ -0,0 +1 @@
1
+ require 'concurrent/atomic_reference/rbx'
@@ -1,4 +1,4 @@
1
- require 'concurrent/utility/native_extension_loader'
1
+ require 'concurrent/synchronization'
2
2
 
3
3
  if defined?(Concurrent::JavaAtomicReference)
4
4
  require 'concurrent/atomic_reference/direct_update'
@@ -1,5 +1,5 @@
1
1
  if defined? Concurrent::CAtomicReference
2
- require 'concurrent/utility/native_extension_loader'
2
+ require 'concurrent/synchronization'
3
3
  require 'concurrent/atomic_reference/direct_update'
4
4
  require 'concurrent/atomic_reference/numeric_cas_wrapper'
5
5
 
@@ -9,13 +9,17 @@ module Concurrent
9
9
  #
10
10
  # @!macro copy_options
11
11
  module Dereferenceable
12
+ # NOTE: This module is going away in 2.0. In the mean time we need it to
13
+ # play nicely with the synchronization layer. This means that the
14
+ # including class SHOULD be synchronized and it MUST implement a
15
+ # `#synchronize` method. Not doing so will lead to runtime errors.
12
16
 
13
17
  # Return the value this object represents after applying the options specified
14
18
  # by the `#set_deref_options` method.
15
19
  #
16
20
  # @return [Object] the current value of the object
17
21
  def value
18
- mutex.synchronize { apply_deref_options(@value) }
22
+ synchronize { apply_deref_options(@value) }
19
23
  end
20
24
  alias_method :deref, :value
21
25
 
@@ -25,43 +29,24 @@ module Concurrent
25
29
  #
26
30
  # @param [Object] value the new value
27
31
  def value=(value)
28
- mutex.synchronize{ @value = value }
29
- end
30
-
31
- # A mutex lock used for synchronizing thread-safe operations. Methods defined
32
- # by `Dereferenceable` are synchronized using the `Mutex` returned from this
33
- # method. Operations performed by the including class that operate on the
34
- # `@value` instance variable should be locked with this `Mutex`.
35
- #
36
- # @return [Mutex] the synchronization object
37
- def mutex
38
- @mutex
39
- end
40
-
41
- # Initializes the internal `Mutex`.
42
- #
43
- # @note This method *must* be called from within the constructor of the including class.
44
- #
45
- # @see #mutex
46
- def init_mutex(mutex = Mutex.new)
47
- @mutex = mutex
32
+ synchronize{ @value = value }
48
33
  end
49
34
 
50
35
  # @!macro [attach] dereferenceable_set_deref_options
51
36
  # Set the options which define the operations #value performs before
52
37
  # returning data to the caller (dereferencing).
53
- #
38
+ #
54
39
  # @note Most classes that include this module will call `#set_deref_options`
55
40
  # from within the constructor, thus allowing these options to be set at
56
41
  # object creation.
57
- #
42
+ #
58
43
  # @param [Hash] opts the options defining dereference behavior.
59
44
  # @option opts [String] :dup_on_deref (false) call `#dup` before returning the data
60
45
  # @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data
61
46
  # @option opts [String] :copy_on_deref (nil) call the given `Proc` passing
62
47
  # the internal value and returning the value returned from the proc
63
48
  def set_deref_options(opts = {})
64
- mutex.synchronize{ ns_set_deref_options(opts) }
49
+ synchronize{ ns_set_deref_options(opts) }
65
50
  end
66
51
 
67
52
  # @!macro dereferenceable_set_deref_options
@@ -9,6 +9,10 @@ module Concurrent
9
9
 
10
10
  module Obligation
11
11
  include Concern::Dereferenceable
12
+ # NOTE: The Dereferenceable module is going away in 2.0. In the mean time
13
+ # we need it to place nicely with the synchronization layer. This means
14
+ # that the including class SHOULD be synchronized and it MUST implement a
15
+ # `#synchronize` method. Not doing so will lead to runtime errors.
12
16
 
13
17
  # Has the obligation been fulfilled?
14
18
  #
@@ -104,7 +108,7 @@ module Concurrent
104
108
  #
105
109
  # @return [Symbol] the current state
106
110
  def state
107
- mutex.synchronize { @state }
111
+ synchronize { @state }
108
112
  end
109
113
 
110
114
  # If an exception was raised during processing this will return the
@@ -113,7 +117,7 @@ module Concurrent
113
117
  #
114
118
  # @return [Exception] the exception raised during processing or `nil`
115
119
  def reason
116
- mutex.synchronize { @reason }
120
+ synchronize { @reason }
117
121
  end
118
122
 
119
123
  # @example allows Obligation to be risen
@@ -132,8 +136,7 @@ module Concurrent
132
136
  end
133
137
 
134
138
  # @!visibility private
135
- def init_obligation(*args)
136
- init_mutex(*args)
139
+ def init_obligation
137
140
  @event = Event.new
138
141
  end
139
142
 
@@ -155,7 +158,7 @@ module Concurrent
155
158
 
156
159
  # @!visibility private
157
160
  def state=(value)
158
- mutex.synchronize { ns_set_state(value) }
161
+ synchronize { ns_set_state(value) }
159
162
  end
160
163
 
161
164
  # Atomic compare and set operation
@@ -163,12 +166,12 @@ module Concurrent
163
166
  #
164
167
  # @param [Symbol] next_state
165
168
  # @param [Symbol] expected_current
166
- #
169
+ #
167
170
  # @return [Boolean] true is state is changed, false otherwise
168
171
  #
169
172
  # @!visibility private
170
173
  def compare_and_set_state(next_state, *expected_current)
171
- mutex.synchronize do
174
+ synchronize do
172
175
  if expected_current.include? @state
173
176
  @state = next_state
174
177
  true
@@ -184,7 +187,7 @@ module Concurrent
184
187
  #
185
188
  # @!visibility private
186
189
  def if_state(*expected_states)
187
- mutex.synchronize do
190
+ synchronize do
188
191
  raise ArgumentError.new('no block given') unless block_given?
189
192
 
190
193
  if expected_states.include? @state