concurrent-ruby 1.0.0.pre1-java → 1.0.0.pre2-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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -1
  3. data/README.md +16 -18
  4. data/lib/concurrent.rb +3 -3
  5. data/lib/concurrent/agent.rb +583 -0
  6. data/lib/concurrent/array.rb +1 -0
  7. data/lib/concurrent/async.rb +236 -111
  8. data/lib/concurrent/atom.rb +101 -46
  9. data/lib/concurrent/atomic/atomic_boolean.rb +2 -0
  10. data/lib/concurrent/atomic/atomic_fixnum.rb +2 -0
  11. data/lib/concurrent/atomic/cyclic_barrier.rb +1 -1
  12. data/lib/concurrent/atomic/event.rb +1 -1
  13. data/lib/concurrent/atomic/mutex_atomic_boolean.rb +1 -1
  14. data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +1 -1
  15. data/lib/concurrent/atomic/mutex_count_down_latch.rb +1 -1
  16. data/lib/concurrent/atomic/mutex_semaphore.rb +2 -2
  17. data/lib/concurrent/atomic/read_write_lock.rb +5 -4
  18. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
  19. data/lib/concurrent/atomic/thread_local_var.rb +2 -0
  20. data/lib/concurrent/atomic_reference/mutex_atomic.rb +1 -1
  21. data/lib/concurrent/atomics.rb +6 -4
  22. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +7 -15
  23. data/lib/concurrent/collection/copy_on_write_observer_set.rb +7 -15
  24. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +5 -0
  25. data/lib/concurrent/concern/observable.rb +38 -13
  26. data/lib/concurrent/configuration.rb +5 -4
  27. data/lib/concurrent/delay.rb +9 -8
  28. data/lib/concurrent/exchanger.rb +2 -0
  29. data/lib/concurrent/executor/abstract_executor_service.rb +2 -2
  30. data/lib/concurrent/executor/java_single_thread_executor.rb +0 -1
  31. data/lib/concurrent/executor/ruby_executor_service.rb +10 -4
  32. data/lib/concurrent/executor/ruby_single_thread_executor.rb +10 -68
  33. data/lib/concurrent/executor/safe_task_executor.rb +7 -8
  34. data/lib/concurrent/executor/serialized_execution.rb +4 -4
  35. data/lib/concurrent/executor/single_thread_executor.rb +20 -10
  36. data/lib/concurrent/executor/timer_set.rb +4 -2
  37. data/lib/concurrent/executors.rb +0 -1
  38. data/lib/concurrent/future.rb +3 -2
  39. data/lib/concurrent/hash.rb +1 -1
  40. data/lib/concurrent/immutable_struct.rb +5 -1
  41. data/lib/concurrent/ivar.rb +1 -1
  42. data/lib/concurrent/mutable_struct.rb +7 -6
  43. data/lib/concurrent/{executor/executor.rb → options.rb} +4 -3
  44. data/lib/concurrent/promise.rb +3 -2
  45. data/lib/concurrent/scheduled_task.rb +3 -2
  46. data/lib/concurrent/settable_struct.rb +5 -4
  47. data/lib/concurrent/synchronization.rb +11 -3
  48. data/lib/concurrent/synchronization/abstract_lockable_object.rb +117 -0
  49. data/lib/concurrent/synchronization/abstract_object.rb +16 -129
  50. data/lib/concurrent/synchronization/abstract_struct.rb +2 -3
  51. data/lib/concurrent/synchronization/condition.rb +6 -4
  52. data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  53. data/lib/concurrent/synchronization/{java_object.rb → jruby_object.rb} +5 -3
  54. data/lib/concurrent/synchronization/lock.rb +3 -2
  55. data/lib/concurrent/synchronization/lockable_object.rb +59 -0
  56. data/lib/concurrent/synchronization/mri_lockable_object.rb +71 -0
  57. data/lib/concurrent/synchronization/mri_object.rb +35 -0
  58. data/lib/concurrent/synchronization/object.rb +111 -39
  59. data/lib/concurrent/synchronization/rbx_lockable_object.rb +64 -0
  60. data/lib/concurrent/synchronization/rbx_object.rb +17 -68
  61. data/lib/concurrent/thread_safe/util.rb +0 -9
  62. data/lib/concurrent/thread_safe/util/adder.rb +3 -0
  63. data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +3 -1
  64. data/lib/concurrent/thread_safe/util/cheap_lockable.rb +3 -0
  65. data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +1 -0
  66. data/lib/concurrent/thread_safe/util/striped64.rb +6 -1
  67. data/lib/concurrent/thread_safe/util/volatile.rb +2 -0
  68. data/lib/concurrent/thread_safe/util/xor_shift_random.rb +2 -0
  69. data/lib/concurrent/tvar.rb +36 -0
  70. data/lib/concurrent/utility/at_exit.rb +1 -1
  71. data/lib/concurrent/utility/monotonic_time.rb +3 -4
  72. data/lib/concurrent/utility/native_extension_loader.rb +1 -1
  73. data/lib/concurrent/version.rb +2 -2
  74. data/lib/concurrent_ruby_ext.jar +0 -0
  75. metadata +11 -6
  76. data/lib/concurrent/synchronization/monitor_object.rb +0 -27
  77. data/lib/concurrent/synchronization/mutex_object.rb +0 -43
