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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -1
- data/lib/concurrent/agent.rb +5 -1
- data/lib/concurrent/async.rb +77 -38
- data/lib/concurrent/atom.rb +2 -1
- data/lib/concurrent/atomic/atomic_boolean.rb +1 -1
- data/lib/concurrent/atomic/atomic_fixnum.rb +1 -1
- data/lib/concurrent/atomic/atomic_reference.rb +1 -1
- data/lib/concurrent/atomic/semaphore.rb +1 -1
- data/lib/concurrent/atomic_reference/jruby+truffle.rb +1 -0
- data/lib/concurrent/atomic_reference/jruby.rb +1 -1
- data/lib/concurrent/atomic_reference/ruby.rb +1 -1
- data/lib/concurrent/concern/dereferenceable.rb +9 -24
- data/lib/concurrent/concern/obligation.rb +11 -8
- data/lib/concurrent/delay.rb +1 -1
- data/lib/concurrent/exchanger.rb +30 -17
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +6 -1
- data/lib/concurrent/ivar.rb +1 -1
- data/lib/concurrent/lazy_register.rb +11 -8
- data/lib/concurrent/map.rb +1 -1
- data/lib/concurrent/maybe.rb +6 -3
- data/lib/concurrent/mvar.rb +26 -2
- data/lib/concurrent/promise.rb +1 -1
- data/lib/concurrent/synchronization.rb +3 -0
- data/lib/concurrent/synchronization/abstract_lockable_object.rb +1 -20
- data/lib/concurrent/synchronization/abstract_object.rb +1 -27
- data/lib/concurrent/synchronization/jruby_object.rb +26 -18
- data/lib/concurrent/synchronization/lockable_object.rb +29 -16
- data/lib/concurrent/synchronization/mri_object.rb +27 -19
- data/lib/concurrent/synchronization/object.rb +48 -53
- data/lib/concurrent/synchronization/rbx_lockable_object.rb +9 -8
- data/lib/concurrent/synchronization/rbx_object.rb +29 -21
- data/lib/concurrent/synchronization/volatile.rb +34 -0
- data/lib/concurrent/timer_task.rb +0 -1
- data/lib/concurrent/tvar.rb +3 -1
- data/lib/concurrent/utility/engine.rb +4 -0
- data/lib/concurrent/utility/native_extension_loader.rb +6 -3
- data/lib/concurrent/version.rb +2 -2
- data/lib/concurrent_ruby_ext.jar +0 -0
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7d7b579740ce1202c660c37eb72e05a0097c49ab
|
4
|
+
data.tar.gz: 37f19da839100f7d8002f64edcbcada96b751ccc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef72505a341a9a6b984b4946f754540df39fdba7a052c6dbdd6830ce3e9fedd5856b1e7563592557c3044ec6f30a92e845402bae1aa6447a6f7c93fb5ccb458d
|
7
|
+
data.tar.gz: 29ac27d5f288f2fb6f7fb86894fa83b79a3e837cf3a0623a03d85cc4d00f0076f7fb49ea30ae313fc4d547e935ed2a7eb6903eb0cea1316c280e086631d750c8
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
### Upcoming Release v1.0.0 (TBD)
|
2
2
|
|
3
|
-
## Current Release v1.0.0.
|
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
|
data/lib/concurrent/agent.rb
CHANGED
@@ -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
|
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
|
data/lib/concurrent/async.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
require 'concurrent/configuration'
|
1
2
|
require 'concurrent/ivar'
|
2
|
-
require 'concurrent/
|
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
|
45
|
-
#
|
46
|
-
#
|
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
|
-
|
310
|
-
|
311
|
-
def initialize(delegate, executor, blocking)
|
307
|
+
def initialize(delegate)
|
308
|
+
super()
|
312
309
|
@delegate = delegate
|
313
|
-
@
|
314
|
-
@
|
310
|
+
@queue = []
|
311
|
+
@executor = Concurrent.global_io_executor
|
315
312
|
end
|
316
313
|
|
317
|
-
# Delegates method calls to the wrapped object.
|
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
|
-
|
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, *
|
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
|
-
@
|
400
|
-
|
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
|
data/lib/concurrent/atom.rb
CHANGED
@@ -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
|
-
|
117
|
+
self.value = value
|
117
118
|
end
|
118
119
|
|
119
120
|
# @!method value
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'concurrent/atomic_reference/rbx'
|
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
190
|
+
synchronize do
|
188
191
|
raise ArgumentError.new('no block given') unless block_given?
|
189
192
|
|
190
193
|
if expected_states.include? @state
|