concurrent-ruby 1.1.5
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 +478 -0
- data/Gemfile +41 -0
- data/LICENSE.md +23 -0
- data/README.md +381 -0
- data/Rakefile +327 -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 +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.rb +1 -0
- data/lib/concurrent.rb +134 -0
- data/lib/concurrent/agent.rb +587 -0
- data/lib/concurrent/array.rb +66 -0
- data/lib/concurrent/async.rb +459 -0
- data/lib/concurrent/atom.rb +222 -0
- data/lib/concurrent/atomic/abstract_thread_local_var.rb +66 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +126 -0
- data/lib/concurrent/atomic/atomic_fixnum.rb +143 -0
- data/lib/concurrent/atomic/atomic_markable_reference.rb +164 -0
- data/lib/concurrent/atomic/atomic_reference.rb +204 -0
- data/lib/concurrent/atomic/count_down_latch.rb +100 -0
- data/lib/concurrent/atomic/cyclic_barrier.rb +128 -0
- data/lib/concurrent/atomic/event.rb +109 -0
- data/lib/concurrent/atomic/java_count_down_latch.rb +42 -0
- data/lib/concurrent/atomic/java_thread_local_var.rb +37 -0
- data/lib/concurrent/atomic/mutex_atomic_boolean.rb +62 -0
- data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +75 -0
- data/lib/concurrent/atomic/mutex_count_down_latch.rb +44 -0
- data/lib/concurrent/atomic/mutex_semaphore.rb +115 -0
- data/lib/concurrent/atomic/read_write_lock.rb +254 -0
- data/lib/concurrent/atomic/reentrant_read_write_lock.rb +379 -0
- data/lib/concurrent/atomic/ruby_thread_local_var.rb +161 -0
- data/lib/concurrent/atomic/semaphore.rb +145 -0
- data/lib/concurrent/atomic/thread_local_var.rb +104 -0
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +56 -0
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +28 -0
- data/lib/concurrent/atomics.rb +10 -0
- data/lib/concurrent/collection/copy_on_notify_observer_set.rb +107 -0
- data/lib/concurrent/collection/copy_on_write_observer_set.rb +111 -0
- data/lib/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
- data/lib/concurrent/collection/lock_free_stack.rb +158 -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 +140 -0
- data/lib/concurrent/collection/map/synchronized_map_backend.rb +82 -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/deprecation.rb +34 -0
- data/lib/concurrent/concern/dereferenceable.rb +73 -0
- data/lib/concurrent/concern/logging.rb +32 -0
- data/lib/concurrent/concern/obligation.rb +220 -0
- data/lib/concurrent/concern/observable.rb +110 -0
- data/lib/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent/configuration.rb +184 -0
- data/lib/concurrent/constants.rb +8 -0
- data/lib/concurrent/dataflow.rb +81 -0
- data/lib/concurrent/delay.rb +199 -0
- data/lib/concurrent/errors.rb +69 -0
- data/lib/concurrent/exchanger.rb +352 -0
- data/lib/concurrent/executor/abstract_executor_service.rb +134 -0
- data/lib/concurrent/executor/cached_thread_pool.rb +62 -0
- data/lib/concurrent/executor/executor_service.rb +185 -0
- data/lib/concurrent/executor/fixed_thread_pool.rb +206 -0
- data/lib/concurrent/executor/immediate_executor.rb +66 -0
- data/lib/concurrent/executor/indirect_immediate_executor.rb +44 -0
- data/lib/concurrent/executor/java_executor_service.rb +91 -0
- data/lib/concurrent/executor/java_single_thread_executor.rb +29 -0
- data/lib/concurrent/executor/java_thread_pool_executor.rb +123 -0
- data/lib/concurrent/executor/ruby_executor_service.rb +78 -0
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +22 -0
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +362 -0
- data/lib/concurrent/executor/safe_task_executor.rb +35 -0
- data/lib/concurrent/executor/serial_executor_service.rb +34 -0
- data/lib/concurrent/executor/serialized_execution.rb +107 -0
- data/lib/concurrent/executor/serialized_execution_delegator.rb +28 -0
- data/lib/concurrent/executor/simple_executor_service.rb +100 -0
- data/lib/concurrent/executor/single_thread_executor.rb +56 -0
- data/lib/concurrent/executor/thread_pool_executor.rb +87 -0
- data/lib/concurrent/executor/timer_set.rb +173 -0
- data/lib/concurrent/executors.rb +20 -0
- data/lib/concurrent/future.rb +141 -0
- data/lib/concurrent/hash.rb +59 -0
- data/lib/concurrent/immutable_struct.rb +93 -0
- data/lib/concurrent/ivar.rb +207 -0
- data/lib/concurrent/map.rb +337 -0
- data/lib/concurrent/maybe.rb +229 -0
- data/lib/concurrent/mutable_struct.rb +229 -0
- data/lib/concurrent/mvar.rb +242 -0
- data/lib/concurrent/options.rb +42 -0
- data/lib/concurrent/promise.rb +579 -0
- data/lib/concurrent/promises.rb +2167 -0
- data/lib/concurrent/re_include.rb +58 -0
- data/lib/concurrent/scheduled_task.rb +318 -0
- data/lib/concurrent/set.rb +66 -0
- data/lib/concurrent/settable_struct.rb +129 -0
- data/lib/concurrent/synchronization.rb +30 -0
- data/lib/concurrent/synchronization/abstract_lockable_object.rb +98 -0
- data/lib/concurrent/synchronization/abstract_object.rb +24 -0
- data/lib/concurrent/synchronization/abstract_struct.rb +160 -0
- data/lib/concurrent/synchronization/condition.rb +60 -0
- data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
- data/lib/concurrent/synchronization/jruby_object.rb +45 -0
- data/lib/concurrent/synchronization/lock.rb +36 -0
- data/lib/concurrent/synchronization/lockable_object.rb +74 -0
- data/lib/concurrent/synchronization/mri_object.rb +44 -0
- data/lib/concurrent/synchronization/mutex_lockable_object.rb +76 -0
- data/lib/concurrent/synchronization/object.rb +183 -0
- data/lib/concurrent/synchronization/rbx_lockable_object.rb +65 -0
- data/lib/concurrent/synchronization/rbx_object.rb +49 -0
- data/lib/concurrent/synchronization/truffleruby_object.rb +47 -0
- data/lib/concurrent/synchronization/volatile.rb +36 -0
- data/lib/concurrent/thread_safe/synchronized_delegator.rb +50 -0
- data/lib/concurrent/thread_safe/util.rb +16 -0
- data/lib/concurrent/thread_safe/util/adder.rb +74 -0
- data/lib/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
- data/lib/concurrent/thread_safe/util/data_structures.rb +63 -0
- data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
- data/lib/concurrent/thread_safe/util/striped64.rb +246 -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 +334 -0
- data/lib/concurrent/tuple.rb +86 -0
- data/lib/concurrent/tvar.rb +258 -0
- data/lib/concurrent/utility/at_exit.rb +97 -0
- data/lib/concurrent/utility/engine.rb +56 -0
- data/lib/concurrent/utility/monotonic_time.rb +58 -0
- data/lib/concurrent/utility/native_extension_loader.rb +79 -0
- data/lib/concurrent/utility/native_integer.rb +53 -0
- data/lib/concurrent/utility/processor_counter.rb +158 -0
- data/lib/concurrent/version.rb +3 -0
- metadata +193 -0
@@ -0,0 +1,74 @@
|
|
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
|
+
MonitorLockableObject
|
9
|
+
when Concurrent.on_cruby? && Concurrent.ruby_version(:>, 1, 9, 3)
|
10
|
+
MutexLockableObject
|
11
|
+
when Concurrent.on_jruby?
|
12
|
+
JRubyLockableObject
|
13
|
+
when Concurrent.on_rbx?
|
14
|
+
RbxLockableObject
|
15
|
+
when Concurrent.on_truffleruby?
|
16
|
+
MutexLockableObject
|
17
|
+
else
|
18
|
+
warn 'Possibly unsupported Ruby implementation'
|
19
|
+
MonitorLockableObject
|
20
|
+
end
|
21
|
+
private_constant :LockableObjectImplementation
|
22
|
+
|
23
|
+
# Safe synchronization under any Ruby implementation.
|
24
|
+
# It provides methods like {#synchronize}, {#wait}, {#signal} and {#broadcast}.
|
25
|
+
# Provides a single layer which can improve its implementation over time without changes needed to
|
26
|
+
# the classes using it. Use {Synchronization::Object} not this abstract class.
|
27
|
+
#
|
28
|
+
# @note this object does not support usage together with
|
29
|
+
# [`Thread#wakeup`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-wakeup)
|
30
|
+
# and [`Thread#raise`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-raise).
|
31
|
+
# `Thread#sleep` and `Thread#wakeup` will work as expected but mixing `Synchronization::Object#wait` and
|
32
|
+
# `Thread#wakeup` will not work on all platforms.
|
33
|
+
#
|
34
|
+
# @see Event implementation as an example of this class use
|
35
|
+
#
|
36
|
+
# @example simple
|
37
|
+
# class AnClass < Synchronization::Object
|
38
|
+
# def initialize
|
39
|
+
# super
|
40
|
+
# synchronize { @value = 'asd' }
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# def value
|
44
|
+
# synchronize { @value }
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# @!visibility private
|
49
|
+
class LockableObject < LockableObjectImplementation
|
50
|
+
|
51
|
+
# TODO (pitr 12-Sep-2015): make private for c-r, prohibit subclassing
|
52
|
+
# TODO (pitr 12-Sep-2015): we inherit too much ourselves :/
|
53
|
+
|
54
|
+
# @!method initialize(*args, &block)
|
55
|
+
# @!macro synchronization_object_method_initialize
|
56
|
+
|
57
|
+
# @!method synchronize
|
58
|
+
# @!macro synchronization_object_method_synchronize
|
59
|
+
|
60
|
+
# @!method wait_until(timeout = nil, &condition)
|
61
|
+
# @!macro synchronization_object_method_ns_wait_until
|
62
|
+
|
63
|
+
# @!method wait(timeout = nil)
|
64
|
+
# @!macro synchronization_object_method_ns_wait
|
65
|
+
|
66
|
+
# @!method signal
|
67
|
+
# @!macro synchronization_object_method_ns_signal
|
68
|
+
|
69
|
+
# @!method broadcast
|
70
|
+
# @!macro synchronization_object_method_ns_broadcast
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
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,76 @@
|
|
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
|
+
protected
|
36
|
+
|
37
|
+
def synchronize
|
38
|
+
if @__Lock__.owned?
|
39
|
+
yield
|
40
|
+
else
|
41
|
+
@__Lock__.synchronize { yield }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def ns_wait(timeout = nil)
|
46
|
+
@__Condition__.wait @__Lock__, timeout
|
47
|
+
self
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# @!visibility private
|
52
|
+
# @!macro internal_implementation_note
|
53
|
+
class MonitorLockableObject < AbstractLockableObject
|
54
|
+
include ConditionSignalling
|
55
|
+
|
56
|
+
safe_initialization!
|
57
|
+
|
58
|
+
def initialize(*defaults)
|
59
|
+
super(*defaults)
|
60
|
+
@__Lock__ = ::Monitor.new
|
61
|
+
@__Condition__ = @__Lock__.new_cond
|
62
|
+
end
|
63
|
+
|
64
|
+
protected
|
65
|
+
|
66
|
+
def synchronize # TODO may be a problem with lock.synchronize { lock.wait }
|
67
|
+
@__Lock__.synchronize { yield }
|
68
|
+
end
|
69
|
+
|
70
|
+
def ns_wait(timeout = nil)
|
71
|
+
@__Condition__.wait timeout
|
72
|
+
self
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
# @!visibility private
|
5
|
+
# @!macro internal_implementation_note
|
6
|
+
ObjectImplementation = case
|
7
|
+
when Concurrent.on_cruby?
|
8
|
+
MriObject
|
9
|
+
when Concurrent.on_jruby?
|
10
|
+
JRubyObject
|
11
|
+
when Concurrent.on_rbx?
|
12
|
+
RbxObject
|
13
|
+
when Concurrent.on_truffleruby?
|
14
|
+
TruffleRubyObject
|
15
|
+
else
|
16
|
+
warn 'Possibly unsupported Ruby implementation'
|
17
|
+
MriObject
|
18
|
+
end
|
19
|
+
private_constant :ObjectImplementation
|
20
|
+
|
21
|
+
# Abstract object providing final, volatile, ans CAS extensions to build other concurrent abstractions.
|
22
|
+
# - final instance variables see {Object.safe_initialization!}
|
23
|
+
# - volatile instance variables see {Object.attr_volatile}
|
24
|
+
# - volatile instance variables see {Object.attr_atomic}
|
25
|
+
class Object < ObjectImplementation
|
26
|
+
# TODO make it a module if possible
|
27
|
+
|
28
|
+
# @!method self.attr_volatile(*names)
|
29
|
+
# Creates methods for reading and writing (as `attr_accessor` does) to a instance variable with
|
30
|
+
# volatile (Java) semantic. The instance variable should be accessed only through generated methods.
|
31
|
+
#
|
32
|
+
# @param [::Array<Symbol>] names of the instance variables to be volatile
|
33
|
+
# @return [::Array<Symbol>] names of defined method names
|
34
|
+
|
35
|
+
# Has to be called by children.
|
36
|
+
def initialize
|
37
|
+
super
|
38
|
+
__initialize_atomic_fields__
|
39
|
+
end
|
40
|
+
|
41
|
+
# By calling this method on a class, it and all its children are marked to be constructed safely. Meaning that
|
42
|
+
# all writes (ivar initializations) are made visible to all readers of newly constructed object. It ensures
|
43
|
+
# same behaviour as Java's final fields.
|
44
|
+
# @example
|
45
|
+
# class AClass < Concurrent::Synchronization::Object
|
46
|
+
# safe_initialization!
|
47
|
+
#
|
48
|
+
# def initialize
|
49
|
+
# @AFinalValue = 'value' # published safely, does not have to be synchronized
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
# @return [true]
|
53
|
+
def self.safe_initialization!
|
54
|
+
# define only once, and not again in children
|
55
|
+
return if safe_initialization?
|
56
|
+
|
57
|
+
# @!visibility private
|
58
|
+
def self.new(*args, &block)
|
59
|
+
object = super(*args, &block)
|
60
|
+
ensure
|
61
|
+
object.full_memory_barrier if object
|
62
|
+
end
|
63
|
+
|
64
|
+
@safe_initialization = true
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return [true, false] if this class is safely initialized.
|
68
|
+
def self.safe_initialization?
|
69
|
+
@safe_initialization = false unless defined? @safe_initialization
|
70
|
+
@safe_initialization || (superclass.respond_to?(:safe_initialization?) && superclass.safe_initialization?)
|
71
|
+
end
|
72
|
+
|
73
|
+
# For testing purposes, quite slow. Injects assert code to new method which will raise if class instance contains
|
74
|
+
# any instance variables with CamelCase names and isn't {.safe_initialization?}.
|
75
|
+
# @raise when offend found
|
76
|
+
# @return [true]
|
77
|
+
def self.ensure_safe_initialization_when_final_fields_are_present
|
78
|
+
Object.class_eval do
|
79
|
+
def self.new(*args, &block)
|
80
|
+
object = super(*args, &block)
|
81
|
+
ensure
|
82
|
+
has_final_field = object.instance_variables.any? { |v| v.to_s =~ /^@[A-Z]/ }
|
83
|
+
if has_final_field && !safe_initialization?
|
84
|
+
raise "there was an instance of #{object.class} with final field but not marked with safe_initialization!"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
true
|
89
|
+
end
|
90
|
+
|
91
|
+
# Creates methods for reading and writing to a instance variable with
|
92
|
+
# volatile (Java) semantic as {.attr_volatile} does.
|
93
|
+
# The instance variable should be accessed oly through generated methods.
|
94
|
+
# This method generates following methods: `value`, `value=(new_value) #=> new_value`,
|
95
|
+
# `swap_value(new_value) #=> old_value`,
|
96
|
+
# `compare_and_set_value(expected, value) #=> true || false`, `update_value(&block)`.
|
97
|
+
# @param [::Array<Symbol>] names of the instance variables to be volatile with CAS.
|
98
|
+
# @return [::Array<Symbol>] names of defined method names.
|
99
|
+
# @!macro attr_atomic
|
100
|
+
# @!method $1
|
101
|
+
# @return [Object] The $1.
|
102
|
+
# @!method $1=(new_$1)
|
103
|
+
# Set the $1.
|
104
|
+
# @return [Object] new_$1.
|
105
|
+
# @!method swap_$1(new_$1)
|
106
|
+
# Set the $1 to new_$1 and return the old $1.
|
107
|
+
# @return [Object] old $1
|
108
|
+
# @!method compare_and_set_$1(expected_$1, new_$1)
|
109
|
+
# Sets the $1 to new_$1 if the current $1 is expected_$1
|
110
|
+
# @return [true, false]
|
111
|
+
# @!method update_$1(&block)
|
112
|
+
# Updates the $1 using the block.
|
113
|
+
# @yield [Object] Calculate a new $1 using given (old) $1
|
114
|
+
# @yieldparam [Object] old $1
|
115
|
+
# @return [Object] new $1
|
116
|
+
def self.attr_atomic(*names)
|
117
|
+
@__atomic_fields__ ||= []
|
118
|
+
@__atomic_fields__ += names
|
119
|
+
safe_initialization!
|
120
|
+
define_initialize_atomic_fields
|
121
|
+
|
122
|
+
names.each do |name|
|
123
|
+
ivar = :"@Atomic#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }}"
|
124
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
125
|
+
def #{name}
|
126
|
+
#{ivar}.get
|
127
|
+
end
|
128
|
+
|
129
|
+
def #{name}=(value)
|
130
|
+
#{ivar}.set value
|
131
|
+
end
|
132
|
+
|
133
|
+
def swap_#{name}(value)
|
134
|
+
#{ivar}.swap value
|
135
|
+
end
|
136
|
+
|
137
|
+
def compare_and_set_#{name}(expected, value)
|
138
|
+
#{ivar}.compare_and_set expected, value
|
139
|
+
end
|
140
|
+
|
141
|
+
def update_#{name}(&block)
|
142
|
+
#{ivar}.update(&block)
|
143
|
+
end
|
144
|
+
RUBY
|
145
|
+
end
|
146
|
+
names.flat_map { |n| [n, :"#{n}=", :"swap_#{n}", :"compare_and_set_#{n}", :"update_#{n}"] }
|
147
|
+
end
|
148
|
+
|
149
|
+
# @param [true, false] inherited should inherited volatile with CAS fields be returned?
|
150
|
+
# @return [::Array<Symbol>] Returns defined volatile with CAS fields on this class.
|
151
|
+
def self.atomic_attributes(inherited = true)
|
152
|
+
@__atomic_fields__ ||= []
|
153
|
+
((superclass.atomic_attributes if superclass.respond_to?(:atomic_attributes) && inherited) || []) + @__atomic_fields__
|
154
|
+
end
|
155
|
+
|
156
|
+
# @return [true, false] is the attribute with name atomic?
|
157
|
+
def self.atomic_attribute?(name)
|
158
|
+
atomic_attributes.include? name
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
|
163
|
+
def self.define_initialize_atomic_fields
|
164
|
+
assignments = @__atomic_fields__.map do |name|
|
165
|
+
"@Atomic#{name.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }} = Concurrent::AtomicReference.new(nil)"
|
166
|
+
end.join("\n")
|
167
|
+
|
168
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
169
|
+
def __initialize_atomic_fields__
|
170
|
+
super
|
171
|
+
#{assignments}
|
172
|
+
end
|
173
|
+
RUBY
|
174
|
+
end
|
175
|
+
|
176
|
+
private_class_method :define_initialize_atomic_fields
|
177
|
+
|
178
|
+
def __initialize_atomic_fields__
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
# @!visibility private
|
5
|
+
# @!macro internal_implementation_note
|
6
|
+
class RbxLockableObject < AbstractLockableObject
|
7
|
+
safe_initialization!
|
8
|
+
|
9
|
+
def initialize(*defaults)
|
10
|
+
super(*defaults)
|
11
|
+
@__Waiters__ = []
|
12
|
+
@__owner__ = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def synchronize(&block)
|
18
|
+
if @__owner__ == Thread.current
|
19
|
+
yield
|
20
|
+
else
|
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
|
+
end
|
30
|
+
result
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def ns_wait(timeout = nil)
|
35
|
+
wchan = Rubinius::Channel.new
|
36
|
+
|
37
|
+
begin
|
38
|
+
@__Waiters__.push wchan
|
39
|
+
Rubinius.unlock(self)
|
40
|
+
signaled = wchan.receive_timeout timeout
|
41
|
+
ensure
|
42
|
+
Rubinius.lock(self)
|
43
|
+
|
44
|
+
if !signaled && !@__Waiters__.delete(wchan)
|
45
|
+
# we timed out, but got signaled afterwards,
|
46
|
+
# so pass that signal on to the next waiter
|
47
|
+
@__Waiters__.shift << true unless @__Waiters__.empty?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def ns_signal
|
55
|
+
@__Waiters__.shift << true unless @__Waiters__.empty?
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def ns_broadcast
|
60
|
+
@__Waiters__.shift << true until @__Waiters__.empty?
|
61
|
+
self
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
# @!visibility private
|
5
|
+
module RbxAttrVolatile
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
def attr_volatile(*names)
|
13
|
+
names.each do |name|
|
14
|
+
ivar = :"@volatile_#{name}"
|
15
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
16
|
+
def #{name}
|
17
|
+
Rubinius.memory_barrier
|
18
|
+
#{ivar}
|
19
|
+
end
|
20
|
+
|
21
|
+
def #{name}=(value)
|
22
|
+
#{ivar} = value
|
23
|
+
Rubinius.memory_barrier
|
24
|
+
end
|
25
|
+
RUBY
|
26
|
+
end
|
27
|
+
names.map { |n| [n, :"#{n}="] }.flatten
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
def full_memory_barrier
|
33
|
+
# Rubinius instance variables are not volatile so we need to insert barrier
|
34
|
+
# TODO (pitr 26-Nov-2015): check comments like ^
|
35
|
+
Rubinius.memory_barrier
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# @!visibility private
|
40
|
+
# @!macro internal_implementation_note
|
41
|
+
class RbxObject < AbstractObject
|
42
|
+
include RbxAttrVolatile
|
43
|
+
|
44
|
+
def initialize
|
45
|
+
# nothing to do
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|