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
@@ -155,7 +155,7 @@ module Concurrent
155
155
  protected
156
156
 
157
157
  def ns_initialize(opts, &block)
158
- init_obligation(self)
158
+ init_obligation
159
159
  set_deref_options(opts)
160
160
  @executor = opts[:executor]
161
161
 
@@ -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
- raise NotImplementedError
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
- # @!visibility private
144
- class Node < Concurrent::AtomicReference
145
- attr_reader :item, :latch
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
- @item = item
148
- @latch = Concurrent::CountDownLatch.new
149
- super(nil)
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
- @slot = Concurrent::AtomicReference.new
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.get
256
- if other && slot.compare_and_set(other, nil)
268
+ other = slot
269
+ if other && compare_and_set_slot(other, nil)
257
270
  # try to fulfill
258
- if other.compare_and_set(nil, value)
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? && slot.compare_and_set(nil, me)
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 slot.compare_and_set(me, nil)
284
+ if compare_and_set_slot(me, nil)
272
285
  break CANCEL
273
- elsif !me.compare_and_set(nil, CANCEL)
286
+ elsif !me.compare_and_set_value(nil, CANCEL)
274
287
  # I've failed to block the fulfiller
275
- break me.get
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
@@ -153,7 +153,7 @@ module Concurrent
153
153
  # @!visibility private
154
154
  def ns_initialize(value, opts)
155
155
  value = yield if block_given?
156
- init_obligation(self)
156
+ init_obligation
157
157
  self.observers = Collection::CopyOnWriteObserverSet.new
158
158
  set_deref_options(opts)
159
159
 
@@ -7,19 +7,22 @@ module Concurrent
7
7
  #
8
8
  # @example
9
9
  # register = Concurrent::LazyRegister.new
10
- # #=> #<Concurrent::LazyRegister:0x007fd7ecd5e230 @data=#<Concurrent::AtomicReference:0x007fd7ecd5e1e0>>
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 @data=#<Concurrent::AtomicReference:0x007fd7ecd5e1e0>>
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
- @data = AtomicReference.new(Hash.new)
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 = @data.get[key]
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
- @data.get.key?(key)
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
- @data.update { |h| h.merge(key => delay) }
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
- @data.update { |h| h.dup.tap { |j| j.delete(key) } }
74
+ update_data { |h| h.dup.tap { |j| j.delete(key) } }
72
75
  self
73
76
  end
74
77
 
@@ -1,6 +1,6 @@
1
1
  require 'thread'
2
2
  require 'concurrent/constants'
3
- require 'concurrent/utility/native_extension_loader'
3
+ require 'concurrent/synchronization'
4
4
 
5
5
  module Concurrent
6
6
  # @!visibility private
@@ -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
@@ -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?
@@ -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 } if block.nil?
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
- # @!macro synchronization_object
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
- # @!visibility private
7
- # @!macro internal_implementation_note
8
- class JRubyObject < AbstractObject
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
- def self.attr_volatile(*names)
15
- names.each do |name|
11
+ module ClassMethods
12
+ def attr_volatile(*names)
13
+ names.each do |name|
16
14
 
17
- ivar = :"@volatile_#{name}"
15
+ ivar = :"@volatile_#{name}"
18
16
 
19
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
20
- def #{name}
21
- instance_variable_get_volatile(:#{ivar})
22
- end
17
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
18
+ def #{name}
19
+ instance_variable_get_volatile(:#{ivar})
20
+ end
23
21
 
24
- def #{name}=(value)
25
- instance_variable_set_volatile(:#{ivar}, value)
26
- end
27
- RUBY
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