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.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +542 -0
  3. data/Gemfile +37 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +404 -0
  6. data/Rakefile +307 -0
  7. data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
  9. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
  10. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
  11. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
  12. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +189 -0
  13. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +307 -0
  14. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
  15. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
  16. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
  17. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
  18. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
  19. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
  20. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
  21. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
  22. data/lib/concurrent-ruby/concurrent/agent.rb +587 -0
  23. data/lib/concurrent-ruby/concurrent/array.rb +66 -0
  24. data/lib/concurrent-ruby/concurrent/async.rb +449 -0
  25. data/lib/concurrent-ruby/concurrent/atom.rb +222 -0
  26. data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +66 -0
  27. data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +126 -0
  28. data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +143 -0
  29. data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +164 -0
  30. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +205 -0
  31. data/lib/concurrent-ruby/concurrent/atomic/count_down_latch.rb +100 -0
  32. data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +128 -0
  33. data/lib/concurrent-ruby/concurrent/atomic/event.rb +109 -0
  34. data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +42 -0
  35. data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +37 -0
  36. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +62 -0
  37. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +75 -0
  38. data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +44 -0
  39. data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +131 -0
  40. data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +254 -0
  41. data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +377 -0
  42. data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +181 -0
  43. data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +166 -0
  44. data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +104 -0
  45. data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +56 -0
  46. data/lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb +28 -0
  47. data/lib/concurrent-ruby/concurrent/atomics.rb +10 -0
  48. data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +107 -0
  49. data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +111 -0
  50. data/lib/concurrent-ruby/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
  51. data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +158 -0
  52. data/lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
  53. data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +66 -0
  54. data/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb +140 -0
  55. data/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb +82 -0
  56. data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
  57. data/lib/concurrent-ruby/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
  58. data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +160 -0
  59. data/lib/concurrent-ruby/concurrent/concern/deprecation.rb +34 -0
  60. data/lib/concurrent-ruby/concurrent/concern/dereferenceable.rb +73 -0
  61. data/lib/concurrent-ruby/concurrent/concern/logging.rb +32 -0
  62. data/lib/concurrent-ruby/concurrent/concern/obligation.rb +220 -0
  63. data/lib/concurrent-ruby/concurrent/concern/observable.rb +110 -0
  64. data/lib/concurrent-ruby/concurrent/configuration.rb +188 -0
  65. data/lib/concurrent-ruby/concurrent/constants.rb +8 -0
  66. data/lib/concurrent-ruby/concurrent/dataflow.rb +81 -0
  67. data/lib/concurrent-ruby/concurrent/delay.rb +199 -0
  68. data/lib/concurrent-ruby/concurrent/errors.rb +69 -0
  69. data/lib/concurrent-ruby/concurrent/exchanger.rb +352 -0
  70. data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +131 -0
  71. data/lib/concurrent-ruby/concurrent/executor/cached_thread_pool.rb +62 -0
  72. data/lib/concurrent-ruby/concurrent/executor/executor_service.rb +185 -0
  73. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +220 -0
  74. data/lib/concurrent-ruby/concurrent/executor/immediate_executor.rb +66 -0
  75. data/lib/concurrent-ruby/concurrent/executor/indirect_immediate_executor.rb +44 -0
  76. data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +103 -0
  77. data/lib/concurrent-ruby/concurrent/executor/java_single_thread_executor.rb +30 -0
  78. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +140 -0
  79. data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +82 -0
  80. data/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb +21 -0
  81. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +368 -0
  82. data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +35 -0
  83. data/lib/concurrent-ruby/concurrent/executor/serial_executor_service.rb +34 -0
  84. data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +107 -0
  85. data/lib/concurrent-ruby/concurrent/executor/serialized_execution_delegator.rb +28 -0
  86. data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +100 -0
  87. data/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb +57 -0
  88. data/lib/concurrent-ruby/concurrent/executor/thread_pool_executor.rb +88 -0
  89. data/lib/concurrent-ruby/concurrent/executor/timer_set.rb +172 -0
  90. data/lib/concurrent-ruby/concurrent/executors.rb +20 -0
  91. data/lib/concurrent-ruby/concurrent/future.rb +141 -0
  92. data/lib/concurrent-ruby/concurrent/hash.rb +59 -0
  93. data/lib/concurrent-ruby/concurrent/immutable_struct.rb +101 -0
  94. data/lib/concurrent-ruby/concurrent/ivar.rb +207 -0
  95. data/lib/concurrent-ruby/concurrent/map.rb +346 -0
  96. data/lib/concurrent-ruby/concurrent/maybe.rb +229 -0
  97. data/lib/concurrent-ruby/concurrent/mutable_struct.rb +239 -0
  98. data/lib/concurrent-ruby/concurrent/mvar.rb +242 -0
  99. data/lib/concurrent-ruby/concurrent/options.rb +42 -0
  100. data/lib/concurrent-ruby/concurrent/promise.rb +580 -0
  101. data/lib/concurrent-ruby/concurrent/promises.rb +2167 -0
  102. data/lib/concurrent-ruby/concurrent/re_include.rb +58 -0
  103. data/lib/concurrent-ruby/concurrent/scheduled_task.rb +331 -0
  104. data/lib/concurrent-ruby/concurrent/set.rb +74 -0
  105. data/lib/concurrent-ruby/concurrent/settable_struct.rb +139 -0
  106. data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +98 -0
  107. data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +24 -0
  108. data/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb +171 -0
  109. data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +60 -0
  110. data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  111. data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +45 -0
  112. data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +36 -0
  113. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +72 -0
  114. data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +44 -0
  115. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +88 -0
  116. data/lib/concurrent-ruby/concurrent/synchronization/object.rb +183 -0
  117. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +71 -0
  118. data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +49 -0
  119. data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +47 -0
  120. data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +36 -0
  121. data/lib/concurrent-ruby/concurrent/synchronization.rb +30 -0
  122. data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +50 -0
  123. data/lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb +74 -0
  124. data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
  125. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +88 -0
  126. data/lib/concurrent-ruby/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
  127. data/lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb +246 -0
  128. data/lib/concurrent-ruby/concurrent/thread_safe/util/volatile.rb +75 -0
  129. data/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
  130. data/lib/concurrent-ruby/concurrent/thread_safe/util.rb +16 -0
  131. data/lib/concurrent-ruby/concurrent/timer_task.rb +311 -0
  132. data/lib/concurrent-ruby/concurrent/tuple.rb +86 -0
  133. data/lib/concurrent-ruby/concurrent/tvar.rb +221 -0
  134. data/lib/concurrent-ruby/concurrent/utility/engine.rb +56 -0
  135. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +90 -0
  136. data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +79 -0
  137. data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +53 -0
  138. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +130 -0
  139. data/lib/concurrent-ruby/concurrent/version.rb +3 -0
  140. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -0
  141. data/lib/concurrent-ruby/concurrent.rb +134 -0
  142. metadata +192 -0
