concurrent-ruby 1.0.5 → 1.1.1
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 +5 -5
- data/CHANGELOG.md +65 -0
- data/Gemfile +39 -0
- data/{LICENSE.txt → LICENSE.md} +2 -0
- data/README.md +207 -105
- data/Rakefile +314 -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 +159 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +306 -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/agent.rb +7 -7
- data/lib/concurrent/array.rb +59 -32
- data/lib/concurrent/async.rb +4 -4
- data/lib/concurrent/atom.rb +9 -9
- data/lib/concurrent/atomic/atomic_boolean.rb +24 -20
- data/lib/concurrent/atomic/atomic_fixnum.rb +27 -23
- data/lib/concurrent/atomic/atomic_markable_reference.rb +164 -0
- data/lib/concurrent/atomic/atomic_reference.rb +185 -32
- data/lib/concurrent/atomic/count_down_latch.rb +6 -6
- data/lib/concurrent/atomic/cyclic_barrier.rb +1 -1
- data/lib/concurrent/atomic/event.rb +1 -1
- data/lib/concurrent/atomic/java_count_down_latch.rb +9 -6
- data/lib/concurrent/atomic/mutex_atomic_boolean.rb +2 -0
- data/lib/concurrent/atomic/mutex_count_down_latch.rb +1 -0
- data/lib/concurrent/atomic/read_write_lock.rb +2 -1
- data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
- data/lib/concurrent/atomic/semaphore.rb +8 -8
- data/lib/concurrent/atomic/thread_local_var.rb +7 -7
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +3 -8
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +1 -1
- data/lib/concurrent/atomics.rb +0 -43
- data/lib/concurrent/collection/lock_free_stack.rb +158 -0
- data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +3 -3
- data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +1 -2
- data/lib/concurrent/collection/non_concurrent_priority_queue.rb +29 -29
- data/lib/concurrent/concern/dereferenceable.rb +1 -1
- data/lib/concurrent/concern/logging.rb +6 -1
- data/lib/concurrent/concern/observable.rb +7 -7
- data/lib/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent/configuration.rb +1 -6
- data/lib/concurrent/constants.rb +1 -1
- data/lib/concurrent/dataflow.rb +2 -1
- data/lib/concurrent/delay.rb +9 -7
- data/lib/concurrent/exchanger.rb +21 -25
- data/lib/concurrent/executor/abstract_executor_service.rb +2 -2
- data/lib/concurrent/executor/cached_thread_pool.rb +1 -1
- data/lib/concurrent/executor/executor_service.rb +15 -15
- data/lib/concurrent/executor/fixed_thread_pool.rb +18 -18
- data/lib/concurrent/executor/java_thread_pool_executor.rb +10 -7
- data/lib/concurrent/executor/single_thread_executor.rb +2 -2
- data/lib/concurrent/executor/thread_pool_executor.rb +6 -6
- data/lib/concurrent/executor/timer_set.rb +1 -1
- data/lib/concurrent/future.rb +4 -1
- data/lib/concurrent/hash.rb +53 -30
- data/lib/concurrent/ivar.rb +5 -6
- data/lib/concurrent/map.rb +178 -81
- data/lib/concurrent/maybe.rb +1 -1
- data/lib/concurrent/mutable_struct.rb +15 -14
- data/lib/concurrent/mvar.rb +2 -2
- data/lib/concurrent/promise.rb +53 -21
- data/lib/concurrent/promises.rb +1936 -0
- data/lib/concurrent/re_include.rb +58 -0
- data/lib/concurrent/set.rb +66 -0
- data/lib/concurrent/settable_struct.rb +1 -0
- data/lib/concurrent/synchronization/abstract_lockable_object.rb +5 -5
- data/lib/concurrent/synchronization/abstract_struct.rb +6 -4
- data/lib/concurrent/synchronization/lockable_object.rb +6 -6
- data/lib/concurrent/synchronization/{mri_lockable_object.rb → mutex_lockable_object.rb} +19 -14
- data/lib/concurrent/synchronization/object.rb +8 -4
- data/lib/concurrent/synchronization/truffleruby_object.rb +46 -0
- data/lib/concurrent/synchronization/volatile.rb +11 -9
- data/lib/concurrent/synchronization.rb +4 -5
- data/lib/concurrent/thread_safe/util/data_structures.rb +63 -0
- data/lib/concurrent/thread_safe/util/striped64.rb +9 -4
- data/lib/concurrent/timer_task.rb +5 -2
- data/lib/concurrent/tuple.rb +1 -1
- data/lib/concurrent/tvar.rb +2 -2
- data/lib/concurrent/utility/193.rb +17 -0
- data/lib/concurrent/utility/at_exit.rb +1 -1
- data/lib/concurrent/utility/engine.rb +4 -4
- data/lib/concurrent/utility/monotonic_time.rb +3 -3
- data/lib/concurrent/utility/native_extension_loader.rb +31 -33
- data/lib/concurrent/utility/processor_counter.rb +0 -2
- data/lib/concurrent/version.rb +2 -2
- data/lib/concurrent-ruby.rb +1 -0
- data/lib/concurrent.rb +26 -20
- metadata +33 -18
- data/lib/concurrent/atomic_reference/concurrent_update_error.rb +0 -8
- data/lib/concurrent/atomic_reference/direct_update.rb +0 -81
- data/lib/concurrent/atomic_reference/jruby+truffle.rb +0 -2
- data/lib/concurrent/atomic_reference/jruby.rb +0 -16
- data/lib/concurrent/atomic_reference/rbx.rb +0 -22
- data/lib/concurrent/atomic_reference/ruby.rb +0 -32
- data/lib/concurrent/edge.rb +0 -26
- data/lib/concurrent/lazy_register.rb +0 -81
- data/lib/concurrent/synchronization/truffle_lockable_object.rb +0 -9
- data/lib/concurrent/synchronization/truffle_object.rb +0 -31
- data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +0 -30
@@ -0,0 +1,58 @@
|
|
1
|
+
module Concurrent
|
2
|
+
|
3
|
+
# Methods form module A included to a module B, which is already included into class C,
|
4
|
+
# will not be visible in the C class. If this module is extended to B then A's methods
|
5
|
+
# are correctly made visible to C.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# module A
|
9
|
+
# def a
|
10
|
+
# :a
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# module B1
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# class C1
|
18
|
+
# include B1
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# module B2
|
22
|
+
# extend Concurrent::ReInclude
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# class C2
|
26
|
+
# include B2
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# B1.send :include, A
|
30
|
+
# B2.send :include, A
|
31
|
+
#
|
32
|
+
# C1.new.respond_to? :a # => false
|
33
|
+
# C2.new.respond_to? :a # => true
|
34
|
+
module ReInclude
|
35
|
+
# @!visibility private
|
36
|
+
def included(base)
|
37
|
+
(@re_include_to_bases ||= []) << [:include, base]
|
38
|
+
super(base)
|
39
|
+
end
|
40
|
+
|
41
|
+
# @!visibility private
|
42
|
+
def extended(base)
|
43
|
+
(@re_include_to_bases ||= []) << [:extend, base]
|
44
|
+
super(base)
|
45
|
+
end
|
46
|
+
|
47
|
+
# @!visibility private
|
48
|
+
def include(*modules)
|
49
|
+
result = super(*modules)
|
50
|
+
modules.reverse.each do |module_being_included|
|
51
|
+
(@re_include_to_bases ||= []).each do |method, mod|
|
52
|
+
mod.send method, module_being_included
|
53
|
+
end
|
54
|
+
end
|
55
|
+
result
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'concurrent/utility/engine'
|
2
|
+
require 'concurrent/thread_safe/util'
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
module Concurrent
|
6
|
+
|
7
|
+
# @!macro concurrent_set
|
8
|
+
#
|
9
|
+
# A thread-safe subclass of Set. This version locks against the object
|
10
|
+
# itself for every method call, ensuring only one thread can be reading
|
11
|
+
# or writing at a time. This includes iteration methods like `#each`.
|
12
|
+
#
|
13
|
+
# @note `a += b` is **not** a **thread-safe** operation on
|
14
|
+
# `Concurrent::Set`. It reads Set `a`, then it creates new `Concurrent::Set`
|
15
|
+
# which is union of `a` and `b`, then it writes the union to `a`.
|
16
|
+
# The read and write are independent operations they do not form a single atomic
|
17
|
+
# operation therefore when two `+=` operations are executed concurrently updates
|
18
|
+
# may be lost. Use `#merge` instead.
|
19
|
+
#
|
20
|
+
# @see http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html Ruby standard library `Set`
|
21
|
+
|
22
|
+
|
23
|
+
# @!macro internal_implementation_note
|
24
|
+
SetImplementation = case
|
25
|
+
when Concurrent.on_cruby?
|
26
|
+
# Because MRI never runs code in parallel, the existing
|
27
|
+
# non-thread-safe structures should usually work fine.
|
28
|
+
::Set
|
29
|
+
|
30
|
+
when Concurrent.on_jruby?
|
31
|
+
require 'jruby/synchronized'
|
32
|
+
|
33
|
+
class JRubySet < ::Set
|
34
|
+
include JRuby::Synchronized
|
35
|
+
end
|
36
|
+
JRubySet
|
37
|
+
|
38
|
+
when Concurrent.on_rbx?
|
39
|
+
require 'monitor'
|
40
|
+
require 'concurrent/thread_safe/util/data_structures'
|
41
|
+
|
42
|
+
class RbxSet < ::Set
|
43
|
+
end
|
44
|
+
ThreadSafe::Util.make_synchronized_on_rbx Concurrent::RbxSet
|
45
|
+
RbxSet
|
46
|
+
|
47
|
+
when Concurrent.on_truffleruby?
|
48
|
+
require 'concurrent/thread_safe/util/data_structures'
|
49
|
+
|
50
|
+
class TruffleRubySet < ::Set
|
51
|
+
end
|
52
|
+
|
53
|
+
ThreadSafe::Util.make_synchronized_on_truffleruby Concurrent::TruffleRubySet
|
54
|
+
TruffleRubySet
|
55
|
+
|
56
|
+
else
|
57
|
+
warn 'Possibly unsupported Ruby implementation'
|
58
|
+
::Set
|
59
|
+
end
|
60
|
+
private_constant :SetImplementation
|
61
|
+
|
62
|
+
# @!macro concurrent_set
|
63
|
+
class Set < SetImplementation
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
@@ -107,6 +107,7 @@ module Concurrent
|
|
107
107
|
synchronize do
|
108
108
|
clazz = Synchronization::AbstractStruct.define_struct_class(SettableStruct, Synchronization::LockableObject, name, members, &block)
|
109
109
|
members.each_with_index do |member, index|
|
110
|
+
clazz.send :remove_method, member if clazz.instance_methods.include? member
|
110
111
|
clazz.send(:define_method, member) do
|
111
112
|
synchronize { @values[index] }
|
112
113
|
end
|
@@ -6,7 +6,7 @@ module Concurrent
|
|
6
6
|
|
7
7
|
protected
|
8
8
|
|
9
|
-
# @!macro
|
9
|
+
# @!macro synchronization_object_method_synchronize
|
10
10
|
#
|
11
11
|
# @yield runs the block synchronized against this object,
|
12
12
|
# equivalent of java's `synchronize(this) {}`
|
@@ -15,7 +15,7 @@ module Concurrent
|
|
15
15
|
raise NotImplementedError
|
16
16
|
end
|
17
17
|
|
18
|
-
# @!macro
|
18
|
+
# @!macro synchronization_object_method_ns_wait_until
|
19
19
|
#
|
20
20
|
# Wait until condition is met or timeout passes,
|
21
21
|
# protects against spurious wake-ups.
|
@@ -45,7 +45,7 @@ module Concurrent
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
# @!macro
|
48
|
+
# @!macro synchronization_object_method_ns_wait
|
49
49
|
#
|
50
50
|
# Wait until another thread calls #signal or #broadcast,
|
51
51
|
# spurious wake-ups can happen.
|
@@ -63,7 +63,7 @@ module Concurrent
|
|
63
63
|
raise NotImplementedError
|
64
64
|
end
|
65
65
|
|
66
|
-
# @!macro
|
66
|
+
# @!macro synchronization_object_method_ns_signal
|
67
67
|
#
|
68
68
|
# Signal one waiting thread.
|
69
69
|
# @return [self]
|
@@ -78,7 +78,7 @@ module Concurrent
|
|
78
78
|
raise NotImplementedError
|
79
79
|
end
|
80
80
|
|
81
|
-
# @!macro
|
81
|
+
# @!macro synchronization_object_method_ns_broadcast
|
82
82
|
#
|
83
83
|
# Broadcast to all waiting threads.
|
84
84
|
# @return [self]
|
@@ -11,7 +11,7 @@ module Concurrent
|
|
11
11
|
ns_initialize(*values)
|
12
12
|
end
|
13
13
|
|
14
|
-
# @!macro
|
14
|
+
# @!macro struct_length
|
15
15
|
#
|
16
16
|
# Returns the number of struct members.
|
17
17
|
#
|
@@ -21,7 +21,7 @@ module Concurrent
|
|
21
21
|
end
|
22
22
|
alias_method :size, :length
|
23
23
|
|
24
|
-
# @!macro
|
24
|
+
# @!macro struct_members
|
25
25
|
#
|
26
26
|
# Returns the struct members as an array of symbols.
|
27
27
|
#
|
@@ -117,7 +117,7 @@ module Concurrent
|
|
117
117
|
|
118
118
|
# @!visibility private
|
119
119
|
def pr_underscore(clazz)
|
120
|
-
word = clazz.to_s
|
120
|
+
word = clazz.to_s.dup # dup string to workaround JRuby 9.2.0.0 bug https://github.com/jruby/jruby/issues/5229
|
121
121
|
word.gsub!(/::/, '/')
|
122
122
|
word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
123
123
|
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
@@ -138,13 +138,15 @@ module Concurrent
|
|
138
138
|
end
|
139
139
|
unless name.nil?
|
140
140
|
begin
|
141
|
+
parent.send :remove_const, name if parent.const_defined? name
|
141
142
|
parent.const_set(name, clazz)
|
142
|
-
|
143
|
+
clazz
|
143
144
|
rescue NameError
|
144
145
|
raise NameError.new("identifier #{name} needs to be constant")
|
145
146
|
end
|
146
147
|
end
|
147
148
|
members.each_with_index do |member, index|
|
149
|
+
clazz.send :remove_method, member if clazz.instance_methods.include? member
|
148
150
|
clazz.send(:define_method, member) do
|
149
151
|
@values[index]
|
150
152
|
end
|
@@ -5,18 +5,18 @@ module Concurrent
|
|
5
5
|
# @!macro internal_implementation_note
|
6
6
|
LockableObjectImplementation = case
|
7
7
|
when Concurrent.on_cruby? && Concurrent.ruby_version(:<=, 1, 9, 3)
|
8
|
-
|
8
|
+
MonitorLockableObject
|
9
9
|
when Concurrent.on_cruby? && Concurrent.ruby_version(:>, 1, 9, 3)
|
10
|
-
|
10
|
+
MutexLockableObject
|
11
11
|
when Concurrent.on_jruby?
|
12
12
|
JRubyLockableObject
|
13
13
|
when Concurrent.on_rbx?
|
14
14
|
RbxLockableObject
|
15
|
-
when Concurrent.
|
16
|
-
|
15
|
+
when Concurrent.on_truffleruby?
|
16
|
+
MutexLockableObject
|
17
17
|
else
|
18
18
|
warn 'Possibly unsupported Ruby implementation'
|
19
|
-
|
19
|
+
MonitorLockableObject
|
20
20
|
end
|
21
21
|
private_constant :LockableObjectImplementation
|
22
22
|
|
@@ -31,7 +31,7 @@ module Concurrent
|
|
31
31
|
# `Thread#sleep` and `Thread#wakeup` will work as expected but mixing `Synchronization::Object#wait` and
|
32
32
|
# `Thread#wakeup` will not work on all platforms.
|
33
33
|
#
|
34
|
-
# @see
|
34
|
+
# @see Event implementation as an example of this class use
|
35
35
|
#
|
36
36
|
# @example simple
|
37
37
|
# class AnClass < Synchronization::Object
|
@@ -1,18 +1,19 @@
|
|
1
1
|
module Concurrent
|
2
|
+
# noinspection RubyInstanceVariableNamingConvention
|
2
3
|
module Synchronization
|
3
4
|
|
4
5
|
# @!visibility private
|
5
6
|
# @!macro internal_implementation_note
|
6
|
-
|
7
|
+
module ConditionSignalling
|
7
8
|
protected
|
8
9
|
|
9
10
|
def ns_signal
|
10
|
-
@
|
11
|
+
@__Condition__.signal
|
11
12
|
self
|
12
13
|
end
|
13
14
|
|
14
15
|
def ns_broadcast
|
15
|
-
@
|
16
|
+
@__Condition__.broadcast
|
16
17
|
self
|
17
18
|
end
|
18
19
|
end
|
@@ -20,50 +21,54 @@ module Concurrent
|
|
20
21
|
|
21
22
|
# @!visibility private
|
22
23
|
# @!macro internal_implementation_note
|
23
|
-
class
|
24
|
+
class MutexLockableObject < AbstractLockableObject
|
25
|
+
include ConditionSignalling
|
26
|
+
|
24
27
|
safe_initialization!
|
25
28
|
|
26
29
|
def initialize(*defaults)
|
27
30
|
super(*defaults)
|
28
|
-
@
|
29
|
-
@
|
31
|
+
@__Lock__ = ::Mutex.new
|
32
|
+
@__Condition__ = ::ConditionVariable.new
|
30
33
|
end
|
31
34
|
|
32
35
|
protected
|
33
36
|
|
34
37
|
def synchronize
|
35
|
-
if @
|
38
|
+
if @__Lock__.owned?
|
36
39
|
yield
|
37
40
|
else
|
38
|
-
@
|
41
|
+
@__Lock__.synchronize { yield }
|
39
42
|
end
|
40
43
|
end
|
41
44
|
|
42
45
|
def ns_wait(timeout = nil)
|
43
|
-
@
|
46
|
+
@__Condition__.wait @__Lock__, timeout
|
44
47
|
self
|
45
48
|
end
|
46
49
|
end
|
47
50
|
|
48
51
|
# @!visibility private
|
49
52
|
# @!macro internal_implementation_note
|
50
|
-
class
|
53
|
+
class MonitorLockableObject < AbstractLockableObject
|
54
|
+
include ConditionSignalling
|
55
|
+
|
51
56
|
safe_initialization!
|
52
57
|
|
53
58
|
def initialize(*defaults)
|
54
59
|
super(*defaults)
|
55
|
-
@
|
56
|
-
@
|
60
|
+
@__Lock__ = ::Monitor.new
|
61
|
+
@__Condition__ = @__Lock__.new_cond
|
57
62
|
end
|
58
63
|
|
59
64
|
protected
|
60
65
|
|
61
66
|
def synchronize # TODO may be a problem with lock.synchronize { lock.wait }
|
62
|
-
@
|
67
|
+
@__Lock__.synchronize { yield }
|
63
68
|
end
|
64
69
|
|
65
70
|
def ns_wait(timeout = nil)
|
66
|
-
@
|
71
|
+
@__Condition__.wait timeout
|
67
72
|
self
|
68
73
|
end
|
69
74
|
end
|
@@ -10,9 +10,10 @@ module Concurrent
|
|
10
10
|
JRubyObject
|
11
11
|
when Concurrent.on_rbx?
|
12
12
|
RbxObject
|
13
|
-
when Concurrent.
|
14
|
-
|
13
|
+
when Concurrent.on_truffleruby?
|
14
|
+
TruffleRubyObject
|
15
15
|
else
|
16
|
+
warn 'Possibly unsupported Ruby implementation'
|
16
17
|
MriObject
|
17
18
|
end
|
18
19
|
private_constant :ObjectImplementation
|
@@ -134,8 +135,11 @@ module Concurrent
|
|
134
135
|
private
|
135
136
|
|
136
137
|
def self.define_initialize_volatile_with_cas
|
137
|
-
assignments = @volatile_cas_fields.map
|
138
|
-
|
138
|
+
assignments = @volatile_cas_fields.map do |name|
|
139
|
+
"@Atomic#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }} = Concurrent::AtomicReference.new(nil)"
|
140
|
+
end.join("\n")
|
141
|
+
|
142
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
139
143
|
def initialize_volatile_with_cas
|
140
144
|
super
|
141
145
|
#{assignments}
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
module TruffleRubyAttrVolatile
|
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
|
+
|
14
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
15
|
+
def #{name}
|
16
|
+
full_memory_barrier
|
17
|
+
#{ivar}
|
18
|
+
end
|
19
|
+
|
20
|
+
def #{name}=(value)
|
21
|
+
#{ivar} = value
|
22
|
+
full_memory_barrier
|
23
|
+
end
|
24
|
+
RUBY
|
25
|
+
end
|
26
|
+
|
27
|
+
names.map { |n| [n, :"#{n}="] }.flatten
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def full_memory_barrier
|
32
|
+
TruffleRuby.full_memory_barrier
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# @!visibility private
|
37
|
+
# @!macro internal_implementation_note
|
38
|
+
class TruffleRubyObject < AbstractObject
|
39
|
+
include TruffleRubyAttrVolatile
|
40
|
+
|
41
|
+
def initialize
|
42
|
+
# nothing to do
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -21,14 +21,16 @@ module Concurrent
|
|
21
21
|
# => 2
|
22
22
|
|
23
23
|
Volatile = case
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
24
|
+
when Concurrent.on_cruby?
|
25
|
+
MriAttrVolatile
|
26
|
+
when Concurrent.on_jruby?
|
27
|
+
JRubyAttrVolatile
|
28
|
+
when Concurrent.on_rbx?
|
29
|
+
RbxAttrVolatile
|
30
|
+
when Concurrent.on_truffleruby?
|
31
|
+
TruffleRubyAttrVolatile
|
32
|
+
else
|
33
|
+
MriAttrVolatile
|
34
|
+
end
|
33
35
|
end
|
34
36
|
end
|
@@ -7,15 +7,14 @@ Concurrent.load_native_extensions
|
|
7
7
|
require 'concurrent/synchronization/mri_object'
|
8
8
|
require 'concurrent/synchronization/jruby_object'
|
9
9
|
require 'concurrent/synchronization/rbx_object'
|
10
|
-
require 'concurrent/synchronization/
|
10
|
+
require 'concurrent/synchronization/truffleruby_object'
|
11
11
|
require 'concurrent/synchronization/object'
|
12
12
|
require 'concurrent/synchronization/volatile'
|
13
13
|
|
14
14
|
require 'concurrent/synchronization/abstract_lockable_object'
|
15
|
-
require 'concurrent/synchronization/
|
15
|
+
require 'concurrent/synchronization/mutex_lockable_object'
|
16
16
|
require 'concurrent/synchronization/jruby_lockable_object'
|
17
17
|
require 'concurrent/synchronization/rbx_lockable_object'
|
18
|
-
require 'concurrent/synchronization/truffle_lockable_object'
|
19
18
|
|
20
19
|
require 'concurrent/synchronization/lockable_object'
|
21
20
|
|
@@ -23,8 +22,8 @@ require 'concurrent/synchronization/condition'
|
|
23
22
|
require 'concurrent/synchronization/lock'
|
24
23
|
|
25
24
|
module Concurrent
|
26
|
-
# {include:file:
|
27
|
-
# {include:file:
|
25
|
+
# {include:file:docs-source/synchronization.md}
|
26
|
+
# {include:file:docs-source/synchronization-notes.md}
|
28
27
|
module Synchronization
|
29
28
|
end
|
30
29
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'concurrent/thread_safe/util'
|
2
|
+
|
3
|
+
# Shim for TruffleRuby.synchronized
|
4
|
+
if Concurrent.on_truffleruby? && !TruffleRuby.respond_to?(:synchronized)
|
5
|
+
module TruffleRuby
|
6
|
+
def self.synchronized(object, &block)
|
7
|
+
Truffle::System.synchronized(object, &block)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module Concurrent
|
13
|
+
module ThreadSafe
|
14
|
+
module Util
|
15
|
+
def self.make_synchronized_on_rbx(klass)
|
16
|
+
klass.class_eval do
|
17
|
+
private
|
18
|
+
|
19
|
+
def _mon_initialize
|
20
|
+
@_monitor = Monitor.new unless @_monitor # avoid double initialisation
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.new(*args)
|
24
|
+
obj = super(*args)
|
25
|
+
obj.send(:_mon_initialize)
|
26
|
+
obj
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
klass.superclass.instance_methods(false).each do |method|
|
31
|
+
case method
|
32
|
+
when :new_range, :new_reserved
|
33
|
+
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
34
|
+
def #{method}(*args)
|
35
|
+
obj = super
|
36
|
+
obj.send(:_mon_initialize)
|
37
|
+
obj
|
38
|
+
end
|
39
|
+
RUBY
|
40
|
+
else
|
41
|
+
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
42
|
+
def #{method}(*args)
|
43
|
+
monitor = @_monitor
|
44
|
+
monitor or raise("BUG: Internal monitor was not properly initialized. Please report this to the concurrent-ruby developers.")
|
45
|
+
monitor.synchronize { super }
|
46
|
+
end
|
47
|
+
RUBY
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.make_synchronized_on_truffleruby(klass)
|
53
|
+
klass.superclass.instance_methods(false).each do |method|
|
54
|
+
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
55
|
+
def #{method}(*args, &block)
|
56
|
+
TruffleRuby.synchronized(self) { super(*args, &block) }
|
57
|
+
end
|
58
|
+
RUBY
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -86,15 +86,20 @@ module Concurrent
|
|
86
86
|
# @!visibility private
|
87
87
|
class Cell < Concurrent::AtomicReference
|
88
88
|
|
89
|
-
# TODO: this only adds padding after the :value slot, need to find a way to add padding before the slot
|
90
|
-
# @!visibility private
|
91
|
-
attr_reader *(12.times.collect{ |i| "padding_#{i}".to_sym })
|
92
|
-
|
93
89
|
alias_method :cas, :compare_and_set
|
94
90
|
|
95
91
|
def cas_computed
|
96
92
|
cas(current_value = value, yield(current_value))
|
97
93
|
end
|
94
|
+
|
95
|
+
# @!visibility private
|
96
|
+
def self.padding
|
97
|
+
# TODO: this only adds padding after the :value slot, need to find a way to add padding before the slot
|
98
|
+
# TODO (pitr-ch 28-Jul-2018): the padding instance vars may not be created
|
99
|
+
# hide from yardoc in a method
|
100
|
+
attr_reader *(12.times.collect{ |i| "padding_#{i}".to_sym })
|
101
|
+
end
|
102
|
+
padding
|
98
103
|
end
|
99
104
|
|
100
105
|
extend Volatile
|
@@ -126,6 +126,7 @@ module Concurrent
|
|
126
126
|
# task = Concurrent::TimerTask.new(execution_interval: 1, timeout_interval: 1){ 42 }
|
127
127
|
# task.add_observer(TaskObserver.new)
|
128
128
|
# task.execute
|
129
|
+
# sleep 4
|
129
130
|
#
|
130
131
|
# #=> (2013-10-13 19:08:58 -0400) Execution successfully returned 42
|
131
132
|
# #=> (2013-10-13 19:08:59 -0400) Execution successfully returned 42
|
@@ -164,7 +165,7 @@ module Concurrent
|
|
164
165
|
|
165
166
|
# Create a new TimerTask with the given task and configuration.
|
166
167
|
#
|
167
|
-
# @!macro
|
168
|
+
# @!macro timer_task_initialize
|
168
169
|
# @param [Hash] opts the options defining task execution.
|
169
170
|
# @option opts [Integer] :execution_interval number of seconds between
|
170
171
|
# task executions (default: EXECUTION_INTERVAL)
|
@@ -189,6 +190,7 @@ module Concurrent
|
|
189
190
|
def initialize(opts = {}, &task)
|
190
191
|
raise ArgumentError.new('no block given') unless block_given?
|
191
192
|
super
|
193
|
+
set_deref_options opts
|
192
194
|
end
|
193
195
|
|
194
196
|
# Is the executor running?
|
@@ -280,6 +282,7 @@ module Concurrent
|
|
280
282
|
@run_now = opts[:now] || opts[:run_now]
|
281
283
|
@executor = Concurrent::SafeTaskExecutor.new(task)
|
282
284
|
@running = Concurrent::AtomicBoolean.new(false)
|
285
|
+
@value = nil
|
283
286
|
|
284
287
|
self.observers = Collection::CopyOnNotifyObserverSet.new
|
285
288
|
end
|
@@ -305,7 +308,7 @@ module Concurrent
|
|
305
308
|
# @!visibility private
|
306
309
|
def execute_task(completion)
|
307
310
|
return nil unless @running.true?
|
308
|
-
ScheduledTask.execute(
|
311
|
+
ScheduledTask.execute(timeout_interval, args: [completion], &method(:timeout_task))
|
309
312
|
_success, value, reason = @executor.execute(self)
|
310
313
|
if completion.try?
|
311
314
|
self.value = value
|
data/lib/concurrent/tuple.rb
CHANGED
@@ -24,7 +24,7 @@ module Concurrent
|
|
24
24
|
attr_reader :size
|
25
25
|
|
26
26
|
# @!visibility private
|
27
|
-
Tuple = defined?(Rubinius::Tuple) ? Rubinius::Tuple : Array
|
27
|
+
Tuple = defined?(Rubinius::Tuple) ? Rubinius::Tuple : ::Array
|
28
28
|
private_constant :Tuple
|
29
29
|
|
30
30
|
# Create a new tuple of the given size.
|
data/lib/concurrent/tvar.rb
CHANGED
@@ -8,7 +8,7 @@ module Concurrent
|
|
8
8
|
#
|
9
9
|
# @!macro thread_safe_variable_comparison
|
10
10
|
#
|
11
|
-
# {include:file:
|
11
|
+
# {include:file:docs-source/tvar.md}
|
12
12
|
class TVar < Synchronization::Object
|
13
13
|
safe_initialization!
|
14
14
|
|
@@ -162,7 +162,7 @@ module Concurrent
|
|
162
162
|
|
163
163
|
class Transaction
|
164
164
|
|
165
|
-
ABORTED = Object.new
|
165
|
+
ABORTED = ::Object.new
|
166
166
|
|
167
167
|
ReadLogEntry = Struct.new(:tvar, :version)
|
168
168
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative 'engine'
|
2
|
+
|
3
|
+
if Concurrent.ruby_version :<, 2, 0, 0
|
4
|
+
# @!visibility private
|
5
|
+
module Kernel
|
6
|
+
def __dir__
|
7
|
+
File.dirname __FILE__
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# @!visibility private
|
12
|
+
class LoadError < ScriptError
|
13
|
+
def path
|
14
|
+
message.split(' -- ').last
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -16,7 +16,7 @@ module Concurrent
|
|
16
16
|
end
|
17
17
|
|
18
18
|
# Add a handler to be run at `Kernel#at_exit`
|
19
|
-
# @param [Object] handler_id optionally provide an id, if
|
19
|
+
# @param [Object] handler_id optionally provide an id, if already present, handler is replaced
|
20
20
|
# @yield the handler
|
21
21
|
# @return id of the handler
|
22
22
|
def add(handler_id = nil, &handler)
|