@@ -9,7 +9,6 @@ module Concurrent
9
9
  def initialize(*values)
10
10
  super()
11
11
  ns_initialize(*values)
12
- ensure_ivar_visibility!
13
12
  end
14
13
 
15
14
  # @!macro [attach] struct_length
@@ -131,7 +130,7 @@ module Concurrent
131
130
  def self.define_struct_class(parent, base, name, members, &block)
132
131
  clazz = Class.new(base || Object) do
133
132
  include parent
134
- self.const_set(:MEMBERS, members.collect{|member| member.to_s.to_sym}.freeze)
133
+ self.const_set(:MEMBERS, members.collect{|member| member.to_s.to_sym}.freeze)
135
134
  def ns_initialize(*values)
136
135
  raise ArgumentError.new('struct size differs') if values.length > length
137
136
  @values = values.fill(nil, values.length..length-1)
@@ -142,7 +141,7 @@ module Concurrent
142
141
  parent.const_set(name, clazz)
143
142
  parent.const_get(name)
144
143
  rescue NameError
145
- raise NameError.new("identifier #{name} needs to be constant")
144
+ raise NameError.new("identifier #{name} needs to be constant")
146
145
  end
147
146
  end
148
147
  members.each_with_index do |member, index|
@@ -1,14 +1,16 @@
1
1
  module Concurrent
2
2
  module Synchronization
3
- class Condition < Object
3
+ class Condition < LockableObject
4
+ safe_initialization!
5
+
6
+ # TODO (pitr 12-Sep-2015): locks two objects, improve
4
7
 
5
8
  singleton_class.send :alias_method, :private_new, :new
6
9
  private_class_method :new
7
10
 
8
11
  def initialize(lock)
9
- @Lock = lock
10
- ensure_ivar_visibility!
11
12
  super()
13
+ @Lock = lock
12
14
  end
13
15
 
14
16
  def wait(timeout = nil)
@@ -44,7 +46,7 @@ module Concurrent
44
46
  end
45
47
  end
46
48
 
47
- class Object < Implementation
49
+ class LockableObject < LockableObjectImplementation
48
50
  def new_condition
49
51
  Condition.private_new(self)
50
52
  end
@@ -0,0 +1,13 @@
1
+ module Concurrent
2
+ module Synchronization
3
+
4
+ if Concurrent.on_jruby?
5
+
6
+ # @!visibility private
7
+ # @!macro internal_implementation_note
8
+ class JRubyLockableObject < AbstractLockableObject
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,5 +1,3 @@
1
- require 'concurrent/utility/native_extension_loader' # load native part first
2
-
3
1
  module Concurrent
4
2
  module Synchronization
5
3
 
@@ -7,7 +5,11 @@ module Concurrent
7
5
 
8
6
  # @!visibility private
9
7
  # @!macro internal_implementation_note
10
- class JavaObject < AbstractObject
8
+ class JRubyObject < AbstractObject
9
+
10
+ def initialize
11
+ # nothing to do
12
+ end
11
13
 
12
14
  def self.attr_volatile(*names)
13
15
  names.each do |name|
@@ -1,6 +1,7 @@
1
1
  module Concurrent
2
2
  module Synchronization
