concurrent-ruby 0.9.2-java → 1.0.0-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 +49 -1
- data/README.md +86 -120
- data/lib/concurrent.rb +14 -5
- data/lib/concurrent/agent.rb +587 -0
- data/lib/concurrent/array.rb +39 -0
- data/lib/concurrent/async.rb +296 -149
- data/lib/concurrent/atom.rb +135 -45
- data/lib/concurrent/atomic/abstract_thread_local_var.rb +38 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +83 -118
- data/lib/concurrent/atomic/atomic_fixnum.rb +101 -163
- data/lib/concurrent/atomic/atomic_reference.rb +1 -8
- data/lib/concurrent/atomic/count_down_latch.rb +62 -103
- data/lib/concurrent/atomic/cyclic_barrier.rb +3 -1
- data/lib/concurrent/atomic/event.rb +1 -1
- data/lib/concurrent/atomic/java_count_down_latch.rb +39 -0
- data/lib/concurrent/atomic/java_thread_local_var.rb +50 -0
- data/lib/concurrent/atomic/mutex_atomic_boolean.rb +60 -0
- data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +91 -0
- data/lib/concurrent/atomic/mutex_count_down_latch.rb +43 -0
- data/lib/concurrent/atomic/mutex_semaphore.rb +115 -0
- 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/ruby_thread_local_var.rb +172 -0
- data/lib/concurrent/atomic/semaphore.rb +84 -178
- data/lib/concurrent/atomic/thread_local_var.rb +65 -294
- data/lib/concurrent/atomic_reference/jruby+truffle.rb +1 -0
- data/lib/concurrent/atomic_reference/jruby.rb +1 -1
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +14 -8
- data/lib/concurrent/atomic_reference/ruby.rb +1 -1
- data/lib/concurrent/atomics.rb +7 -37
- 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/java_non_concurrent_priority_queue.rb +84 -0
- data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
- data/lib/concurrent/collection/map/mri_map_backend.rb +66 -0
- data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +144 -0
- data/lib/concurrent/collection/map/synchronized_map_backend.rb +86 -0
- data/lib/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
- data/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb +150 -0
- data/lib/concurrent/concern/dereferenceable.rb +9 -24
- data/lib/concurrent/concern/logging.rb +1 -1
- data/lib/concurrent/concern/obligation.rb +11 -20
- data/lib/concurrent/concern/observable.rb +38 -13
- data/lib/concurrent/configuration.rb +23 -152
- data/lib/concurrent/constants.rb +8 -0
- data/lib/concurrent/delay.rb +14 -12
- data/lib/concurrent/exchanger.rb +339 -41
- data/lib/concurrent/executor/abstract_executor_service.rb +134 -0
- data/lib/concurrent/executor/executor_service.rb +23 -359
- data/lib/concurrent/executor/immediate_executor.rb +3 -2
- data/lib/concurrent/executor/java_executor_service.rb +100 -0
- data/lib/concurrent/executor/java_single_thread_executor.rb +3 -3
- data/lib/concurrent/executor/java_thread_pool_executor.rb +3 -4
- data/lib/concurrent/executor/ruby_executor_service.rb +78 -0
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +10 -66
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +25 -22
- data/lib/concurrent/executor/safe_task_executor.rb +6 -7
- data/lib/concurrent/executor/serial_executor_service.rb +34 -0
- data/lib/concurrent/executor/serialized_execution.rb +10 -33
- data/lib/concurrent/executor/serialized_execution_delegator.rb +28 -0
- data/lib/concurrent/executor/simple_executor_service.rb +1 -10
- data/lib/concurrent/executor/single_thread_executor.rb +20 -10
- data/lib/concurrent/executor/timer_set.rb +8 -10
- data/lib/concurrent/executors.rb +12 -2
- data/lib/concurrent/future.rb +6 -4
- data/lib/concurrent/hash.rb +35 -0
- data/lib/concurrent/immutable_struct.rb +5 -1
- data/lib/concurrent/ivar.rb +12 -16
- data/lib/concurrent/lazy_register.rb +11 -8
- data/lib/concurrent/map.rb +180 -0
- data/lib/concurrent/maybe.rb +6 -3
- data/lib/concurrent/mutable_struct.rb +7 -6
- data/lib/concurrent/mvar.rb +26 -2
- data/lib/concurrent/{executor/executor.rb → options.rb} +5 -29
- data/lib/concurrent/promise.rb +7 -5
- data/lib/concurrent/scheduled_task.rb +13 -71
- data/lib/concurrent/settable_struct.rb +5 -4
- data/lib/concurrent/synchronization.rb +15 -3
- data/lib/concurrent/synchronization/abstract_lockable_object.rb +98 -0
- data/lib/concurrent/synchronization/abstract_object.rb +7 -146
- 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/jruby_object.rb +44 -0
- data/lib/concurrent/synchronization/lock.rb +3 -2
- data/lib/concurrent/synchronization/lockable_object.rb +72 -0
- data/lib/concurrent/synchronization/mri_lockable_object.rb +71 -0
- data/lib/concurrent/synchronization/mri_object.rb +43 -0
- data/lib/concurrent/synchronization/object.rb +140 -73
- data/lib/concurrent/synchronization/rbx_lockable_object.rb +65 -0
- data/lib/concurrent/synchronization/rbx_object.rb +30 -73
- data/lib/concurrent/synchronization/volatile.rb +34 -0
- data/lib/concurrent/thread_safe/synchronized_delegator.rb +50 -0
- data/lib/concurrent/thread_safe/util.rb +14 -0
- data/lib/concurrent/thread_safe/util/adder.rb +74 -0
- data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +30 -0
- data/lib/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
- data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
- data/lib/concurrent/thread_safe/util/striped64.rb +241 -0
- data/lib/concurrent/thread_safe/util/volatile.rb +75 -0
- data/lib/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
- data/lib/concurrent/timer_task.rb +3 -4
- data/lib/concurrent/tuple.rb +86 -0
- data/lib/concurrent/tvar.rb +5 -1
- data/lib/concurrent/utility/at_exit.rb +1 -1
- data/lib/concurrent/utility/engine.rb +4 -0
- data/lib/concurrent/utility/monotonic_time.rb +3 -4
- data/lib/concurrent/utility/native_extension_loader.rb +50 -30
- data/lib/concurrent/version.rb +2 -2
- data/lib/concurrent_ruby_ext.jar +0 -0
- metadata +47 -12
- data/lib/concurrent/atomic/condition.rb +0 -78
- data/lib/concurrent/collection/priority_queue.rb +0 -360
- data/lib/concurrent/synchronization/java_object.rb +0 -34
- data/lib/concurrent/synchronization/monitor_object.rb +0 -27
- data/lib/concurrent/synchronization/mutex_object.rb +0 -43
- data/lib/concurrent/utilities.rb +0 -5
- data/lib/concurrent/utility/timeout.rb +0 -39
- data/lib/concurrent/utility/timer.rb +0 -26
- data/lib/concurrent_ruby.rb +0 -2
@@ -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
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
if Concurrent.on_jruby? && Concurrent.java_extensions_loaded?
|
5
|
+
|
6
|
+
# @!visibility private
|
7
|
+
# @!macro internal_implementation_note
|
8
|
+
class JRubyLockableObject < AbstractLockableObject
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
if Concurrent.on_jruby? && Concurrent.java_extensions_loaded?
|
5
|
+
|
6
|
+
module JRubyAttrVolatile
|
7
|
+
def self.included(base)
|
8
|
+
base.extend(ClassMethods)
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def attr_volatile(*names)
|
13
|
+
names.each do |name|
|
14
|
+
|
15
|
+
ivar = :"@volatile_#{name}"
|
16
|
+
|
17
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
18
|
+
def #{name}
|
19
|
+
instance_variable_get_volatile(:#{ivar})
|
20
|
+
end
|
21
|
+
|
22
|
+
def #{name}=(value)
|
23
|
+
instance_variable_set_volatile(:#{ivar}, value)
|
24
|
+
end
|
25
|
+
RUBY
|
26
|
+
|
27
|
+
end
|
28
|
+
names.map { |n| [n, :"#{n}="] }.flatten
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
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
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -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,72 @@
|
|
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? || Concurrent.on_truffle?
|
14
|
+
RbxLockableObject
|
15
|
+
else
|
16
|
+
warn 'Possibly unsupported Ruby implementation'
|
17
|
+
MriMonitorLockableObject
|
18
|
+
end
|
19
|
+
private_constant :LockableObjectImplementation
|
20
|
+
|
21
|
+
# Safe synchronization under any Ruby implementation.
|
22
|
+
# It provides methods like {#synchronize}, {#wait}, {#signal} and {#broadcast}.
|
23
|
+
# Provides a single layer which can improve its implementation over time without changes needed to
|
24
|
+
# the classes using it. Use {Synchronization::Object} not this abstract class.
|
25
|
+
#
|
26
|
+
# @note this object does not support usage together with
|
27
|
+
# [`Thread#wakeup`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-wakeup)
|
28
|
+
# and [`Thread#raise`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-raise).
|
29
|
+
# `Thread#sleep` and `Thread#wakeup` will work as expected but mixing `Synchronization::Object#wait` and
|
30
|
+
# `Thread#wakeup` will not work on all platforms.
|
31
|
+
#
|
32
|
+
# @see {Event} implementation as an example of this class use
|
33
|
+
#
|
34
|
+
# @example simple
|
35
|
+
# class AnClass < Synchronization::Object
|
36
|
+
# def initialize
|
37
|
+
# super
|
38
|
+
# synchronize { @value = 'asd' }
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# def value
|
42
|
+
# synchronize { @value }
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# @!visibility private
|
47
|
+
class LockableObject < LockableObjectImplementation
|
48
|
+
|
49
|
+
# TODO (pitr 12-Sep-2015): make private for c-r, prohibit subclassing
|
50
|
+
# TODO (pitr 12-Sep-2015): we inherit too much ourselves :/
|
51
|
+
|
52
|
+
# @!method initialize(*args, &block)
|
53
|
+
# @!macro synchronization_object_method_initialize
|
54
|
+
|
55
|
+
# @!method synchronize
|
56
|
+
# @!macro synchronization_object_method_synchronize
|
57
|
+
|
58
|
+
# @!method wait_until(timeout = nil, &condition)
|
59
|
+
# @!macro synchronization_object_method_ns_wait_until
|
60
|
+
|
61
|
+
# @!method wait(timeout = nil)
|
62
|
+
# @!macro synchronization_object_method_ns_wait
|
63
|
+
|
64
|
+
# @!method signal
|
65
|
+
# @!macro synchronization_object_method_ns_signal
|
66
|
+
|
67
|
+
# @!method broadcast
|
68
|
+
# @!macro synchronization_object_method_ns_broadcast
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
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,43 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
module MriAttrVolatile
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def attr_volatile(*names)
|
11
|
+
names.each do |name|
|
12
|
+
ivar = :"@volatile_#{name}"
|
13
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
14
|
+
def #{name}
|
15
|
+
#{ivar}
|
16
|
+
end
|
17
|
+
|
18
|
+
def #{name}=(value)
|
19
|
+
#{ivar} = value
|
20
|
+
end
|
21
|
+
RUBY
|
22
|
+
end
|
23
|
+
names.map { |n| [n, :"#{n}="] }.flatten
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def full_memory_barrier
|
28
|
+
# relying on undocumented behavior of CRuby, GVL acquire has lock which ensures visibility of ivars
|
29
|
+
# https://github.com/ruby/ruby/blob/ruby_2_2/thread_pthread.c#L204-L211
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# @!visibility private
|
34
|
+
# @!macro internal_implementation_note
|
35
|
+
class MriObject < AbstractObject
|
36
|
+
include MriAttrVolatile
|
37
|
+
|
38
|
+
def initialize
|
39
|
+
# nothing to do
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -1,83 +1,150 @@
|
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
#
|
27
|
-
|
28
|
-
# Safe synchronization under any Ruby implementation.
|
29
|
-
# It provides methods like {#synchronize}, {#wait}, {#signal} and {#broadcast}.
|
30
|
-
# Provides a single layer which can improve its implementation over time without changes needed to
|
31
|
-
# the classes using it. Use {Synchronization::Object} not this abstract class.
|
32
|
-
#
|
33
|
-
# @note this object does not support usage together with
|
34
|
-
# [`Thread#wakeup`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-wakeup)
|
35
|
-
# and [`Thread#raise`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-raise).
|
36
|
-
# `Thread#sleep` and `Thread#wakeup` will work as expected but mixing `Synchronization::Object#wait` and
|
37
|
-
# `Thread#wakeup` will not work on all platforms.
|
38
|
-
#
|
39
|
-
# @see {Event} implementation as an example of this class use
|
40
|
-
#
|
41
|
-
# @example simple
|
42
|
-
# class AnClass < Synchronization::Object
|
43
|
-
# def initialize
|
44
|
-
# super
|
45
|
-
# synchronize { @value = 'asd' }
|
46
|
-
# end
|
47
|
-
#
|
48
|
-
# def value
|
49
|
-
# synchronize { @value }
|
50
|
-
# end
|
51
|
-
# end
|
52
|
-
#
|
53
|
-
class Object < Implementation
|
54
|
-
|
55
|
-
# @!method initialize(*args, &block)
|
56
|
-
# @!macro synchronization_object_method_initialize
|
57
|
-
|
58
|
-
# @!method synchronize
|
59
|
-
# @!macro synchronization_object_method_synchronize
|
60
|
-
|
61
|
-
# @!method initialize(*args, &block)
|
62
|
-
# @!macro synchronization_object_method_ns_initialize
|
63
|
-
|
64
|
-
# @!method wait_until(timeout = nil, &condition)
|
65
|
-
# @!macro synchronization_object_method_ns_wait_until
|
66
|
-
|
67
|
-
# @!method wait(timeout = nil)
|
68
|
-
# @!macro synchronization_object_method_ns_wait
|
69
|
-
|
70
|
-
# @!method signal
|
71
|
-
# @!macro synchronization_object_method_ns_signal
|
72
|
-
|
73
|
-
# @!method broadcast
|
74
|
-
# @!macro synchronization_object_method_ns_broadcast
|
75
|
-
|
76
|
-
# @!method ensure_ivar_visibility!
|
77
|
-
# @!macro synchronization_object_method_ensure_ivar_visibility
|
6
|
+
ObjectImplementation = case
|
7
|
+
when Concurrent.on_cruby?
|
8
|
+
MriObject
|
9
|
+
when Concurrent.on_jruby?
|
10
|
+
JRubyObject
|
11
|
+
when Concurrent.on_rbx? || Concurrent.on_truffle?
|
12
|
+
RbxObject
|
13
|
+
else
|
14
|
+
MriObject
|
15
|
+
end
|
16
|
+
private_constant :ObjectImplementation
|
17
|
+
|
18
|
+
# Abstract object providing final, volatile, ans CAS extensions to build other concurrent abstractions.
|
19
|
+
# - final instance variables see {Object.safe_initialization!}
|
20
|
+
# - volatile instance variables see {Object.attr_volatile}
|
21
|
+
# - volatile instance variables see {Object.attr_atomic}
|
22
|
+
class Object < ObjectImplementation
|
78
23
|
|
79
24
|
# @!method self.attr_volatile(*names)
|
80
|
-
#
|
25
|
+
# Creates methods for reading and writing (as `attr_accessor` does) to a instance variable with
|
26
|
+
# volatile (Java) semantic. The instance variable should be accessed oly through generated methods.
|
27
|
+
#
|
28
|
+
# @param [Array<Symbol>] names of the instance variables to be volatile
|
29
|
+
# @return [Array<Symbol>] names of defined method names
|
30
|
+
|
31
|
+
# Has to be called by children.
|
32
|
+
def initialize
|
33
|
+
super
|
34
|
+
initialize_volatile_with_cas
|
35
|
+
end
|
36
|
+
|
37
|
+
# By calling this method on a class, it and all its children are marked to be constructed safely. Meaning that
|
38
|
+
# all writes (ivar initializations) are made visible to all readers of newly constructed object. It ensures
|
39
|
+
# same behaviour as Java's final fields.
|
40
|
+
# @example
|
41
|
+
# class AClass < Concurrent::Synchronization::Object
|
42
|
+
# safe_initialization!
|
43
|
+
#
|
44
|
+
# def initialize
|
45
|
+
# @AFinalValue = 'value' # published safely, does not have to be synchronized
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
def self.safe_initialization!
|
49
|
+
# define only once, and not again in children
|
50
|
+
return if safe_initialization?
|
51
|
+
|
52
|
+
def self.new(*)
|
53
|
+
object = super
|
54
|
+
ensure
|
55
|
+
object.full_memory_barrier if object
|
56
|
+
end
|
57
|
+
|
58
|
+
@safe_initialization = true
|
59
|
+
end
|
60
|
+
|
61
|
+
# @return [true, false] if this class is safely initialized.
|
62
|
+
def self.safe_initialization?
|
63
|
+
@safe_initialization = false unless defined? @safe_initialization
|
64
|
+
@safe_initialization || (superclass.respond_to?(:safe_initialization?) && superclass.safe_initialization?)
|
65
|
+
end
|
66
|
+
|
67
|
+
# For testing purposes, quite slow. Injects assert code to new method which will raise if class instance contains
|
68
|
+
# any instance variables with CamelCase names and isn't {.safe_initialization?}.
|
69
|
+
def self.ensure_safe_initialization_when_final_fields_are_present
|
70
|
+
Object.class_eval do
|
71
|
+
def self.new(*)
|
72
|
+
object = super
|
73
|
+
ensure
|
74
|
+
has_final_field = object.instance_variables.any? { |v| v.to_s =~ /^@[A-Z]/ }
|
75
|
+
if has_final_field && !safe_initialization?
|
76
|
+
raise "there was an instance of #{object.class} with final field but not marked with safe_initialization!"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Creates methods for reading and writing to a instance variable with
|
83
|
+
# volatile (Java) semantic as {.attr_volatile} does.
|
84
|
+
# The instance variable should be accessed oly through generated methods.
|
85
|
+
# This method generates following methods: `value`, `value=(new_value) #=> new_value`,
|
86
|
+
# `swap_value(new_value) #=> old_value`,
|
87
|
+
# `compare_and_set_value(expected, value) #=> true || false`, `update_value(&block)`.
|
88
|
+
# @param [Array<Symbol>] names of the instance variables to be volatile with CAS.
|
89
|
+
# @return [Array<Symbol>] names of defined method names.
|
90
|
+
def self.attr_atomic(*names)
|
91
|
+
@volatile_cas_fields ||= []
|
92
|
+
@volatile_cas_fields += names
|
93
|
+
safe_initialization!
|
94
|
+
define_initialize_volatile_with_cas
|
95
|
+
|
96
|
+
names.each do |name|
|
97
|
+
ivar = :"@Atomic#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }}"
|
98
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
99
|
+
def #{name}
|
100
|
+
#{ivar}.get
|
101
|
+
end
|
102
|
+
|
103
|
+
def #{name}=(value)
|
104
|
+
#{ivar}.set value
|
105
|
+
end
|
106
|
+
|
107
|
+
def swap_#{name}(value)
|
108
|
+
#{ivar}.swap value
|
109
|
+
end
|
110
|
+
|
111
|
+
def compare_and_set_#{name}(expected, value)
|
112
|
+
#{ivar}.compare_and_set expected, value
|
113
|
+
end
|
114
|
+
|
115
|
+
def update_#{name}(&block)
|
116
|
+
#{ivar}.update(&block)
|
117
|
+
end
|
118
|
+
RUBY
|
119
|
+
end
|
120
|
+
names.flat_map { |n| [n, :"#{n}=", :"swap_#{n}", :"compare_and_set_#{n}", :"update_#{n}"] }
|
121
|
+
end
|
122
|
+
|
123
|
+
# @param [true,false] inherited should inherited volatile with CAS fields be returned?
|
124
|
+
# @return [Array<Symbol>] Returns defined volatile with CAS fields on this class.
|
125
|
+
def self.volatile_cas_fields(inherited = true)
|
126
|
+
@volatile_cas_fields ||= []
|
127
|
+
((superclass.volatile_cas_fields if superclass.respond_to?(:volatile_cas_fields) && inherited) || []) +
|
128
|
+
@volatile_cas_fields
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
def self.define_initialize_volatile_with_cas
|
134
|
+
assignments = @volatile_cas_fields.map { |name| "@Atomic#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }} = AtomicReference.new(nil)" }.join("\n")
|
135
|
+
class_eval <<-RUBY
|
136
|
+
def initialize_volatile_with_cas
|
137
|
+
super
|
138
|
+
#{assignments}
|
139
|
+
end
|
140
|
+
RUBY
|
141
|
+
end
|
142
|
+
|
143
|
+
private_class_method :define_initialize_volatile_with_cas
|
144
|
+
|
145
|
+
def initialize_volatile_with_cas
|
146
|
+
end
|
147
|
+
|
81
148
|
end
|
82
149
|
end
|
83
150
|
end
|