@@ -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,71 @@
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
+ def initialize_copy(other)
16
+ super
17
+ @__Waiters__ = []
18
+ @__owner__ = nil
19
+ end
20
+
21
+ protected
22
+
23
+ def synchronize(&block)
24
+ if @__owner__ == Thread.current
25
+ yield
26
+ else
27
+ result = nil
28
+ Rubinius.synchronize(self) do
29
+ begin
30
+ @__owner__ = Thread.current
31
+ result = yield
32
+ ensure
33
+ @__owner__ = nil
34
+ end
35
+ end
36
+ result
37
+ end
38
+ end
39
+
40
+ def ns_wait(timeout = nil)
41
+ wchan = Rubinius::Channel.new
42
+
43
+ begin
44
+ @__Waiters__.push wchan
45
+ Rubinius.unlock(self)
46
+ signaled = wchan.receive_timeout timeout
47
+ ensure
48
+ Rubinius.lock(self)
49
+
50
+ if !signaled && !@__Waiters__.delete(wchan)
51
+ # we timed out, but got signaled afterwards,
52
+ # so pass that signal on to the next waiter
53
+ @__Waiters__.shift << true unless @__Waiters__.empty?
54
+ end
55
+ end
56
+
57
+ self
58
+ end
59
+
60
+ def ns_signal
61
+ @__Waiters__.shift << true unless @__Waiters__.empty?
62
+ self
63
+ end
64
+
65
+ def ns_broadcast
66
+ @__Waiters__.shift << true until @__Waiters__.empty?
67
+ self
68
+ end
69
+ end
70
+ end
71
+ 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
@@ -0,0 +1,47 @@
1
+ module Concurrent
2
+ module Synchronization
3
+
4
+ # @!visibility private
5
+ module TruffleRubyAttrVolatile
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
+
15
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
16
+ def #{name}
17
+ full_memory_barrier
18
+ #{ivar}
19
+ end
20
+
21
+ def #{name}=(value)
22
+ #{ivar} = value
23
+ full_memory_barrier
24
+ end
25
+ RUBY
26
+ end
27
+
28
+ names.map { |n| [n, :"#{n}="] }.flatten
29
+ end
30
+ end
31
+
32
+ def full_memory_barrier
33
+ TruffleRuby.full_memory_barrier
34
+ end
35
+ end
36
+
37
+ # @!visibility private
38
+ # @!macro internal_implementation_note
39
+ class TruffleRubyObject < AbstractObject
40
+ include TruffleRubyAttrVolatile
41
+
42
+ def initialize
43
+ # nothing to do
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,36 @@
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?
29
+ RbxAttrVolatile
30
+ when Concurrent.on_truffleruby?
31
+ TruffleRubyAttrVolatile
32
+ else
33
+ MriAttrVolatile
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,30 @@
1
+ require 'concurrent/utility/engine'
2
+
3
+ require 'concurrent/synchronization/abstract_object'
4
+ require 'concurrent/utility/native_extension_loader' # load native parts first
5
+ Concurrent.load_native_extensions
6
+
7
+ require 'concurrent/synchronization/mri_object'
8
+ require 'concurrent/synchronization/jruby_object'
9
+ require 'concurrent/synchronization/rbx_object'
10
+ require 'concurrent/synchronization/truffleruby_object'
11
+ require 'concurrent/synchronization/object'
12
+ require 'concurrent/synchronization/volatile'
13
+
14
+ require 'concurrent/synchronization/abstract_lockable_object'
15
+ require 'concurrent/synchronization/mutex_lockable_object'
16
+ require 'concurrent/synchronization/jruby_lockable_object'
17
+ require 'concurrent/synchronization/rbx_lockable_object'
18
+
19
+ require 'concurrent/synchronization/lockable_object'
20
+
21
+ require 'concurrent/synchronization/condition'
22
+ require 'concurrent/synchronization/lock'
23
+
24
+ module Concurrent
25
+ # {include:file:docs-source/synchronization.md}
26
+ # {include:file:docs-source/synchronization-notes.md}
27
+ module Synchronization
28
+ end
29
+ end
30
+
@@ -0,0 +1,50 @@
1
+ require 'delegate'
2
+ require 'monitor'
3
+
4
+ module Concurrent
5
+ unless defined?(SynchronizedDelegator)
6
+
7
+ # This class provides a trivial way to synchronize all calls to a given object
8
+ # by wrapping it with a `Delegator` that performs `Monitor#enter/exit` calls
9
+ # around the delegated `#send`. Example:
10
+ #
11
+ # array = [] # not thread-safe on many impls
12
+ # array = SynchronizedDelegator.new([]) # thread-safe
13
+ #
14
+ # A simple `Monitor` provides a very coarse-grained way to synchronize a given
15
+ # object, in that it will cause synchronization for methods that have no need
16
+ # for it, but this is a trivial way to get thread-safety where none may exist
17
+ # currently on some implementations.
18
+ #
19
+ # This class is currently being considered for inclusion into stdlib, via
20
+ # https://bugs.ruby-lang.org/issues/8556
21
+ #
22
+ # @!visibility private
23
+ class SynchronizedDelegator < SimpleDelegator
24
+ def setup
25
+ @old_abort = Thread.abort_on_exception
26
+ Thread.abort_on_exception = true
27
+ end
28
+
29
+ def teardown
30
+ Thread.abort_on_exception = @old_abort
31
+ end
32
+
33
+ def initialize(obj)
34
+ __setobj__(obj)
35
+ @monitor = Monitor.new
36
+ end
37
+
38
+ def method_missing(method, *args, &block)
39
+ monitor = @monitor
40
+ begin
41
+ monitor.enter
42
+ super
43
+ ensure
44
+ monitor.exit
45
+ end
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,74 @@
1
+ require 'concurrent/thread_safe/util'
2
+ require 'concurrent/thread_safe/util/striped64'
3
+
4
+ module Concurrent
5
+
6
+ # @!visibility private
7
+ module ThreadSafe
8
+
9
+ # @!visibility private
10
+ module Util
11
+
12
+ # A Ruby port of the Doug Lea's jsr166e.LondAdder class version 1.8
13
+ # available in public domain.
14
+ #
15
+ # Original source code available here:
16
+ # http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/LongAdder.java?revision=1.8
17
+ #
18
+ # One or more variables that together maintain an initially zero
19
+ # sum. When updates (method +add+) are contended across threads,
20
+ # the set of variables may grow dynamically to reduce contention.
21
+ # Method +sum+ returns the current total combined across the
22
+ # variables maintaining the sum.
23
+ #
24
+ # This class is usually preferable to single +Atomic+ reference when
25
+ # multiple threads update a common sum that is used for purposes such
26
+ # as collecting statistics, not for fine-grained synchronization
27
+ # control. Under low update contention, the two classes have similar
28
+ # characteristics. But under high contention, expected throughput of
29
+ # this class is significantly higher, at the expense of higher space
30
+ # consumption.
31
+ #
32
+ # @!visibility private
33
+ class Adder < Striped64
34
+ # Adds the given value.
35
+ def add(x)
36
+ if (current_cells = cells) || !cas_base_computed {|current_base| current_base + x}
37
+ was_uncontended = true
38
+ hash = hash_code
39
+ unless current_cells && (cell = current_cells.volatile_get_by_hash(hash)) && (was_uncontended = cell.cas_computed {|current_value| current_value + x})
40
+ retry_update(x, hash, was_uncontended) {|current_value| current_value + x}
41
+ end
42
+ end
43
+ end
44
+
45
+ def increment
46
+ add(1)
47
+ end
48
+
49
+ def decrement
50
+ add(-1)
51
+ end
52
+
53
+ # Returns the current sum. The returned value is _NOT_ an
54
+ # atomic snapshot: Invocation in the absence of concurrent
55
+ # updates returns an accurate result, but concurrent updates that
56
+ # occur while the sum is being calculated might not be
57
+ # incorporated.
58
+ def sum
59
+ x = base
60
+ if current_cells = cells
61
+ current_cells.each do |cell|
62
+ x += cell.value if cell
63
+ end
64
+ end
65
+ x
66
+ end
67
+
68
+ def reset
69
+ internal_reset(0)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,118 @@
1
+ require 'concurrent/thread_safe/util'
2
+ require 'concurrent/thread_safe/util/volatile'
3
+
4
+ module Concurrent
5
+
6
+ # @!visibility private
7
+ module ThreadSafe
8
+
9
+ # @!visibility private
10
+ module Util
11
+
12
+ # Provides a cheapest possible (mainly in terms of memory usage) +Mutex+
13
+ # with the +ConditionVariable+ bundled in.
14
+ #
15
+ # Usage:
16
+ # class A
17
+ # include CheapLockable
18
+ #
19
+ # def do_exlusively
20
+ # cheap_synchronize { yield }
21
+ # end
22
+ #
23
+ # def wait_for_something
24
+ # cheap_synchronize do
25
+ # cheap_wait until resource_available?
26
+ # do_something
27
+ # cheap_broadcast # wake up others
28
+ # end
29
+ # end
30
+ # end
31
+ #
32
+ # @!visibility private
33
+ module CheapLockable
34
+ private
35
+ engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
36
+ if engine == 'rbx'
37
+ # Making use of the Rubinius' ability to lock via object headers to avoid the overhead of the extra Mutex objects.
38
+ def cheap_synchronize
39
+ Rubinius.lock(self)
40
+ begin
41
+ yield
42
+ ensure
43
+ Rubinius.unlock(self)
44
+ end
45
+ end
46
+
47
+ def cheap_wait
48
+ wchan = Rubinius::Channel.new
49
+
50
+ begin
51
+ waiters = @waiters ||= []
52
+ waiters.push wchan
53
+ Rubinius.unlock(self)
54
+ signaled = wchan.receive_timeout nil
55
+ ensure
56
+ Rubinius.lock(self)
57
+
58
+ unless signaled or waiters.delete(wchan)
59
+ # we timed out, but got signaled afterwards (e.g. while waiting to
60
+ # acquire @lock), so pass that signal on to the next waiter
61
+ waiters.shift << true unless waiters.empty?
62
+ end
63
+ end
64
+
65
+ self
66
+ end
67
+
68
+ def cheap_broadcast
69
+ waiters = @waiters ||= []
70
+ waiters.shift << true until waiters.empty?
71
+ self
72
+ end
73
+ elsif engine == 'jruby'
74
+ # Use Java's native synchronized (this) { wait(); notifyAll(); } to avoid the overhead of the extra Mutex objects
75
+ require 'jruby'
76
+
77
+ def cheap_synchronize
78
+ JRuby.reference0(self).synchronized { yield }
79
+ end
80
+
81
+ def cheap_wait
82
+ JRuby.reference0(self).wait
83
+ end
84
+
85
+ def cheap_broadcast
86
+ JRuby.reference0(self).notify_all
87
+ end
88
+ else
89
+ require 'thread'
90
+
91
+ extend Volatile
92
+ attr_volatile :mutex
93
+
94
+ # Non-reentrant Mutex#syncrhonize
95
+ def cheap_synchronize
96
+ true until (my_mutex = mutex) || cas_mutex(nil, my_mutex = Mutex.new)
97
+ my_mutex.synchronize { yield }
98
+ end
99
+
100
+ # Releases this object's +cheap_synchronize+ lock and goes to sleep waiting for other threads to +cheap_broadcast+, reacquires the lock on wakeup.
101
+ # Must only be called in +cheap_broadcast+'s block.
102
+ def cheap_wait
103
+ conditional_variable = @conditional_variable ||= ConditionVariable.new
104
+ conditional_variable.wait(mutex)
105
+ end
106
+
107
+ # Wakes up all threads waiting for this object's +cheap_synchronize+ lock.
108
+ # Must only be called in +cheap_broadcast+'s block.
109
+ def cheap_broadcast
110
+ if conditional_variable = @conditional_variable
111
+ conditional_variable.broadcast
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end