3
- class Lock < Object
3
+ class Lock < LockableObject
4
+ # TODO use JavaReentrantLock on JRuby
4
5
 
5
6
  public :synchronize
6
7
 
@@ -21,7 +22,7 @@ module Concurrent
21
22
  end
22
23
 
23
24
  public :ns_signal
24
-
25
+
25
26
  def broadcast
26
27
  synchronize { ns_broadcast }
27
28
  end
@@ -0,0 +1,59 @@
1
+ module Concurrent
2
+ module Synchronization
3
+
4
+ # @!visibility private
5
+ # @!macro internal_implementation_note
6
+ LockableObjectImplementation = case
7
+ when Concurrent.on_cruby? && Concurrent.ruby_version(:<=, 1, 9, 3)
8
+ MriMonitorLockableObject
9
+ when Concurrent.on_cruby? && Concurrent.ruby_version(:>, 1, 9, 3)
10
+ MriMutexLockableObject
11
+ when Concurrent.on_jruby?
12
+ JRubyLockableObject
13
+ when Concurrent.on_rbx?
14
+ RbxLockableObject
15
+ else
16
+ warn 'Possibly unsupported Ruby implementation'
17
+ MriMonitorLockableObject
18
+ end
19
+ private_constant :LockableObjectImplementation
20
+
21
+ # TODO (pitr 12-Sep-2015): make private for c-r, prohibit subclassing
22
+ class LockableObject < LockableObjectImplementation
23
+
24
+ # TODO (pitr 12-Sep-2015): we inherit too much ourselves :/
25
+ def self.allow_only_direct_descendants!
26
+ this = self
27
+ singleton_class.send :define_method, :inherited do |child|
28
+ # super child
29
+
30
+ if child.superclass != this
31
+ warn "all children of #{this} are final, subclassing is not supported use composition."
32
+ end
33
+ end
34
+ end
35
+
36
+ # @!method initialize(*args, &block)
37
+ # @!macro synchronization_object_method_initialize
38
+
39
+ # @!method synchronize
40
+ # @!macro synchronization_object_method_synchronize
41
+
42
+ # @!method initialize(*args, &block)
43
+ # @!macro synchronization_object_method_ns_initialize
44
+
45
+ # @!method wait_until(timeout = nil, &condition)
46
+ # @!macro synchronization_object_method_ns_wait_until
47
+
48
+ # @!method wait(timeout = nil)
49
+ # @!macro synchronization_object_method_ns_wait
50
+
51
+ # @!method signal
52
+ # @!macro synchronization_object_method_ns_signal
53
+
54
+ # @!method broadcast
55
+ # @!macro synchronization_object_method_ns_broadcast
56
+
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,71 @@
1
+ module Concurrent
2
+ module Synchronization
3
+
4
+ # @!visibility private
5
+ # @!macro internal_implementation_note
6
+ class MriLockableObject < AbstractLockableObject
7
+ protected
8
+
9
+ def ns_signal
10
+ @__condition__.signal
11
+ self
12
+ end
13
+
14
+ def ns_broadcast
15
+ @__condition__.broadcast
16
+ self
17
+ end
18
+ end
19
+
20
+
21
+ # @!visibility private
22
+ # @!macro internal_implementation_note
23
+ class MriMutexLockableObject < MriLockableObject
24
+ safe_initialization!
25
+
26
+ def initialize(*defaults)
27
+ super(*defaults)
28
+ @__lock__ = ::Mutex.new
29
+ @__condition__ = ::ConditionVariable.new
30
+ end
31
+
32
+ protected
33
+
34
+ def synchronize
35
+ if @__lock__.owned?
36
+ yield
37
+ else
38
+ @__lock__.synchronize { yield }
39
+ end
40
+ end
41
+
42
+ def ns_wait(timeout = nil)
43
+ @__condition__.wait @__lock__, timeout
44
+ self
45
+ end
46
+ end
47
+
48
+ # @!visibility private
49
+ # @!macro internal_implementation_note
50
+ class MriMonitorLockableObject < MriLockableObject
51
+ safe_initialization!
52
+
53
+ def initialize(*defaults)
54
+ super(*defaults)
55
+ @__lock__ = ::Monitor.new
56
+ @__condition__ = @__lock__.new_cond
57
+ end
58
+
59
+ protected
60
+
61
+ def synchronize # TODO may be a problem with lock.synchronize { lock.wait }
62
+ @__lock__.synchronize { yield }
63
+ end
64
+
65
+ def ns_wait(timeout = nil)
66
+ @__condition__.wait timeout
67
+ self
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,35 @@
1
+ module Concurrent
2
+ module Synchronization
3
+
4
+ # @!visibility private
5
+ # @!macro internal_implementation_note
6
+ class MriObject < AbstractObject
7
+
8
+ def initialize
9
+ # nothing to do
10
+ end
11
+
12
+ def full_memory_barrier
13
+ # relying on undocumented behavior of CRuby, GVL acquire has lock which ensures visibility of ivars
14
+ # https://github.com/ruby/ruby/blob/ruby_2_2/thread_pthread.c#L204-L211
15
+ end
16
+
17
+ def self.attr_volatile(*names)
18
+ names.each do |name|
19
+ ivar = :"@volatile_#{name}"
20
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
21
+ def #{name}
22
+ #{ivar}
23
+ end
24
+
25
+ def #{name}=(value)
26
+ #{ivar} = value
27
+ end
28
+ RUBY
29
+ end
30
+ names.map { |n| [n, :"#{n}="] }.flatten
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -1,77 +1,149 @@
1
- require 'concurrent/synchronization/java_object'
2
- require 'concurrent/synchronization/monitor_object'
3
- require 'concurrent/synchronization/mutex_object'
4
- require 'concurrent/synchronization/rbx_object'
5
-
6
1
  module Concurrent
