concurrent-ruby 1.0.0.pre4 → 1.0.0.pre5
Sign up to get free protection for your applications and to get access to all the features.
- 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
- metadata +4 -2
@@ -8,9 +8,9 @@ module Concurrent
|
|
8
8
|
MriMonitorLockableObject
|
9
9
|
when Concurrent.on_cruby? && Concurrent.ruby_version(:>, 1, 9, 3)
|
10
10
|
MriMutexLockableObject
|
11
|
-
when
|
11
|
+
when Concurrent.on_jruby?
|
12
12
|
JRubyLockableObject
|
13
|
-
when Concurrent.on_rbx?
|
13
|
+
when Concurrent.on_rbx? || Concurrent.on_truffle?
|
14
14
|
RbxLockableObject
|
15
15
|
else
|
16
16
|
warn 'Possibly unsupported Ruby implementation'
|
@@ -18,20 +18,36 @@ module Concurrent
|
|
18
18
|
end
|
19
19
|
private_constant :LockableObjectImplementation
|
20
20
|
|
21
|
-
#
|
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
|
22
47
|
class LockableObject < LockableObjectImplementation
|
23
48
|
|
49
|
+
# TODO (pitr 12-Sep-2015): make private for c-r, prohibit subclassing
|
24
50
|
# 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
51
|
|
36
52
|
# @!method initialize(*args, &block)
|
37
53
|
# @!macro synchronization_object_method_initialize
|
@@ -39,9 +55,6 @@ module Concurrent
|
|
39
55
|
# @!method synchronize
|
40
56
|
# @!macro synchronization_object_method_synchronize
|
41
57
|
|
42
|
-
# @!method initialize(*args, &block)
|
43
|
-
# @!macro synchronization_object_method_ns_initialize
|
44
|
-
|
45
58
|
# @!method wait_until(timeout = nil, &condition)
|
46
59
|
# @!macro synchronization_object_method_ns_wait_until
|
47
60
|
|
@@ -1,35 +1,43 @@
|
|
1
1
|
module Concurrent
|
2
2
|
module Synchronization
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
module MriAttrVolatile
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
7
8
|
|
8
|
-
|
9
|
-
|
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
|
10
25
|
end
|
11
26
|
|
12
27
|
def full_memory_barrier
|
13
28
|
# relying on undocumented behavior of CRuby, GVL acquire has lock which ensures visibility of ivars
|
14
29
|
# https://github.com/ruby/ruby/blob/ruby_2_2/thread_pthread.c#L204-L211
|
15
30
|
end
|
31
|
+
end
|
16
32
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
def #{name}
|
22
|
-
#{ivar}
|
23
|
-
end
|
33
|
+
# @!visibility private
|
34
|
+
# @!macro internal_implementation_note
|
35
|
+
class MriObject < AbstractObject
|
36
|
+
include MriAttrVolatile
|
24
37
|
|
25
|
-
|
26
|
-
|
27
|
-
end
|
28
|
-
RUBY
|
29
|
-
end
|
30
|
-
names.map { |n| [n, :"#{n}="] }.flatten
|
38
|
+
def initialize
|
39
|
+
# nothing to do
|
31
40
|
end
|
32
41
|
end
|
33
|
-
|
34
42
|
end
|
35
43
|
end
|
@@ -6,52 +6,32 @@ module Concurrent
|
|
6
6
|
ObjectImplementation = case
|
7
7
|
when Concurrent.on_cruby?
|
8
8
|
MriObject
|
9
|
-
when
|
9
|
+
when Concurrent.on_jruby?
|
10
10
|
JRubyObject
|
11
|
-
when Concurrent.on_rbx?
|
11
|
+
when Concurrent.on_rbx? || Concurrent.on_truffle?
|
12
12
|
RbxObject
|
13
13
|
else
|
14
|
-
warn 'Possibly unsupported Ruby implementation'
|
15
14
|
MriObject
|
16
15
|
end
|
17
16
|
private_constant :ObjectImplementation
|
18
17
|
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
# It provides methods like {#synchronize}, {#wait}, {#signal} and {#broadcast}.
|
24
|
-
# Provides a single layer which can improve its implementation over time without changes needed to
|
25
|
-
# the classes using it. Use {Synchronization::Object} not this abstract class.
|
26
|
-
#
|
27
|
-
# @note this object does not support usage together with
|
28
|
-
# [`Thread#wakeup`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-wakeup)
|
29
|
-
# and [`Thread#raise`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-raise).
|
30
|
-
# `Thread#sleep` and `Thread#wakeup` will work as expected but mixing `Synchronization::Object#wait` and
|
31
|
-
# `Thread#wakeup` will not work on all platforms.
|
32
|
-
#
|
33
|
-
# @see {Event} implementation as an example of this class use
|
34
|
-
#
|
35
|
-
# @example simple
|
36
|
-
# class AnClass < Synchronization::Object
|
37
|
-
# def initialize
|
38
|
-
# super
|
39
|
-
# synchronize { @value = 'asd' }
|
40
|
-
# end
|
41
|
-
#
|
42
|
-
# def value
|
43
|
-
# synchronize { @value }
|
44
|
-
# end
|
45
|
-
# end
|
46
|
-
#
|
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_volatile_with_cas}
|
47
22
|
class Object < ObjectImplementation
|
48
23
|
|
24
|
+
# @!method self.attr_volatile(*names)
|
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
|
+
|
49
31
|
# Has to be called by children.
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
super()
|
54
|
-
initialize_volatile_cas_fields(defaults)
|
32
|
+
def initialize
|
33
|
+
super
|
34
|
+
initialize_volatile_with_cas
|
55
35
|
end
|
56
36
|
|
57
37
|
# By calling this method on a class, it and all its children are marked to be constructed safely. Meaning that
|
@@ -62,7 +42,7 @@ module Concurrent
|
|
62
42
|
# safe_initialization!
|
63
43
|
#
|
64
44
|
# def initialize
|
65
|
-
# @AFinalValue = 'value' # published
|
45
|
+
# @AFinalValue = 'value' # published safely, does not have to be synchronized
|
66
46
|
# end
|
67
47
|
# end
|
68
48
|
def self.safe_initialization!
|
@@ -72,17 +52,20 @@ module Concurrent
|
|
72
52
|
def self.new(*)
|
73
53
|
object = super
|
74
54
|
ensure
|
75
|
-
object.
|
55
|
+
object.full_memory_barrier if object
|
76
56
|
end
|
77
57
|
|
78
58
|
@safe_initialization = true
|
79
59
|
end
|
80
60
|
|
61
|
+
# @return [true, false] if this class is safely initialized.
|
81
62
|
def self.safe_initialization?
|
82
|
-
|
63
|
+
@safe_initialization = false unless defined? @safe_initialization
|
64
|
+
@safe_initialization || (superclass.respond_to?(:safe_initialization?) && superclass.safe_initialization?)
|
83
65
|
end
|
84
66
|
|
85
|
-
# For testing purposes, quite slow.
|
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?}.
|
86
69
|
def self.ensure_safe_initialization_when_final_fields_are_present
|
87
70
|
Object.class_eval do
|
88
71
|
def self.new(*)
|
@@ -96,14 +79,22 @@ module Concurrent
|
|
96
79
|
end
|
97
80
|
end
|
98
81
|
|
99
|
-
#
|
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.
|
100
90
|
def self.attr_volatile_with_cas(*names)
|
101
91
|
@volatile_cas_fields ||= []
|
102
92
|
@volatile_cas_fields += names
|
103
93
|
safe_initialization!
|
94
|
+
define_initialize_volatile_with_cas
|
104
95
|
|
105
96
|
names.each do |name|
|
106
|
-
ivar = :"@
|
97
|
+
ivar = :"@VolatileCas#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }}"
|
107
98
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
108
99
|
def #{name}
|
109
100
|
#{ivar}.get
|
@@ -126,11 +117,12 @@ module Concurrent
|
|
126
117
|
end
|
127
118
|
RUBY
|
128
119
|
end
|
129
|
-
names.
|
120
|
+
names.flat_map { |n| [n, :"#{n}=", :"swap_#{n}", :"compare_and_set_#{n}", :"update_#{n}"] }
|
130
121
|
end
|
131
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.
|
132
125
|
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
126
|
@volatile_cas_fields ||= []
|
135
127
|
((superclass.volatile_cas_fields if superclass.respond_to?(:volatile_cas_fields) && inherited) || []) +
|
136
128
|
@volatile_cas_fields
|
@@ -138,18 +130,21 @@ module Concurrent
|
|
138
130
|
|
139
131
|
private
|
140
132
|
|
141
|
-
def
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
133
|
+
def self.define_initialize_volatile_with_cas
|
134
|
+
assignments = @volatile_cas_fields.map { |name| "@VolatileCas#{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
|
146
141
|
end
|
147
142
|
|
148
|
-
|
149
|
-
|
143
|
+
private_class_method :define_initialize_volatile_with_cas
|
144
|
+
|
145
|
+
def initialize_volatile_with_cas
|
146
|
+
end
|
150
147
|
|
151
|
-
# @!method self.attr_volatile(*names)
|
152
|
-
# @!macro synchronization_object_method_self_attr_volatile
|
153
148
|
end
|
154
149
|
end
|
155
150
|
end
|
@@ -18,15 +18,16 @@ module Concurrent
|
|
18
18
|
if @__owner__ == Thread.current
|
19
19
|
yield
|
20
20
|
else
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
21
|
+
result = nil
|
22
|
+
Rubinius.synchronize(self) do
|
23
|
+
begin
|
24
|
+
@__owner__ = Thread.current
|
25
|
+
result = yield
|
26
|
+
ensure
|
27
|
+
@__owner__ = nil
|
28
|
+
end
|
29
29
|
end
|
30
|
+
result
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
@@ -1,37 +1,45 @@
|
|
1
1
|
module Concurrent
|
2
2
|
module Synchronization
|
3
3
|
|
4
|
+
module RbxAttrVolatile
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
4
8
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
+
Rubinius.memory_barrier
|
16
|
+
#{ivar}
|
17
|
+
end
|
18
|
+
|
19
|
+
def #{name}=(value)
|
20
|
+
#{ivar} = value
|
21
|
+
Rubinius.memory_barrier
|
22
|
+
end
|
23
|
+
RUBY
|
24
|
+
end
|
25
|
+
names.map { |n| [n, :"#{n}="] }.flatten
|
26
|
+
end
|
10
27
|
end
|
11
28
|
|
12
29
|
def full_memory_barrier
|
13
30
|
# Rubinius instance variables are not volatile so we need to insert barrier
|
14
31
|
Rubinius.memory_barrier
|
15
32
|
end
|
33
|
+
end
|
16
34
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
def #{name}
|
22
|
-
Rubinius.memory_barrier
|
23
|
-
#{ivar}
|
24
|
-
end
|
35
|
+
# @!visibility private
|
36
|
+
# @!macro internal_implementation_note
|
37
|
+
class RbxObject < AbstractObject
|
38
|
+
include RbxAttrVolatile
|
25
39
|
|
26
|
-
|
27
|
-
|
28
|
-
Rubinius.memory_barrier
|
29
|
-
end
|
30
|
-
RUBY
|
31
|
-
end
|
32
|
-
names.map { |n| [n, :"#{n}="] }.flatten
|
40
|
+
def initialize
|
41
|
+
# nothing to do
|
33
42
|
end
|
34
43
|
end
|
35
|
-
|
36
44
|
end
|
37
45
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
# Volatile adds the attr_volatile class method when included.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# class Foo
|
8
|
+
# include Concurrent::Synchronization::Volatile
|
9
|
+
#
|
10
|
+
# attr_volatile :bar
|
11
|
+
#
|
12
|
+
# def initialize
|
13
|
+
# self.bar = 1
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# foo = Foo.new
|
18
|
+
# foo.bar
|
19
|
+
# => 1
|
20
|
+
# foo.bar = 2
|
21
|
+
# => 2
|
22
|
+
|
23
|
+
Volatile = case
|
24
|
+
when Concurrent.on_cruby?
|
25
|
+
MriAttrVolatile
|
26
|
+
when Concurrent.on_jruby?
|
27
|
+
JRubyAttrVolatile
|
28
|
+
when Concurrent.on_rbx? || Concurrent.on_truffle?
|
29
|
+
RbxAttrVolatile
|
30
|
+
else
|
31
|
+
MriAttrVolatile
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/concurrent/tvar.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'set'
|
2
|
+
require 'concurrent/synchronization'
|
2
3
|
|
3
4
|
module Concurrent
|
4
5
|
|
@@ -8,7 +9,8 @@ module Concurrent
|
|
8
9
|
# @!macro thread_safe_variable_comparison
|
9
10
|
#
|
10
11
|
# {include:file:doc/tvar.md}
|
11
|
-
class TVar
|
12
|
+
class TVar < Synchronization::Object
|
13
|
+
safe_initialization!
|
12
14
|
|
13
15
|
# Create a new `TVar` with an initial value.
|
14
16
|
def initialize(value)
|