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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -1
- data/README.md +16 -18
- data/lib/concurrent.rb +3 -3
- data/lib/concurrent/agent.rb +583 -0
- data/lib/concurrent/array.rb +1 -0
- data/lib/concurrent/async.rb +236 -111
- data/lib/concurrent/atom.rb +101 -46
- data/lib/concurrent/atomic/atomic_boolean.rb +2 -0
- data/lib/concurrent/atomic/atomic_fixnum.rb +2 -0
- data/lib/concurrent/atomic/cyclic_barrier.rb +1 -1
- data/lib/concurrent/atomic/event.rb +1 -1
- data/lib/concurrent/atomic/mutex_atomic_boolean.rb +1 -1
- data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +1 -1
- data/lib/concurrent/atomic/mutex_count_down_latch.rb +1 -1
- data/lib/concurrent/atomic/mutex_semaphore.rb +2 -2
- data/lib/concurrent/atomic/read_write_lock.rb +5 -4
- data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
- data/lib/concurrent/atomic/thread_local_var.rb +2 -0
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +1 -1
- data/lib/concurrent/atomics.rb +6 -4
- data/lib/concurrent/collection/copy_on_notify_observer_set.rb +7 -15
- data/lib/concurrent/collection/copy_on_write_observer_set.rb +7 -15
- data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +5 -0
- data/lib/concurrent/concern/observable.rb +38 -13
- data/lib/concurrent/configuration.rb +5 -4
- data/lib/concurrent/delay.rb +9 -8
- data/lib/concurrent/exchanger.rb +2 -0
- data/lib/concurrent/executor/abstract_executor_service.rb +2 -2
- data/lib/concurrent/executor/java_single_thread_executor.rb +0 -1
- data/lib/concurrent/executor/ruby_executor_service.rb +10 -4
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +10 -68
- data/lib/concurrent/executor/safe_task_executor.rb +7 -8
- data/lib/concurrent/executor/serialized_execution.rb +4 -4
- data/lib/concurrent/executor/single_thread_executor.rb +20 -10
- data/lib/concurrent/executor/timer_set.rb +4 -2
- data/lib/concurrent/executors.rb +0 -1
- data/lib/concurrent/future.rb +3 -2
- data/lib/concurrent/hash.rb +1 -1
- data/lib/concurrent/immutable_struct.rb +5 -1
- data/lib/concurrent/ivar.rb +1 -1
- data/lib/concurrent/mutable_struct.rb +7 -6
- data/lib/concurrent/{executor/executor.rb → options.rb} +4 -3
- data/lib/concurrent/promise.rb +3 -2
- data/lib/concurrent/scheduled_task.rb +3 -2
- data/lib/concurrent/settable_struct.rb +5 -4
- data/lib/concurrent/synchronization.rb +11 -3
- data/lib/concurrent/synchronization/abstract_lockable_object.rb +117 -0
- data/lib/concurrent/synchronization/abstract_object.rb +16 -129
- data/lib/concurrent/synchronization/abstract_struct.rb +2 -3
- data/lib/concurrent/synchronization/condition.rb +6 -4
- data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
- data/lib/concurrent/synchronization/{java_object.rb → jruby_object.rb} +5 -3
- data/lib/concurrent/synchronization/lock.rb +3 -2
- data/lib/concurrent/synchronization/lockable_object.rb +59 -0
- data/lib/concurrent/synchronization/mri_lockable_object.rb +71 -0
- data/lib/concurrent/synchronization/mri_object.rb +35 -0
- data/lib/concurrent/synchronization/object.rb +111 -39
- data/lib/concurrent/synchronization/rbx_lockable_object.rb +64 -0
- data/lib/concurrent/synchronization/rbx_object.rb +17 -68
- data/lib/concurrent/thread_safe/util.rb +0 -9
- data/lib/concurrent/thread_safe/util/adder.rb +3 -0
- data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +3 -1
- data/lib/concurrent/thread_safe/util/cheap_lockable.rb +3 -0
- data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +1 -0
- data/lib/concurrent/thread_safe/util/striped64.rb +6 -1
- data/lib/concurrent/thread_safe/util/volatile.rb +2 -0
- data/lib/concurrent/thread_safe/util/xor_shift_random.rb +2 -0
- data/lib/concurrent/tvar.rb +36 -0
- data/lib/concurrent/utility/at_exit.rb +1 -1
- data/lib/concurrent/utility/monotonic_time.rb +3 -4
- data/lib/concurrent/utility/native_extension_loader.rb +1 -1
- data/lib/concurrent/version.rb +2 -2
- data/lib/concurrent_ruby_ext.jar +0 -0
- metadata +11 -6
- data/lib/concurrent/synchronization/monitor_object.rb +0 -27
- 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 <
|
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
|
49
|
+
class LockableObject < LockableObjectImplementation
|
48
50
|
def new_condition
|
49
51
|
Condition.private_new(self)
|
50
52
|
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
|
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 <
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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 <
|
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
|
-
|
56
|
-
|
112
|
+
def #{name}=(value)
|
113
|
+
#{ivar}.set value
|
114
|
+
end
|
57
115
|
|
58
|
-
|
59
|
-
|
116
|
+
def swap_#{name}(value)
|
117
|
+
#{ivar}.swap value
|
118
|
+
end
|
60
119
|
|
61
|
-
|
62
|
-
|
120
|
+
def compare_and_set_#{name}(expected, value)
|
121
|
+
#{ivar}.compare_and_set expected, value
|
122
|
+
end
|
63
123
|
|
64
|
-
|
65
|
-
|
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
|
-
|
68
|
-
|
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
|
-
|
71
|
-
# @!macro synchronization_object_method_ns_signal
|
139
|
+
private
|
72
140
|
|
73
|
-
|
74
|
-
|
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
|