7
2
  module Synchronization
8
3
 
9
4
  # @!visibility private
10
5
  # @!macro internal_implementation_note
11
- Implementation = case
12
- when Concurrent.on_jruby?
13
- JavaObject
14
- when Concurrent.on_cruby? && Concurrent.ruby_version(:<=, 1, 9, 3)
15
- MonitorObject
16
- when Concurrent.on_cruby? && Concurrent.ruby_version(:>, 1, 9, 3)
17
- MutexObject
18
- when Concurrent.on_rbx?
19
- RbxObject
20
- else
21
- warn 'Possibly unsupported Ruby implementation'
22
- MutexObject
23
- end
24
- private_constant :Implementation
6
+ ObjectImplementation = case
7
+ when Concurrent.on_cruby?
8
+ MriObject
9
+ when Concurrent.on_jruby?
10
+ JRubyObject
11
+ when Concurrent.on_rbx?
12
+ RbxObject
13
+ else
14
+ warn 'Possibly unsupported Ruby implementation'
15
+ MriObject
16
+ end
17
+ private_constant :ObjectImplementation
25
18
 
19
+ # TODO fix documentation
26
20
  # @!macro [attach] synchronization_object
27
- #
21
+ #
28
22
  # Safe synchronization under any Ruby implementation.
29
23
  # It provides methods like {#synchronize}, {#wait}, {#signal} and {#broadcast}.
30
24
  # Provides a single layer which can improve its implementation over time without changes needed to
31
25
  # the classes using it. Use {Synchronization::Object} not this abstract class.
32
- #
26
+ #
33
27
  # @note this object does not support usage together with
34
28
  # [`Thread#wakeup`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-wakeup)
35
29
  # and [`Thread#raise`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-raise).
36
30
  # `Thread#sleep` and `Thread#wakeup` will work as expected but mixing `Synchronization::Object#wait` and
37
31
  # `Thread#wakeup` will not work on all platforms.
38
- #
32
+ #
39
33
  # @see {Event} implementation as an example of this class use
40
- #
34
+ #
41
35
  # @example simple
42
36
  # class AnClass < Synchronization::Object
43
37
  # def initialize
44
38
  # super
45
39
  # synchronize { @value = 'asd' }
46
40
  # end
47
- #
41
+ #
48
42
  # def value
49
43
  # synchronize { @value }
50
44
  # end
51
45
  # end
52
46
  #
