o-concurrent-ruby 1.1.11
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 +7 -0
- data/CHANGELOG.md +542 -0
- data/Gemfile +37 -0
- data/LICENSE.txt +21 -0
- data/README.md +404 -0
- data/Rakefile +307 -0
- data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +189 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +307 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
- data/lib/concurrent-ruby/concurrent/agent.rb +587 -0
- data/lib/concurrent-ruby/concurrent/array.rb +66 -0
- data/lib/concurrent-ruby/concurrent/async.rb +449 -0
- data/lib/concurrent-ruby/concurrent/atom.rb +222 -0
- data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +66 -0
- data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +126 -0
- data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +143 -0
- data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +164 -0
- data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +205 -0
- data/lib/concurrent-ruby/concurrent/atomic/count_down_latch.rb +100 -0
- data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +128 -0
- data/lib/concurrent-ruby/concurrent/atomic/event.rb +109 -0
- data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +42 -0
- data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +37 -0
- data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +62 -0
- data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +75 -0
- data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +44 -0
- data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +131 -0
- data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +254 -0
- data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +377 -0
- data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +181 -0
- data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +166 -0
- data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +104 -0
- data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +56 -0
- data/lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb +28 -0
- data/lib/concurrent-ruby/concurrent/atomics.rb +10 -0
- data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +107 -0
- data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +111 -0
- data/lib/concurrent-ruby/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
- data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +158 -0
- data/lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
- data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +66 -0
- data/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb +140 -0
- data/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb +82 -0
- data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
- data/lib/concurrent-ruby/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
- data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +160 -0
- data/lib/concurrent-ruby/concurrent/concern/deprecation.rb +34 -0
- data/lib/concurrent-ruby/concurrent/concern/dereferenceable.rb +73 -0
- data/lib/concurrent-ruby/concurrent/concern/logging.rb +32 -0
- data/lib/concurrent-ruby/concurrent/concern/obligation.rb +220 -0
- data/lib/concurrent-ruby/concurrent/concern/observable.rb +110 -0
- data/lib/concurrent-ruby/concurrent/configuration.rb +188 -0
- data/lib/concurrent-ruby/concurrent/constants.rb +8 -0
- data/lib/concurrent-ruby/concurrent/dataflow.rb +81 -0
- data/lib/concurrent-ruby/concurrent/delay.rb +199 -0
- data/lib/concurrent-ruby/concurrent/errors.rb +69 -0
- data/lib/concurrent-ruby/concurrent/exchanger.rb +352 -0
- data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +131 -0
- data/lib/concurrent-ruby/concurrent/executor/cached_thread_pool.rb +62 -0
- data/lib/concurrent-ruby/concurrent/executor/executor_service.rb +185 -0
- data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +220 -0
- data/lib/concurrent-ruby/concurrent/executor/immediate_executor.rb +66 -0
- data/lib/concurrent-ruby/concurrent/executor/indirect_immediate_executor.rb +44 -0
- data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +103 -0
- data/lib/concurrent-ruby/concurrent/executor/java_single_thread_executor.rb +30 -0
- data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +140 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +82 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb +21 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +368 -0
- data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +35 -0
- data/lib/concurrent-ruby/concurrent/executor/serial_executor_service.rb +34 -0
- data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +107 -0
- data/lib/concurrent-ruby/concurrent/executor/serialized_execution_delegator.rb +28 -0
- data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +100 -0
- data/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb +57 -0
- data/lib/concurrent-ruby/concurrent/executor/thread_pool_executor.rb +88 -0
- data/lib/concurrent-ruby/concurrent/executor/timer_set.rb +172 -0
- data/lib/concurrent-ruby/concurrent/executors.rb +20 -0
- data/lib/concurrent-ruby/concurrent/future.rb +141 -0
- data/lib/concurrent-ruby/concurrent/hash.rb +59 -0
- data/lib/concurrent-ruby/concurrent/immutable_struct.rb +101 -0
- data/lib/concurrent-ruby/concurrent/ivar.rb +207 -0
- data/lib/concurrent-ruby/concurrent/map.rb +346 -0
- data/lib/concurrent-ruby/concurrent/maybe.rb +229 -0
- data/lib/concurrent-ruby/concurrent/mutable_struct.rb +239 -0
- data/lib/concurrent-ruby/concurrent/mvar.rb +242 -0
- data/lib/concurrent-ruby/concurrent/options.rb +42 -0
- data/lib/concurrent-ruby/concurrent/promise.rb +580 -0
- data/lib/concurrent-ruby/concurrent/promises.rb +2167 -0
- data/lib/concurrent-ruby/concurrent/re_include.rb +58 -0
- data/lib/concurrent-ruby/concurrent/scheduled_task.rb +331 -0
- data/lib/concurrent-ruby/concurrent/set.rb +74 -0
- data/lib/concurrent-ruby/concurrent/settable_struct.rb +139 -0
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +98 -0
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +24 -0
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb +171 -0
- data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +60 -0
- data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +13 -0
- data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +45 -0
- data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +36 -0
- data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +72 -0
- data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +44 -0
- data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +88 -0
- data/lib/concurrent-ruby/concurrent/synchronization/object.rb +183 -0
- data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +71 -0
- data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +49 -0
- data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +47 -0
- data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +36 -0
- data/lib/concurrent-ruby/concurrent/synchronization.rb +30 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +50 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb +74 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +88 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb +246 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/util/volatile.rb +75 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/util.rb +16 -0
- data/lib/concurrent-ruby/concurrent/timer_task.rb +311 -0
- data/lib/concurrent-ruby/concurrent/tuple.rb +86 -0
- data/lib/concurrent-ruby/concurrent/tvar.rb +221 -0
- data/lib/concurrent-ruby/concurrent/utility/engine.rb +56 -0
- data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +90 -0
- data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +79 -0
- data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +53 -0
- data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +130 -0
- data/lib/concurrent-ruby/concurrent/version.rb +3 -0
- data/lib/concurrent-ruby/concurrent-ruby.rb +5 -0
- data/lib/concurrent-ruby/concurrent.rb +134 -0
- metadata +192 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
# @!visibility private
|
5
|
+
# @!macro internal_implementation_note
|
6
|
+
class AbstractObject
|
7
|
+
|
8
|
+
# @abstract has to be implemented based on Ruby runtime
|
9
|
+
def initialize
|
10
|
+
raise NotImplementedError
|
11
|
+
end
|
12
|
+
|
13
|
+
# @!visibility private
|
14
|
+
# @abstract
|
15
|
+
def full_memory_barrier
|
16
|
+
raise NotImplementedError
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.attr_volatile(*names)
|
20
|
+
raise NotImplementedError
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
# @!visibility private
|
5
|
+
# @!macro internal_implementation_note
|
6
|
+
module AbstractStruct
|
7
|
+
|
8
|
+
# @!visibility private
|
9
|
+
def initialize(*values)
|
10
|
+
super()
|
11
|
+
ns_initialize(*values)
|
12
|
+
end
|
13
|
+
|
14
|
+
# @!macro struct_length
|
15
|
+
#
|
16
|
+
# Returns the number of struct members.
|
17
|
+
#
|
18
|
+
# @return [Fixnum] the number of struct members
|
19
|
+
def length
|
20
|
+
self.class::MEMBERS.length
|
21
|
+
end
|
22
|
+
alias_method :size, :length
|
23
|
+
|
24
|
+
# @!macro struct_members
|
25
|
+
#
|
26
|
+
# Returns the struct members as an array of symbols.
|
27
|
+
#
|
28
|
+
# @return [Array] the struct members as an array of symbols
|
29
|
+
def members
|
30
|
+
self.class::MEMBERS.dup
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
# @!macro struct_values
|
36
|
+
#
|
37
|
+
# @!visibility private
|
38
|
+
def ns_values
|
39
|
+
@values.dup
|
40
|
+
end
|
41
|
+
|
42
|
+
# @!macro struct_values_at
|
43
|
+
#
|
44
|
+
# @!visibility private
|
45
|
+
def ns_values_at(indexes)
|
46
|
+
@values.values_at(*indexes)
|
47
|
+
end
|
48
|
+
|
49
|
+
# @!macro struct_to_h
|
50
|
+
#
|
51
|
+
# @!visibility private
|
52
|
+
def ns_to_h
|
53
|
+
length.times.reduce({}){|memo, i| memo[self.class::MEMBERS[i]] = @values[i]; memo}
|
54
|
+
end
|
55
|
+
|
56
|
+
# @!macro struct_get
|
57
|
+
#
|
58
|
+
# @!visibility private
|
59
|
+
def ns_get(member)
|
60
|
+
if member.is_a? Integer
|
61
|
+
if member >= @values.length
|
62
|
+
raise IndexError.new("offset #{member} too large for struct(size:#{@values.length})")
|
63
|
+
end
|
64
|
+
@values[member]
|
65
|
+
else
|
66
|
+
send(member)
|
67
|
+
end
|
68
|
+
rescue NoMethodError
|
69
|
+
raise NameError.new("no member '#{member}' in struct")
|
70
|
+
end
|
71
|
+
|
72
|
+
# @!macro struct_equality
|
73
|
+
#
|
74
|
+
# @!visibility private
|
75
|
+
def ns_equality(other)
|
76
|
+
self.class == other.class && self.values == other.values
|
77
|
+
end
|
78
|
+
|
79
|
+
# @!macro struct_each
|
80
|
+
#
|
81
|
+
# @!visibility private
|
82
|
+
def ns_each
|
83
|
+
values.each{|value| yield value }
|
84
|
+
end
|
85
|
+
|
86
|
+
# @!macro struct_each_pair
|
87
|
+
#
|
88
|
+
# @!visibility private
|
89
|
+
def ns_each_pair
|
90
|
+
@values.length.times do |index|
|
91
|
+
yield self.class::MEMBERS[index], @values[index]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# @!macro struct_select
|
96
|
+
#
|
97
|
+
# @!visibility private
|
98
|
+
def ns_select
|
99
|
+
values.select{|value| yield value }
|
100
|
+
end
|
101
|
+
|
102
|
+
# @!macro struct_inspect
|
103
|
+
#
|
104
|
+
# @!visibility private
|
105
|
+
def ns_inspect
|
106
|
+
struct = pr_underscore(self.class.ancestors[1])
|
107
|
+
clazz = ((self.class.to_s =~ /^#<Class:/) == 0) ? '' : " #{self.class}"
|
108
|
+
"#<#{struct}#{clazz} #{ns_to_h}>"
|
109
|
+
end
|
110
|
+
|
111
|
+
# @!macro struct_merge
|
112
|
+
#
|
113
|
+
# @!visibility private
|
114
|
+
def ns_merge(other, &block)
|
115
|
+
self.class.new(*self.to_h.merge(other, &block).values)
|
116
|
+
end
|
117
|
+
|
118
|
+
# @!visibility private
|
119
|
+
def ns_initialize_copy
|
120
|
+
@values = @values.map do |val|
|
121
|
+
begin
|
122
|
+
val.clone
|
123
|
+
rescue TypeError
|
124
|
+
val
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# @!visibility private
|
130
|
+
def pr_underscore(clazz)
|
131
|
+
word = clazz.to_s.dup # dup string to workaround JRuby 9.2.0.0 bug https://github.com/jruby/jruby/issues/5229
|
132
|
+
word.gsub!(/::/, '/')
|
133
|
+
word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
134
|
+
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
135
|
+
word.tr!("-", "_")
|
136
|
+
word.downcase!
|
137
|
+
word
|
138
|
+
end
|
139
|
+
|
140
|
+
# @!visibility private
|
141
|
+
def self.define_struct_class(parent, base, name, members, &block)
|
142
|
+
clazz = Class.new(base || Object) do
|
143
|
+
include parent
|
144
|
+
self.const_set(:MEMBERS, members.collect{|member| member.to_s.to_sym}.freeze)
|
145
|
+
def ns_initialize(*values)
|
146
|
+
raise ArgumentError.new('struct size differs') if values.length > length
|
147
|
+
@values = values.fill(nil, values.length..length-1)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
unless name.nil?
|
151
|
+
begin
|
152
|
+
parent.send :remove_const, name if parent.const_defined?(name, false)
|
153
|
+
parent.const_set(name, clazz)
|
154
|
+
clazz
|
155
|
+
rescue NameError
|
156
|
+
raise NameError.new("identifier #{name} needs to be constant")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
members.each_with_index do |member, index|
|
160
|
+
clazz.send :remove_method, member if clazz.instance_methods.include? member
|
161
|
+
clazz.send(:define_method, member) do
|
162
|
+
@values[index]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
clazz.class_exec(&block) unless block.nil?
|
166
|
+
clazz.singleton_class.send :alias_method, :[], :new
|
167
|
+
clazz
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
# @!visibility private
|
5
|
+
# TODO (pitr-ch 04-Dec-2016): should be in edge
|
6
|
+
class Condition < LockableObject
|
7
|
+
safe_initialization!
|
8
|
+
|
9
|
+
# TODO (pitr 12-Sep-2015): locks two objects, improve
|
10
|
+
# TODO (pitr 26-Sep-2015): study
|
11
|
+
# http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/java/util/concurrent/locks/AbstractQueuedSynchronizer.java#AbstractQueuedSynchronizer.Node
|
12
|
+
|
13
|
+
singleton_class.send :alias_method, :private_new, :new
|
14
|
+
private_class_method :new
|
15
|
+
|
16
|
+
def initialize(lock)
|
17
|
+
super()
|
18
|
+
@Lock = lock
|
19
|
+
end
|
20
|
+
|
21
|
+
def wait(timeout = nil)
|
22
|
+
@Lock.synchronize { ns_wait(timeout) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def ns_wait(timeout = nil)
|
26
|
+
synchronize { super(timeout) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def wait_until(timeout = nil, &condition)
|
30
|
+
@Lock.synchronize { ns_wait_until(timeout, &condition) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def ns_wait_until(timeout = nil, &condition)
|
34
|
+
synchronize { super(timeout, &condition) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def signal
|
38
|
+
@Lock.synchronize { ns_signal }
|
39
|
+
end
|
40
|
+
|
41
|
+
def ns_signal
|
42
|
+
synchronize { super }
|
43
|
+
end
|
44
|
+
|
45
|
+
def broadcast
|
46
|
+
@Lock.synchronize { ns_broadcast }
|
47
|
+
end
|
48
|
+
|
49
|
+
def ns_broadcast
|
50
|
+
synchronize { super }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class LockableObject < LockableObjectImplementation
|
55
|
+
def new_condition
|
56
|
+
Condition.private_new(self)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
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,45 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
if Concurrent.on_jruby? && Concurrent.java_extensions_loaded?
|
5
|
+
|
6
|
+
# @!visibility private
|
7
|
+
module JRubyAttrVolatile
|
8
|
+
def self.included(base)
|
9
|
+
base.extend(ClassMethods)
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def attr_volatile(*names)
|
14
|
+
names.each do |name|
|
15
|
+
|
16
|
+
ivar = :"@volatile_#{name}"
|
17
|
+
|
18
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
19
|
+
def #{name}
|
20
|
+
instance_variable_get_volatile(:#{ivar})
|
21
|
+
end
|
22
|
+
|
23
|
+
def #{name}=(value)
|
24
|
+
instance_variable_set_volatile(:#{ivar}, value)
|
25
|
+
end
|
26
|
+
RUBY
|
27
|
+
|
28
|
+
end
|
29
|
+
names.map { |n| [n, :"#{n}="] }.flatten
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# @!visibility private
|
35
|
+
# @!macro internal_implementation_note
|
36
|
+
class JRubyObject < AbstractObject
|
37
|
+
include JRubyAttrVolatile
|
38
|
+
|
39
|
+
def initialize
|
40
|
+
# nothing to do
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
# @!visibility private
|
5
|
+
# TODO (pitr-ch 04-Dec-2016): should be in edge
|
6
|
+
class Lock < LockableObject
|
7
|
+
# TODO use JavaReentrantLock on JRuby
|
8
|
+
|
9
|
+
public :synchronize
|
10
|
+
|
11
|
+
def wait(timeout = nil)
|
12
|
+
synchronize { ns_wait(timeout) }
|
13
|
+
end
|
14
|
+
|
15
|
+
public :ns_wait
|
16
|
+
|
17
|
+
def wait_until(timeout = nil, &condition)
|
18
|
+
synchronize { ns_wait_until(timeout, &condition) }
|
19
|
+
end
|
20
|
+
|
21
|
+
public :ns_wait_until
|
22
|
+
|
23
|
+
def signal
|
24
|
+
synchronize { ns_signal }
|
25
|
+
end
|
26
|
+
|
27
|
+
public :ns_signal
|
28
|
+
|
29
|
+
def broadcast
|
30
|
+
synchronize { ns_broadcast }
|
31
|
+
end
|
32
|
+
|
33
|
+
public :ns_broadcast
|
34
|
+
end
|
35
|
+
end
|
36
|
+
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?
|
8
|
+
MutexLockableObject
|
9
|
+
when Concurrent.on_jruby?
|
10
|
+
JRubyLockableObject
|
11
|
+
when Concurrent.on_rbx?
|
12
|
+
RbxLockableObject
|
13
|
+
when Concurrent.on_truffleruby?
|
14
|
+
MutexLockableObject
|
15
|
+
else
|
16
|
+
warn 'Possibly unsupported Ruby implementation'
|
17
|
+
MonitorLockableObject
|
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/Thread.html#method-i-wakeup)
|
28
|
+
# and [`Thread#raise`](http://ruby-doc.org/core/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,44 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
# @!visibility private
|
5
|
+
module MriAttrVolatile
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def attr_volatile(*names)
|
12
|
+
names.each do |name|
|
13
|
+
ivar = :"@volatile_#{name}"
|
14
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
15
|
+
def #{name}
|
16
|
+
#{ivar}
|
17
|
+
end
|
18
|
+
|
19
|
+
def #{name}=(value)
|
20
|
+
#{ivar} = value
|
21
|
+
end
|
22
|
+
RUBY
|
23
|
+
end
|
24
|
+
names.map { |n| [n, :"#{n}="] }.flatten
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def full_memory_barrier
|
29
|
+
# relying on undocumented behavior of CRuby, GVL acquire has lock which ensures visibility of ivars
|
30
|
+
# https://github.com/ruby/ruby/blob/ruby_2_2/thread_pthread.c#L204-L211
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# @!visibility private
|
35
|
+
# @!macro internal_implementation_note
|
36
|
+
class MriObject < AbstractObject
|
37
|
+
include MriAttrVolatile
|
38
|
+
|
39
|
+
def initialize
|
40
|
+
# nothing to do
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Concurrent
|
2
|
+
# noinspection RubyInstanceVariableNamingConvention
|
3
|
+
module Synchronization
|
4
|
+
|
5
|
+
# @!visibility private
|
6
|
+
# @!macro internal_implementation_note
|
7
|
+
module ConditionSignalling
|
8
|
+
protected
|
9
|
+
|
10
|
+
def ns_signal
|
11
|
+
@__Condition__.signal
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def ns_broadcast
|
16
|
+
@__Condition__.broadcast
|
17
|
+
self
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
# @!visibility private
|
23
|
+
# @!macro internal_implementation_note
|
24
|
+
class MutexLockableObject < AbstractLockableObject
|
25
|
+
include ConditionSignalling
|
26
|
+
|
27
|
+
safe_initialization!
|
28
|
+
|
29
|
+
def initialize(*defaults)
|
30
|
+
super(*defaults)
|
31
|
+
@__Lock__ = ::Mutex.new
|
32
|
+
@__Condition__ = ::ConditionVariable.new
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize_copy(other)
|
36
|
+
super
|
37
|
+
@__Lock__ = ::Mutex.new
|
38
|
+
@__Condition__ = ::ConditionVariable.new
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
def synchronize
|
44
|
+
if @__Lock__.owned?
|
45
|
+
yield
|
46
|
+
else
|
47
|
+
@__Lock__.synchronize { yield }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def ns_wait(timeout = nil)
|
52
|
+
@__Condition__.wait @__Lock__, timeout
|
53
|
+
self
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# @!visibility private
|
58
|
+
# @!macro internal_implementation_note
|
59
|
+
class MonitorLockableObject < AbstractLockableObject
|
60
|
+
include ConditionSignalling
|
61
|
+
|
62
|
+
safe_initialization!
|
63
|
+
|
64
|
+
def initialize(*defaults)
|
65
|
+
super(*defaults)
|
66
|
+
@__Lock__ = ::Monitor.new
|
67
|
+
@__Condition__ = @__Lock__.new_cond
|
68
|
+
end
|
69
|
+
|
70
|
+
def initialize_copy(other)
|
71
|
+
super
|
72
|
+
@__Lock__ = ::Monitor.new
|
73
|
+
@__Condition__ = @__Lock__.new_cond
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
|
78
|
+
def synchronize # TODO may be a problem with lock.synchronize { lock.wait }
|
79
|
+
@__Lock__.synchronize { yield }
|
80
|
+
end
|
81
|
+
|
82
|
+
def ns_wait(timeout = nil)
|
83
|
+
@__Condition__.wait timeout
|
84
|
+
self
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|