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
data/lib/concurrent/delay.rb
CHANGED
data/lib/concurrent/exchanger.rb
CHANGED
@@ -38,7 +38,7 @@ module Concurrent
|
|
38
38
|
# threads.each {|t| t.join(2) }
|
39
39
|
#
|
40
40
|
# @!visibility private
|
41
|
-
class AbstractExchanger
|
41
|
+
class AbstractExchanger < Synchronization::Object
|
42
42
|
|
43
43
|
# @!visibility private
|
44
44
|
CANCEL = Object.new
|
@@ -46,7 +46,7 @@ module Concurrent
|
|
46
46
|
|
47
47
|
# @!macro [attach] exchanger_method_initialize
|
48
48
|
def initialize
|
49
|
-
|
49
|
+
super
|
50
50
|
end
|
51
51
|
|
52
52
|
# @!macro [attach] exchanger_method_do_exchange
|
@@ -140,24 +140,38 @@ module Concurrent
|
|
140
140
|
# not include the arena or the multi-processor spin loops.
|
141
141
|
# http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/concurrent/Exchanger.java
|
142
142
|
|
143
|
-
|
144
|
-
|
145
|
-
|
143
|
+
safe_initialization!
|
144
|
+
|
145
|
+
class Node < Concurrent::Synchronization::Object
|
146
|
+
attr_volatile_with_cas :value
|
147
|
+
safe_initialization!
|
148
|
+
|
146
149
|
def initialize(item)
|
147
|
-
|
148
|
-
@
|
149
|
-
|
150
|
+
super()
|
151
|
+
@Item = item
|
152
|
+
@Latch = Concurrent::CountDownLatch.new
|
153
|
+
self.value = nil
|
154
|
+
end
|
155
|
+
|
156
|
+
def latch
|
157
|
+
@Latch
|
158
|
+
end
|
159
|
+
|
160
|
+
def item
|
161
|
+
@Item
|
150
162
|
end
|
151
163
|
end
|
152
164
|
private_constant :Node
|
153
165
|
|
154
166
|
# @!macro exchanger_method_initialize
|
155
167
|
def initialize
|
156
|
-
|
168
|
+
super
|
157
169
|
end
|
158
170
|
|
159
171
|
private
|
160
172
|
|
173
|
+
attr_volatile_with_cas(:slot)
|
174
|
+
|
161
175
|
# @!macro exchanger_method_do_exchange
|
162
176
|
#
|
163
177
|
# @return [Object, CANCEL] the value exchanged by the other thread; {CANCEL} on timeout
|
@@ -247,20 +261,19 @@ module Concurrent
|
|
247
261
|
# - Return the occupier's item
|
248
262
|
|
249
263
|
value = NULL if value.nil? # The sentinel allows nil to be a valid value
|
250
|
-
slot = @slot # Convenience to minimize typing @
|
251
264
|
me = Node.new(value) # create my node in case I need to occupy
|
252
265
|
end_at = Concurrent.monotonic_time + timeout.to_f # The time to give up
|
253
266
|
|
254
267
|
result = loop do
|
255
|
-
other = slot
|
256
|
-
if other &&
|
268
|
+
other = slot
|
269
|
+
if other && compare_and_set_slot(other, nil)
|
257
270
|
# try to fulfill
|
258
|
-
if other.
|
271
|
+
if other.compare_and_set_value(nil, value)
|
259
272
|
# happy path
|
260
273
|
other.latch.count_down
|
261
274
|
break other.item
|
262
275
|
end
|
263
|
-
elsif other.nil? &&
|
276
|
+
elsif other.nil? && compare_and_set_slot(nil, me)
|
264
277
|
# try to occupy
|
265
278
|
timeout = end_at - Concurrent.monotonic_time if timeout
|
266
279
|
if me.latch.wait(timeout)
|
@@ -268,11 +281,11 @@ module Concurrent
|
|
268
281
|
break me.value
|
269
282
|
else
|
270
283
|
# attempt to remove myself from the slot
|
271
|
-
if
|
284
|
+
if compare_and_set_slot(me, nil)
|
272
285
|
break CANCEL
|
273
|
-
elsif !me.
|
286
|
+
elsif !me.compare_and_set_value(nil, CANCEL)
|
274
287
|
# I've failed to block the fulfiller
|
275
|
-
break me.
|
288
|
+
break me.value
|
276
289
|
end
|
277
290
|
end
|
278
291
|
end
|
@@ -101,6 +101,11 @@ module Concurrent
|
|
101
101
|
synchronize { ns_worker_died worker }
|
102
102
|
end
|
103
103
|
|
104
|
+
# @!visibility private
|
105
|
+
def worker_task_completed
|
106
|
+
synchronize { @completed_task_count += 1 }
|
107
|
+
end
|
108
|
+
|
104
109
|
private
|
105
110
|
|
106
111
|
# @!visibility private
|
@@ -180,7 +185,6 @@ module Concurrent
|
|
180
185
|
worker = (@ready.pop if @pool.size >= @min_length) || ns_add_busy_worker
|
181
186
|
if worker
|
182
187
|
worker << [task, args]
|
183
|
-
@completed_task_count += 1
|
184
188
|
true
|
185
189
|
else
|
186
190
|
false
|
@@ -327,6 +331,7 @@ module Concurrent
|
|
327
331
|
|
328
332
|
def run_task(pool, task, args)
|
329
333
|
task.call(*args)
|
334
|
+
pool.worker_task_completed
|
330
335
|
rescue => ex
|
331
336
|
# let it fail
|
332
337
|
log DEBUG, ex
|
data/lib/concurrent/ivar.rb
CHANGED
@@ -7,19 +7,22 @@ module Concurrent
|
|
7
7
|
#
|
8
8
|
# @example
|
9
9
|
# register = Concurrent::LazyRegister.new
|
10
|
-
# #=> #<Concurrent::LazyRegister:0x007fd7ecd5e230 @
|
10
|
+
# #=> #<Concurrent::LazyRegister:0x007fd7ecd5e230 @Data=#<Concurrent::AtomicReference:0x007fd7ecd5e1e0>>
|
11
11
|
# register[:key]
|
12
12
|
# #=> nil
|
13
13
|
# register.add(:key) { Concurrent::Actor.spawn!(Actor::AdHoc, :ping) { -> message { message } } }
|
14
|
-
# #=> #<Concurrent::LazyRegister:0x007fd7ecd5e230 @
|
14
|
+
# #=> #<Concurrent::LazyRegister:0x007fd7ecd5e230 @Data=#<Concurrent::AtomicReference:0x007fd7ecd5e1e0>>
|
15
15
|
# register[:key]
|
16
16
|
# #=> #<Concurrent::Actor::Reference /ping (Concurrent::Actor::AdHoc)>
|
17
17
|
#
|
18
18
|
# @!macro edge_warning
|
19
|
-
class LazyRegister
|
19
|
+
class LazyRegister < Synchronization::Object
|
20
|
+
|
21
|
+
private(*attr_volatile_with_cas(:data))
|
20
22
|
|
21
23
|
def initialize
|
22
|
-
|
24
|
+
super
|
25
|
+
self.data = {}
|
23
26
|
end
|
24
27
|
|
25
28
|
# Element reference. Retrieves the value object corresponding to the
|
@@ -31,7 +34,7 @@ module Concurrent
|
|
31
34
|
#
|
32
35
|
# @raise Exception when the initialization block fails
|
33
36
|
def [](key)
|
34
|
-
delay =
|
37
|
+
delay = data[key]
|
35
38
|
delay ? delay.value! : nil
|
36
39
|
end
|
37
40
|
|
@@ -40,7 +43,7 @@ module Concurrent
|
|
40
43
|
# @param [Object] key
|
41
44
|
# @return [true, false] if the key is registered
|
42
45
|
def registered?(key)
|
43
|
-
|
46
|
+
data.key?(key)
|
44
47
|
end
|
45
48
|
|
46
49
|
alias_method :key?, :registered?
|
@@ -55,7 +58,7 @@ module Concurrent
|
|
55
58
|
# @return [LazyRegister] self
|
56
59
|
def register(key, &block)
|
57
60
|
delay = Delay.new(executor: :immediate, &block)
|
58
|
-
|
61
|
+
update_data { |h| h.merge(key => delay) }
|
59
62
|
self
|
60
63
|
end
|
61
64
|
|
@@ -68,7 +71,7 @@ module Concurrent
|
|
68
71
|
#
|
69
72
|
# @return [LazyRegister] self
|
70
73
|
def unregister(key)
|
71
|
-
|
74
|
+
update_data { |h| h.dup.tap { |j| j.delete(key) } }
|
72
75
|
self
|
73
76
|
end
|
74
77
|
|
data/lib/concurrent/map.rb
CHANGED
data/lib/concurrent/maybe.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'concurrent/synchronization'
|
2
|
+
|
1
3
|
module Concurrent
|
2
4
|
|
3
5
|
# A `Maybe` encapsulates an optional value. A `Maybe` either contains a value
|
@@ -99,8 +101,9 @@ module Concurrent
|
|
99
101
|
#
|
100
102
|
# @see https://hackage.haskell.org/package/base-4.2.0.1/docs/Data-Maybe.html Haskell Data.Maybe
|
101
103
|
# @see https://github.com/purescript/purescript-maybe/blob/master/docs/Data.Maybe.md PureScript Data.Maybe
|
102
|
-
class Maybe
|
104
|
+
class Maybe < Synchronization::Object
|
103
105
|
include Comparable
|
106
|
+
safe_initialization!
|
104
107
|
|
105
108
|
# Indicates that the given attribute has not been set.
|
106
109
|
# When `Just` the {#nothing} getter will return `NONE`.
|
@@ -168,7 +171,7 @@ module Concurrent
|
|
168
171
|
end
|
169
172
|
|
170
173
|
# Is this `Maybe` a `Just` (successfully fulfilled with a value)?
|
171
|
-
#
|
174
|
+
#
|
172
175
|
# @return [Boolean] True if `Just` or false if `Nothing`.
|
173
176
|
def just?
|
174
177
|
! nothing?
|
@@ -176,7 +179,7 @@ module Concurrent
|
|
176
179
|
alias :fulfilled? :just?
|
177
180
|
|
178
181
|
# Is this `Maybe` a `nothing` (rejected with an exception upon fulfillment)?
|
179
|
-
#
|
182
|
+
#
|
180
183
|
# @return [Boolean] True if `Nothing` or false if `Just`.
|
181
184
|
def nothing?
|
182
185
|
@nothing != NONE
|
data/lib/concurrent/mvar.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'concurrent/concern/dereferenceable'
|
2
|
+
require 'concurrent/synchronization'
|
2
3
|
|
3
4
|
module Concurrent
|
4
5
|
|
@@ -34,9 +35,9 @@ module Concurrent
|
|
34
35
|
# 2. S. Peyton Jones, A. Gordon, and S. Finne. [Concurrent Haskell](http://dl.acm.org/citation.cfm?id=237794).
|
35
36
|
# In Proceedings of the 23rd Symposium on Principles of Programming Languages
|
36
37
|
# (PoPL), 1996.
|
37
|
-
class MVar
|
38
|
-
|
38
|
+
class MVar < Synchronization::Object
|
39
39
|
include Concern::Dereferenceable
|
40
|
+
safe_initialization!
|
40
41
|
|
41
42
|
# Unique value that represents that an `MVar` was empty
|
42
43
|
EMPTY = Object.new
|
@@ -78,6 +79,23 @@ module Concurrent
|
|
78
79
|
end
|
79
80
|
end
|
80
81
|
|
82
|
+
# acquires lock on the from an `MVAR`, yields the value to provided block,
|
83
|
+
# and release lock. A timeout can be set to limit the time spent blocked,
|
84
|
+
# in which case it returns `TIMEOUT` if the time is exceeded.
|
85
|
+
# @return [Object] the value returned by the block, or `TIMEOUT`
|
86
|
+
def borrow(timeout = nil)
|
87
|
+
@mutex.synchronize do
|
88
|
+
wait_for_full(timeout)
|
89
|
+
|
90
|
+
# if we timeoud out we'll still be empty
|
91
|
+
if unlocked_full?
|
92
|
+
yield @value
|
93
|
+
else
|
94
|
+
TIMEOUT
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
81
99
|
# Put a value into an `MVar`, blocking if there is already a value until
|
82
100
|
# it is empty. A timeout can be set to limit the time spent blocked, in
|
83
101
|
# which case it returns `TIMEOUT` if the time is exceeded.
|
@@ -183,6 +201,12 @@ module Concurrent
|
|
183
201
|
!empty?
|
184
202
|
end
|
185
203
|
|
204
|
+
protected
|
205
|
+
|
206
|
+
def synchronize(&block)
|
207
|
+
@mutex.synchronize(&block)
|
208
|
+
end
|
209
|
+
|
186
210
|
private
|
187
211
|
|
188
212
|
def unlocked_empty?
|
data/lib/concurrent/promise.rb
CHANGED
@@ -304,7 +304,7 @@ module Concurrent
|
|
304
304
|
# @return [Promise] the new promise
|
305
305
|
def then(rescuer = nil, &block)
|
306
306
|
raise ArgumentError.new('rescuers and block are both missing') if rescuer.nil? && !block_given?
|
307
|
-
block = Proc.new { |result| result }
|
307
|
+
block = Proc.new { |result| result } unless block_given?
|
308
308
|
child = Promise.new(
|
309
309
|
parent: self,
|
310
310
|
executor: @executor,
|
@@ -2,11 +2,13 @@ require 'concurrent/utility/engine'
|
|
2
2
|
|
3
3
|
require 'concurrent/synchronization/abstract_object'
|
4
4
|
require 'concurrent/utility/native_extension_loader' # load native parts first
|
5
|
+
Concurrent.load_native_extensions
|
5
6
|
|
6
7
|
require 'concurrent/synchronization/mri_object'
|
7
8
|
require 'concurrent/synchronization/jruby_object'
|
8
9
|
require 'concurrent/synchronization/rbx_object'
|
9
10
|
require 'concurrent/synchronization/object'
|
11
|
+
require 'concurrent/synchronization/volatile'
|
10
12
|
|
11
13
|
require 'concurrent/synchronization/abstract_lockable_object'
|
12
14
|
require 'concurrent/synchronization/mri_lockable_object'
|
@@ -20,6 +22,7 @@ require 'concurrent/synchronization/lock'
|
|
20
22
|
|
21
23
|
module Concurrent
|
22
24
|
# {include:file:doc/synchronization.md}
|
25
|
+
# {include:file:doc/synchronization-notes.md}
|
23
26
|
module Synchronization
|
24
27
|
end
|
25
28
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Concurrent
|
2
2
|
module Synchronization
|
3
|
-
|
3
|
+
|
4
4
|
# @!visibility private
|
5
5
|
class AbstractLockableObject < Object
|
6
6
|
|
@@ -15,25 +15,6 @@ module Concurrent
|
|
15
15
|
raise NotImplementedError
|
16
16
|
end
|
17
17
|
|
18
|
-
# @!macro [attach] synchronization_object_method_ns_initialize
|
19
|
-
#
|
20
|
-
# initialization of the object called inside synchronize block
|
21
|
-
# @note has to be called manually when required in children of this class
|
22
|
-
# @example
|
23
|
-
# class Child < Concurrent::Synchornization::Object
|
24
|
-
# def initialize(*args, &block)
|
25
|
-
# super(&nil)
|
26
|
-
# synchronize { ns_initialize(*args, &block) }
|
27
|
-
# end
|
28
|
-
#
|
29
|
-
# def ns_initialize(*args, &block)
|
30
|
-
# @args = args
|
31
|
-
# end
|
32
|
-
# end
|
33
|
-
# TODO (pitr 12-Sep-2015): remove
|
34
|
-
def ns_initialize(*args, &block)
|
35
|
-
end
|
36
|
-
|
37
18
|
# @!macro [attach] synchronization_object_method_ns_wait_until
|
38
19
|
#
|
39
20
|
# Wait until condition is met or timeout passes,
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module Concurrent
|
2
2
|
module Synchronization
|
3
3
|
|
4
|
-
# @!macro synchronization_object
|
5
4
|
# @!visibility private
|
5
|
+
# @!macro internal_implementation_note
|
6
6
|
class AbstractObject
|
7
7
|
|
8
8
|
# @abstract has to be implemented based on Ruby runtime
|
@@ -10,38 +10,12 @@ module Concurrent
|
|
10
10
|
raise NotImplementedError
|
11
11
|
end
|
12
12
|
|
13
|
-
# @!macro [attach] synchronization_object_method_ensure_ivar_visibility
|
14
|
-
#
|
15
|
-
# Allows to construct immutable objects where all fields are visible after initialization, not requiring
|
16
|
-
# further synchronization on access.
|
17
|
-
# @example
|
18
|
-
# class AClass
|
19
|
-
# attr_reader :val
|
20
|
-
# def initialize(val)
|
21
|
-
# @val = val # final value, after assignment it's not changed (just convention, not enforced)
|
22
|
-
# ensure_ivar_visibility!
|
23
|
-
# # now it can be shared as Java's final field
|
24
|
-
# end
|
25
|
-
# end
|
26
|
-
# @!visibility private
|
27
|
-
def ensure_ivar_visibility!
|
28
|
-
# We have to prevent ivar writes to reordered with storing of the final instance reference
|
29
|
-
# Therefore wee need a fullFence to prevent reordering in both directions.
|
30
|
-
full_memory_barrier
|
31
|
-
end
|
32
|
-
|
33
|
-
protected
|
34
|
-
|
35
13
|
# @!visibility private
|
36
14
|
# @abstract
|
37
15
|
def full_memory_barrier
|
38
16
|
raise NotImplementedError
|
39
17
|
end
|
40
18
|
|
41
|
-
# @!macro [attach] synchronization_object_method_self_attr_volatile
|
42
|
-
#
|
43
|
-
# creates methods for reading and writing to a instance variable with volatile (Java semantic) instance variable
|
44
|
-
# return [Array<Symbol>] names of defined method names
|
45
19
|
def self.attr_volatile(*names)
|
46
20
|
raise NotImplementedError
|
47
21
|
end
|
@@ -3,33 +3,41 @@ module Concurrent
|
|
3
3
|
|
4
4
|
if Concurrent.on_jruby? && Concurrent.java_extensions_loaded?
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def initialize
|
11
|
-
# nothing to do
|
6
|
+
module JRubyAttrVolatile
|
7
|
+
def self.included(base)
|
8
|
+
base.extend(ClassMethods)
|
12
9
|
end
|
13
10
|
|
14
|
-
|
15
|
-
names
|
11
|
+
module ClassMethods
|
12
|
+
def attr_volatile(*names)
|
13
|
+
names.each do |name|
|
16
14
|
|
17
|
-
|
15
|
+
ivar = :"@volatile_#{name}"
|
18
16
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
18
|
+
def #{name}
|
19
|
+
instance_variable_get_volatile(:#{ivar})
|
20
|
+
end
|
23
21
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
22
|
+
def #{name}=(value)
|
23
|
+
instance_variable_set_volatile(:#{ivar}, value)
|
24
|
+
end
|
25
|
+
RUBY
|
28
26
|
|
27
|
+
end
|
28
|
+
names.map { |n| [n, :"#{n}="] }.flatten
|
29
29
|
end
|
30
|
-
names.map { |n| [n, :"#{n}="] }.flatten
|
31
30
|
end
|
31
|
+
end
|
32
32
|
|
33
|
+
# @!visibility private
|
34
|
+
# @!macro internal_implementation_note
|
35
|
+
class JRubyObject < AbstractObject
|
36
|
+
include JRubyAttrVolatile
|
37
|
+
|
38
|
+
def initialize
|
39
|
+
# nothing to do
|
40
|
+
end
|
33
41
|
end
|
34
42
|
end
|
35
43
|
end
|