53
- class Object < Implementation
47
+ class Object < ObjectImplementation
48
+
49
+ # Has to be called by children.
50
+ # Initializes default volatile fields with cas if any.
51
+ # @param [Array<Object>] defaults values for fields, in same order as they are defined
52
+ def initialize(*defaults)
53
+ super()
54
+ initialize_volatile_cas_fields(defaults)
55
+ end
56
+
57
+ # By calling this method on a class, it and all its children are marked to be constructed safely. Meaning that
58
+ # all writes (ivar initializations) are made visible to all readers of newly constructed object. It ensures
59
+ # same behaviour as Java's final fields.
60
+ # @example
61
+ # class AClass < Concurrent::Synchronization::Object
62
+ # safe_initialization!
63
+ #
64
+ # def initialize
65
+ # @AFinalValue = 'value' # published safly, does not have to be synchronized
66
+ # end
67
+ # end
68
+ def self.safe_initialization!
69
+ # define only once, and not again in children
70
+ return if safe_initialization?
71
+
72
+ def self.new(*)
73
+ object = super
74
+ ensure
75
+ object.ensure_ivar_visibility! if object
76
+ end
77
+
78
+ @safe_initialization = true
79
+ end
80
+
81
+ def self.safe_initialization?
82
+ @safe_initialization || (superclass.respond_to?(:safe_initialization?) && superclass.safe_initialization?)
83
+ end
84
+
85
+ # For testing purposes, quite slow.
86
+ def self.ensure_safe_initialization_when_final_fields_are_present
87
+ Object.class_eval do
88
+ def self.new(*)
89
+ object = super
90
+ ensure
91
+ has_final_field = object.instance_variables.any? { |v| v.to_s =~ /^@[A-Z]/ }
92
+ if has_final_field && !safe_initialization?
93
+ raise "there was an instance of #{object.class} with final field but not marked with safe_initialization!"
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ # TODO documentation
100
+ def self.attr_volatile_with_cas(*names)
101
+ @volatile_cas_fields ||= []
102
+ @volatile_cas_fields += names
103
+ safe_initialization!
104
+
105
+ names.each do |name|
106
+ ivar = :"@VolatileCas_#{name}"
107
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
108
+ def #{name}
109
+ #{ivar}.get
110
+ end
54
111
 
55
- # @!method initialize(*args, &block)
56
- # @!macro synchronization_object_method_initialize
112
+ def #{name}=(value)
113
+ #{ivar}.set value
114
+ end
57
115
 
58
- # @!method synchronize
59
- # @!macro synchronization_object_method_synchronize
116
+ def swap_#{name}(value)
117
+ #{ivar}.swap value
118
+ end
60
119
 
61
- # @!method initialize(*args, &block)
62
- # @!macro synchronization_object_method_ns_initialize
120
+ def compare_and_set_#{name}(expected, value)
121
+ #{ivar}.compare_and_set expected, value
122
+ end
63
123
 
64
- # @!method wait_until(timeout = nil, &condition)
65
- # @!macro synchronization_object_method_ns_wait_until
124
+ def update_#{name}(&block)
125
+ #{ivar}.update &block
126
+ end
127
+ RUBY
128
+ end
129
+ names.map { |n| [n, :"#{n}=", :"swap_#{n}", :"compare_and_set_#{n}"] }.flatten
130
+ end
66
131
 
67
- # @!method wait(timeout = nil)
68
- # @!macro synchronization_object_method_ns_wait
132
+ def self.volatile_cas_fields(inherited = true)
133
+ # TODO (pitr 11-Sep-2015): maybe use constant for better optimisation on Truffle since it will not speculate on ivar being final
134
+ @volatile_cas_fields ||= []
135
+ ((superclass.volatile_cas_fields if superclass.respond_to?(:volatile_cas_fields) && inherited) || []) +
136
+ @volatile_cas_fields
137
+ end
69
138
 
70
- # @!method signal
71
- # @!macro synchronization_object_method_ns_signal
139
+ private
72
140
 
73
- # @!method broadcast
74
- # @!macro synchronization_object_method_ns_broadcast
141
+ def initialize_volatile_cas_fields(defaults)
142
+ self.class.volatile_cas_fields.zip(defaults) do |name, default|
143
+ instance_variable_set :"@VolatileCas_#{name}", AtomicReference.new(default)
144
+ end
145
+ nil
146
+ end
75
147
 
76
148
  # @!method ensure_ivar_visibility!
77
149
  # @!macro synchronization_object_method_ensure_ivar_visibility