concurrent-ruby 1.1.10 → 1.3.7

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 (110) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +76 -3
  3. data/Gemfile +6 -7
  4. data/README.md +28 -23
  5. data/Rakefile +90 -76
  6. data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +3 -38
  7. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +1 -1
  9. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +1 -1
  10. data/lib/concurrent-ruby/concurrent/agent.rb +4 -3
  11. data/lib/concurrent-ruby/concurrent/array.rb +3 -13
  12. data/lib/concurrent-ruby/concurrent/async.rb +1 -1
  13. data/lib/concurrent-ruby/concurrent/atom.rb +2 -2
  14. data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +5 -4
  15. data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +5 -4
  16. data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +3 -0
  17. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +90 -153
  18. data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +1 -1
  19. data/lib/concurrent-ruby/concurrent/atomic/event.rb +1 -1
  20. data/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb +109 -0
  21. data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +1 -0
  22. data/lib/concurrent-ruby/concurrent/atomic/locals.rb +189 -0
  23. data/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb +29 -0
  24. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +11 -5
  25. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +11 -5
  26. data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +1 -1
  27. data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +1 -1
  28. data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +17 -3
  29. data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +13 -4
  30. data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +6 -9
  31. data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +96 -89
  32. data/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb +37 -0
  33. data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +16 -6
  34. data/lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb +9 -1
  35. data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +1 -1
  36. data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +1 -1
  37. data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +2 -0
  38. data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +2 -2
  39. data/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb +16 -8
  40. data/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb +23 -20
  41. data/lib/concurrent-ruby/concurrent/collection/ruby_timeout_queue.rb +55 -0
  42. data/lib/concurrent-ruby/concurrent/collection/timeout_queue.rb +18 -0
  43. data/lib/concurrent-ruby/concurrent/concern/logging.rb +95 -6
  44. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  45. data/lib/concurrent-ruby/concurrent/configuration.rb +4 -87
  46. data/lib/concurrent-ruby/concurrent/delay.rb +3 -3
  47. data/lib/concurrent-ruby/concurrent/errors.rb +5 -0
  48. data/lib/concurrent-ruby/concurrent/exchanger.rb +1 -0
  49. data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +1 -1
  50. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +6 -4
  51. data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +7 -9
  52. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +7 -0
  53. data/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb +2 -0
  54. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +61 -31
  55. data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +1 -1
  56. data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +1 -1
  57. data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +4 -1
  58. data/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb +1 -1
  59. data/lib/concurrent-ruby/concurrent/executor/timer_set.rb +10 -3
  60. data/lib/concurrent-ruby/concurrent/executors.rb +0 -1
  61. data/lib/concurrent-ruby/concurrent/hash.rb +5 -12
  62. data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
  63. data/lib/concurrent-ruby/concurrent/ivar.rb +2 -1
  64. data/lib/concurrent-ruby/concurrent/map.rb +43 -39
  65. data/lib/concurrent-ruby/concurrent/maybe.rb +1 -1
  66. data/lib/concurrent-ruby/concurrent/mutable_struct.rb +1 -1
  67. data/lib/concurrent-ruby/concurrent/mvar.rb +5 -5
  68. data/lib/concurrent-ruby/concurrent/promise.rb +3 -3
  69. data/lib/concurrent-ruby/concurrent/promises.rb +40 -29
  70. data/lib/concurrent-ruby/concurrent/re_include.rb +2 -0
  71. data/lib/concurrent-ruby/concurrent/scheduled_task.rb +2 -2
  72. data/lib/concurrent-ruby/concurrent/set.rb +0 -10
  73. data/lib/concurrent-ruby/concurrent/settable_struct.rb +2 -2
  74. data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +5 -1
  75. data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +1 -3
  76. data/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb +1 -1
  77. data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +2 -0
  78. data/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb +29 -0
  79. data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +3 -1
  80. data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +2 -0
  81. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +5 -2
  82. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +6 -5
  83. data/lib/concurrent-ruby/concurrent/synchronization/object.rb +13 -45
  84. data/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb +36 -0
  85. data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +77 -12
  86. data/lib/concurrent-ruby/concurrent/synchronization.rb +1 -18
  87. data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +36 -39
  88. data/lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb +1 -1
  89. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +1 -37
  90. data/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb +1 -1
  91. data/lib/concurrent-ruby/concurrent/timer_task.rb +65 -10
  92. data/lib/concurrent-ruby/concurrent/tuple.rb +1 -5
  93. data/lib/concurrent-ruby/concurrent/tvar.rb +2 -1
  94. data/lib/concurrent-ruby/concurrent/utility/engine.rb +5 -16
  95. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +3 -74
  96. data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +8 -10
  97. data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +1 -0
  98. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +148 -58
  99. data/lib/concurrent-ruby/concurrent/version.rb +1 -1
  100. metadata +14 -19
  101. data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +0 -66
  102. data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +0 -37
  103. data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +0 -181
  104. data/lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb +0 -927
  105. data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +0 -45
  106. data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +0 -44
  107. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +0 -71
  108. data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +0 -49
  109. data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +0 -47
  110. data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +0 -118
@@ -55,12 +55,6 @@ public class SynchronizationLibrary implements Library {
55
55
  }
56
56
  }
57
57
 
58
- private static final ObjectAllocator JRUBY_OBJECT_ALLOCATOR = new ObjectAllocator() {
59
- public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
60
- return new JRubyObject(runtime, klazz);
61
- }
62
- };
63
-
64
58
  private static final ObjectAllocator OBJECT_ALLOCATOR = new ObjectAllocator() {
65
59
  public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
66
60
  return new Object(runtime, klazz);
@@ -87,10 +81,7 @@ public class SynchronizationLibrary implements Library {
87
81
  RubyModule jrubyAttrVolatileModule = synchronizationModule.defineModuleUnder("JRubyAttrVolatile");
88
82
  jrubyAttrVolatileModule.defineAnnotatedMethods(JRubyAttrVolatile.class);
89
83
 
90
- defineClass(runtime, synchronizationModule, "AbstractObject", "JRubyObject",
91
- JRubyObject.class, JRUBY_OBJECT_ALLOCATOR);
92
-
93
- defineClass(runtime, synchronizationModule, "JRubyObject", "Object",
84
+ defineClass(runtime, synchronizationModule, "AbstractObject", "Object",
94
85
  Object.class, OBJECT_ALLOCATOR);
95
86
 
96
87
  defineClass(runtime, synchronizationModule, "Object", "AbstractLockableObject",
@@ -143,8 +134,8 @@ public class SynchronizationLibrary implements Library {
143
134
  // attempt to avoid code elimination.
144
135
  private static volatile int volatileField;
145
136
 
146
- @JRubyMethod(name = "full_memory_barrier", visibility = Visibility.PUBLIC)
147
- public static IRubyObject fullMemoryBarrier(ThreadContext context, IRubyObject self) {
137
+ @JRubyMethod(name = "full_memory_barrier", visibility = Visibility.PUBLIC, module = true)
138
+ public static IRubyObject fullMemoryBarrier(ThreadContext context, IRubyObject module) {
148
139
  // Prevent reordering of ivar writes with publication of this instance
149
140
  if (!FULL_FENCE) {
150
141
  // Assuming that following volatile read and write is not eliminated it simulates fullFence.
@@ -158,9 +149,10 @@ public class SynchronizationLibrary implements Library {
158
149
  return context.nil;
159
150
  }
160
151
 
161
- @JRubyMethod(name = "instance_variable_get_volatile", visibility = Visibility.PUBLIC)
152
+ @JRubyMethod(name = "instance_variable_get_volatile", visibility = Visibility.PUBLIC, module = true)
162
153
  public static IRubyObject instanceVariableGetVolatile(
163
154
  ThreadContext context,
155
+ IRubyObject module,
164
156
  IRubyObject self,
165
157
  IRubyObject name) {
166
158
  // Ensure we ses latest value with loadFence
@@ -174,9 +166,10 @@ public class SynchronizationLibrary implements Library {
174
166
  }
175
167
  }
176
168
 
177
- @JRubyMethod(name = "instance_variable_set_volatile", visibility = Visibility.PUBLIC)
169
+ @JRubyMethod(name = "instance_variable_set_volatile", visibility = Visibility.PUBLIC, module = true)
178
170
  public static IRubyObject InstanceVariableSetVolatile(
179
171
  ThreadContext context,
172
+ IRubyObject module,
180
173
  IRubyObject self,
181
174
  IRubyObject name,
182
175
  IRubyObject value) {
@@ -195,16 +188,8 @@ public class SynchronizationLibrary implements Library {
195
188
  }
196
189
  }
197
190
 
198
- @JRubyClass(name = "JRubyObject", parent = "AbstractObject")
199
- public static class JRubyObject extends RubyObject {
200
-
201
- public JRubyObject(Ruby runtime, RubyClass metaClass) {
202
- super(runtime, metaClass);
203
- }
204
- }
205
-
206
- @JRubyClass(name = "Object", parent = "JRubyObject")
207
- public static class Object extends JRubyObject {
191
+ @JRubyClass(name = "Object", parent = "AbstractObject")
192
+ public static class Object extends RubyObject {
208
193
 
209
194
  public Object(Ruby runtime, RubyClass metaClass) {
210
195
  super(runtime, metaClass);
@@ -220,7 +205,7 @@ public class SynchronizationLibrary implements Library {
220
205
  }
221
206
 
222
207
  @JRubyClass(name = "JRubyLockableObject", parent = "AbstractLockableObject")
223
- public static class JRubyLockableObject extends JRubyObject {
208
+ public static class JRubyLockableObject extends AbstractLockableObject {
224
209
 
225
210
  public JRubyLockableObject(Ruby runtime, RubyClass metaClass) {
226
211
  super(runtime, metaClass);
@@ -481,7 +481,7 @@ public class ConcurrentHashMapV8<K, V>
481
481
  *
482
482
  * Maintaining API and serialization compatibility with previous
483
483
  * versions of this class introduces several oddities. Mainly: We
484
- * leave untouched but unused constructor arguments refering to
484
+ * leave untouched but unused constructor arguments referring to
485
485
  * concurrencyLevel. We accept a loadFactor constructor argument,
486
486
  * but apply it only to initial table capacity (which is the only
487
487
  * time that we can guarantee to honor it.) We also declare an
@@ -484,7 +484,7 @@ public class ConcurrentHashMapV8<K, V>
484
484
  *
485
485
  * Maintaining API and serialization compatibility with previous
486
486
  * versions of this class introduces several oddities. Mainly: We
487
- * leave untouched but unused constructor arguments refering to
487
+ * leave untouched but unused constructor arguments referring to
488
488
  * concurrencyLevel. We accept a loadFactor constructor argument,
489
489
  * but apply it only to initial table capacity (which is the only
490
490
  * time that we can guarantee to honor it.) We also declare an
@@ -1,9 +1,10 @@
1
1
  require 'concurrent/configuration'
2
2
  require 'concurrent/atomic/atomic_reference'
3
+ require 'concurrent/atomic/count_down_latch'
3
4
  require 'concurrent/atomic/thread_local_var'
4
5
  require 'concurrent/collection/copy_on_write_observer_set'
5
6
  require 'concurrent/concern/observable'
6
- require 'concurrent/synchronization'
7
+ require 'concurrent/synchronization/lockable_object'
7
8
 
8
9
  module Concurrent
9
10
 
@@ -370,7 +371,7 @@ module Concurrent
370
371
  # @param [Float] timeout the maximum number of seconds to wait
371
372
  # @return [Boolean] true if all actions complete before timeout
372
373
  #
373
- # @raise [Concurrent::TimeoutError] when timout is reached
374
+ # @raise [Concurrent::TimeoutError] when timeout is reached
374
375
  #
375
376
  # @!macro agent_await_warning
376
377
  def await_for!(timeout)
@@ -476,7 +477,7 @@ module Concurrent
476
477
  # @param [Array<Concurrent::Agent>] agents the Agents on which to wait
477
478
  # @return [Boolean] true if all actions complete before timeout
478
479
  #
479
- # @raise [Concurrent::TimeoutError] when timout is reached
480
+ # @raise [Concurrent::TimeoutError] when timeout is reached
480
481
  # @!macro agent_await_warning
481
482
  def await_for!(timeout, *agents)
482
483
  raise Concurrent::TimeoutError unless await_for(timeout, *agents)
@@ -21,9 +21,9 @@ module Concurrent
21
21
  # @!macro internal_implementation_note
22
22
  ArrayImplementation = case
23
23
  when Concurrent.on_cruby?
24
- # Array is thread-safe in practice because CRuby runs
25
- # threads one at a time and does not do context
26
- # switching during the execution of C functions.
24
+ # Array is not fully thread-safe on CRuby, see
25
+ # https://github.com/ruby-concurrency/concurrent-ruby/issues/929
26
+ # So we will need to add synchronization here
27
27
  ::Array
28
28
 
29
29
  when Concurrent.on_jruby?
@@ -34,16 +34,6 @@ module Concurrent
34
34
  end
35
35
  JRubyArray
36
36
 
37
- when Concurrent.on_rbx?
38
- require 'monitor'
39
- require 'concurrent/thread_safe/util/data_structures'
40
-
41
- class RbxArray < ::Array
42
- end
43
-
44
- ThreadSafe::Util.make_synchronized_on_rbx RbxArray
45
- RbxArray
46
-
47
37
  when Concurrent.on_truffleruby?
48
38
  require 'concurrent/thread_safe/util/data_structures'
49
39
 
@@ -218,7 +218,7 @@ module Concurrent
218
218
 
219
219
  # @!method self.new(*args, &block)
220
220
  #
221
- # Instanciate a new object and ensure proper initialization of the
221
+ # Instantiate a new object and ensure proper initialization of the
222
222
  # synchronization mechanisms.
223
223
  #
224
224
  # @param [Array<Object>] args Zero or more arguments to be passed to the
@@ -1,7 +1,7 @@
1
1
  require 'concurrent/atomic/atomic_reference'
2
2
  require 'concurrent/collection/copy_on_notify_observer_set'
3
3
  require 'concurrent/concern/observable'
4
- require 'concurrent/synchronization'
4
+ require 'concurrent/synchronization/object'
5
5
 
6
6
  # @!macro thread_safe_variable_comparison
7
7
  #
@@ -113,7 +113,7 @@ module Concurrent
113
113
  # @option opts [Proc] :validator (nil) Optional proc used to validate new
114
114
  # values. It must accept one and only one argument which will be the
115
115
  # intended new value. The validator will return true if the new value
116
- # is acceptable else return false (preferrably) or raise an exception.
116
+ # is acceptable else return false (preferably) or raise an exception.
117
117
  #
118
118
  # @!macro deref_options
119
119
  #
@@ -1,5 +1,6 @@
1
+ require 'concurrent/utility/native_extension_loader' # load native parts first
2
+
1
3
  require 'concurrent/atomic/mutex_atomic_boolean'
2
- require 'concurrent/synchronization'
3
4
 
4
5
  module Concurrent
5
6
 
@@ -79,10 +80,10 @@ module Concurrent
79
80
  # @!visibility private
80
81
  # @!macro internal_implementation_note
81
82
  AtomicBooleanImplementation = case
82
- when defined?(JavaAtomicBoolean)
83
- JavaAtomicBoolean
84
- when defined?(CAtomicBoolean)
83
+ when Concurrent.on_cruby? && Concurrent.c_extensions_loaded?
85
84
  CAtomicBoolean
85
+ when Concurrent.on_jruby?
86
+ JavaAtomicBoolean
86
87
  else
87
88
  MutexAtomicBoolean
88
89
  end
@@ -1,5 +1,6 @@
1
+ require 'concurrent/utility/native_extension_loader' # load native parts first
2
+
1
3
  require 'concurrent/atomic/mutex_atomic_fixnum'
2
- require 'concurrent/synchronization'
3
4
 
4
5
  module Concurrent
5
6
 
@@ -96,10 +97,10 @@ module Concurrent
96
97
  # @!visibility private
97
98
  # @!macro internal_implementation_note
98
99
  AtomicFixnumImplementation = case
99
- when defined?(JavaAtomicFixnum)
100
- JavaAtomicFixnum
101
- when defined?(CAtomicFixnum)
100
+ when Concurrent.on_cruby? && Concurrent.c_extensions_loaded?
102
101
  CAtomicFixnum
102
+ when Concurrent.on_jruby?
103
+ JavaAtomicFixnum
103
104
  else
104
105
  MutexAtomicFixnum
105
106
  end
@@ -1,3 +1,6 @@
1
+ require 'concurrent/errors'
2
+ require 'concurrent/synchronization/object'
3
+
1
4
  module Concurrent
2
5
  # An atomic reference which maintains an object reference along with a mark bit
3
6
  # that can be updated atomically.
@@ -1,6 +1,8 @@
1
- require 'concurrent/synchronization'
2
- require 'concurrent/utility/engine'
1
+ require 'concurrent/utility/native_extension_loader' # load native parts first
2
+
3
+ require 'concurrent/atomic_reference/atomic_direct_update'
3
4
  require 'concurrent/atomic_reference/numeric_cas_wrapper'
5
+ require 'concurrent/atomic_reference/mutex_atomic'
4
6
 
5
7
  # Shim for TruffleRuby::AtomicReference
6
8
  if Concurrent.on_truffleruby? && !defined?(TruffleRuby::AtomicReference)
@@ -12,138 +14,6 @@ end
12
14
 
13
15
  module Concurrent
14
16
 
15
- # Define update methods that use direct paths
16
- #
17
- # @!visibility private
18
- # @!macro internal_implementation_note
19
- module AtomicDirectUpdate
20
-
21
- # @!macro atomic_reference_method_update
22
- #
23
- # Pass the current value to the given block, replacing it
24
- # with the block's result. May retry if the value changes
25
- # during the block's execution.
26
- #
27
- # @yield [Object] Calculate a new value for the atomic reference using
28
- # given (old) value
29
- # @yieldparam [Object] old_value the starting value of the atomic reference
30
- # @return [Object] the new value
31
- def update
32
- true until compare_and_set(old_value = get, new_value = yield(old_value))
33
- new_value
34
- end
35
-
36
- # @!macro atomic_reference_method_try_update
37
- #
38
- # Pass the current value to the given block, replacing it
39
- # with the block's result. Return nil if the update fails.
40
- #
41
- # @yield [Object] Calculate a new value for the atomic reference using
42
- # given (old) value
43
- # @yieldparam [Object] old_value the starting value of the atomic reference
44
- # @note This method was altered to avoid raising an exception by default.
45
- # Instead, this method now returns `nil` in case of failure. For more info,
46
- # please see: https://github.com/ruby-concurrency/concurrent-ruby/pull/336
47
- # @return [Object] the new value, or nil if update failed
48
- def try_update
49
- old_value = get
50
- new_value = yield old_value
51
-
52
- return unless compare_and_set old_value, new_value
53
-
54
- new_value
55
- end
56
-
57
- # @!macro atomic_reference_method_try_update!
58
- #
59
- # Pass the current value to the given block, replacing it
60
- # with the block's result. Raise an exception if the update
61
- # fails.
62
- #
63
- # @yield [Object] Calculate a new value for the atomic reference using
64
- # given (old) value
65
- # @yieldparam [Object] old_value the starting value of the atomic reference
66
- # @note This behavior mimics the behavior of the original
67
- # `AtomicReference#try_update` API. The reason this was changed was to
68
- # avoid raising exceptions (which are inherently slow) by default. For more
69
- # info: https://github.com/ruby-concurrency/concurrent-ruby/pull/336
70
- # @return [Object] the new value
71
- # @raise [Concurrent::ConcurrentUpdateError] if the update fails
72
- def try_update!
73
- old_value = get
74
- new_value = yield old_value
75
- unless compare_and_set(old_value, new_value)
76
- if $VERBOSE
77
- raise ConcurrentUpdateError, "Update failed"
78
- else
79
- raise ConcurrentUpdateError, "Update failed", ConcurrentUpdateError::CONC_UP_ERR_BACKTRACE
80
- end
81
- end
82
- new_value
83
- end
84
- end
85
-
86
- require 'concurrent/atomic_reference/mutex_atomic'
87
-
88
- # @!macro atomic_reference
89
- #
90
- # An object reference that may be updated atomically. All read and write
91
- # operations have java volatile semantic.
92
- #
93
- # @!macro thread_safe_variable_comparison
94
- #
95
- # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
96
- # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html
97
- #
98
- # @!method initialize(value = nil)
99
- # @!macro atomic_reference_method_initialize
100
- # @param [Object] value The initial value.
101
- #
102
- # @!method get
103
- # @!macro atomic_reference_method_get
104
- # Gets the current value.
105
- # @return [Object] the current value
106
- #
107
- # @!method set(new_value)
108
- # @!macro atomic_reference_method_set
109
- # Sets to the given value.
110
- # @param [Object] new_value the new value
111
- # @return [Object] the new value
112
- #
113
- # @!method get_and_set(new_value)
114
- # @!macro atomic_reference_method_get_and_set
115
- # Atomically sets to the given value and returns the old value.
116
- # @param [Object] new_value the new value
117
- # @return [Object] the old value
118
- #
119
- # @!method compare_and_set(old_value, new_value)
120
- # @!macro atomic_reference_method_compare_and_set
121
- #
122
- # Atomically sets the value to the given updated value if
123
- # the current value == the expected value.
124
- #
125
- # @param [Object] old_value the expected value
126
- # @param [Object] new_value the new value
127
- #
128
- # @return [Boolean] `true` if successful. A `false` return indicates
129
- # that the actual value was not equal to the expected value.
130
- #
131
- # @!method update
132
- # @!macro atomic_reference_method_update
133
- #
134
- # @!method try_update
135
- # @!macro atomic_reference_method_try_update
136
- #
137
- # @!method try_update!
138
- # @!macro atomic_reference_method_try_update!
139
-
140
-
141
- # @!macro internal_implementation_note
142
- class ConcurrentUpdateError < ThreadError
143
- # frozen pre-allocated backtrace to speed ConcurrentUpdateError
144
- CONC_UP_ERR_BACKTRACE = ['backtrace elided; set verbose to enable'].freeze
145
- end
146
-
147
17
  # @!macro internal_implementation_note
148
18
  AtomicReferenceImplementation = case
149
19
  when Concurrent.on_cruby? && Concurrent.c_extensions_loaded?
@@ -152,7 +22,6 @@ module Concurrent
152
22
  class CAtomicReference
153
23
  include AtomicDirectUpdate
154
24
  include AtomicNumericCompareAndSetWrapper
155
- alias_method :compare_and_swap, :compare_and_set
156
25
  end
157
26
  CAtomicReference
158
27
  when Concurrent.on_jruby?
@@ -160,39 +29,107 @@ module Concurrent
160
29
  # @!macro internal_implementation_note
161
30
  class JavaAtomicReference
162
31
  include AtomicDirectUpdate
32
+ include AtomicNumericCompareAndSetWrapper
163
33
  end
164
34
  JavaAtomicReference
165
35
  when Concurrent.on_truffleruby?
166
36
  class TruffleRubyAtomicReference < TruffleRuby::AtomicReference
167
37
  include AtomicDirectUpdate
38
+ if private_method_defined?(:compare_and_set_reference)
39
+ alias_method :_compare_and_set, :compare_and_set_reference
40
+ private :_compare_and_set
41
+ include AtomicNumericCompareAndSetWrapper
42
+ else
43
+ # AtomicNumericCompareAndSetWrapper behavior already in TruffleRuby::AtomicReference
44
+ alias_method :compare_and_swap, :compare_and_set
45
+ end
168
46
  alias_method :value, :get
169
47
  alias_method :value=, :set
170
- alias_method :compare_and_swap, :compare_and_set
171
48
  alias_method :swap, :get_and_set
172
49
  end
173
50
  TruffleRubyAtomicReference
174
- when Concurrent.on_rbx?
175
- # @note Extends `Rubinius::AtomicReference` version adding aliases
176
- # and numeric logic.
177
- #
178
- # @!visibility private
179
- # @!macro internal_implementation_note
180
- class RbxAtomicReference < Rubinius::AtomicReference
181
- alias_method :_compare_and_set, :compare_and_set
182
- include AtomicDirectUpdate
183
- include AtomicNumericCompareAndSetWrapper
184
- alias_method :value, :get
185
- alias_method :value=, :set
186
- alias_method :swap, :get_and_set
187
- alias_method :compare_and_swap, :compare_and_set
188
- end
189
- RbxAtomicReference
190
51
  else
191
52
  MutexAtomicReference
192
53
  end
193
54
  private_constant :AtomicReferenceImplementation
194
55
 
195
- # @!macro atomic_reference
56
+ # An object reference that may be updated atomically. All read and write
57
+ # operations have java volatile semantic.
58
+ #
59
+ # @!macro thread_safe_variable_comparison
60
+ #
61
+ # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
62
+ # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html
63
+ #
64
+ # @!method initialize(value = nil)
65
+ # @!macro atomic_reference_method_initialize
66
+ # @param [Object] value The initial value.
67
+ #
68
+ # @!method get
69
+ # @!macro atomic_reference_method_get
70
+ # Gets the current value.
71
+ # @return [Object] the current value
72
+ #
73
+ # @!method set(new_value)
74
+ # @!macro atomic_reference_method_set
75
+ # Sets to the given value.
76
+ # @param [Object] new_value the new value
77
+ # @return [Object] the new value
78
+ #
79
+ # @!method get_and_set(new_value)
80
+ # @!macro atomic_reference_method_get_and_set
81
+ # Atomically sets to the given value and returns the old value.
82
+ # @param [Object] new_value the new value
83
+ # @return [Object] the old value
84
+ #
85
+ # @!method compare_and_set(old_value, new_value)
86
+ # @!macro atomic_reference_method_compare_and_set
87
+ #
88
+ # Atomically sets the value to the given updated value if
89
+ # the current value == the expected value.
90
+ #
91
+ # @param [Object] old_value the expected value
92
+ # @param [Object] new_value the new value
93
+ #
94
+ # @return [Boolean] `true` if successful. A `false` return indicates
95
+ # that the actual value was not equal to the expected value.
96
+ #
97
+ # @!method update
98
+ # Pass the current value to the given block, replacing it
99
+ # with the block's result. May retry if the value changes
100
+ # during the block's execution.
101
+ #
102
+ # @yield [Object] Calculate a new value for the atomic reference using
103
+ # given (old) value
104
+ # @yieldparam [Object] old_value the starting value of the atomic reference
105
+ # @return [Object] the new value
106
+ #
107
+ # @!method try_update
108
+ # Pass the current value to the given block, replacing it
109
+ # with the block's result. Return nil if the update fails.
110
+ #
111
+ # @yield [Object] Calculate a new value for the atomic reference using
112
+ # given (old) value
113
+ # @yieldparam [Object] old_value the starting value of the atomic reference
114
+ # @note This method was altered to avoid raising an exception by default.
115
+ # Instead, this method now returns `nil` in case of failure. For more info,
116
+ # please see: https://github.com/ruby-concurrency/concurrent-ruby/pull/336
117
+ # @return [Object] the new value, or nil if update failed
118
+ #
119
+ # @!method try_update!
120
+ # Pass the current value to the given block, replacing it
121
+ # with the block's result. Raise an exception if the update
122
+ # fails.
123
+ #
124
+ # @yield [Object] Calculate a new value for the atomic reference using
125
+ # given (old) value
126
+ # @yieldparam [Object] old_value the starting value of the atomic reference
127
+ # @note This behavior mimics the behavior of the original
128
+ # `AtomicReference#try_update` API. The reason this was changed was to
129
+ # avoid raising exceptions (which are inherently slow) by default. For more
130
+ # info: https://github.com/ruby-concurrency/concurrent-ruby/pull/336
131
+ # @return [Object] the new value
132
+ # @raise [Concurrent::ConcurrentUpdateError] if the update fails
196
133
  class AtomicReference < AtomicReferenceImplementation
197
134
 
198
135
  # @return [String] Short string representation.
@@ -1,4 +1,4 @@
1
- require 'concurrent/synchronization'
1
+ require 'concurrent/synchronization/lockable_object'
2
2
  require 'concurrent/utility/native_integer'
3
3
 
4
4
  module Concurrent
@@ -1,5 +1,5 @@
1
1
  require 'thread'
2
- require 'concurrent/synchronization'
2
+ require 'concurrent/synchronization/lockable_object'
3
3
 
4
4
  module Concurrent
5
5
 
@@ -0,0 +1,109 @@
1
+ require 'concurrent/constants'
2
+ require_relative 'locals'
3
+
4
+ module Concurrent
5
+
6
+ # A `FiberLocalVar` is a variable where the value is different for each fiber.
7
+ # Each variable may have a default value, but when you modify the variable only
8
+ # the current fiber will ever see that change.
9
+ #
10
+ # This is similar to Ruby's built-in fiber-local variables (`Thread.current[:name]`),
11
+ # but with these major advantages:
12
+ # * `FiberLocalVar` has its own identity, it doesn't need a Symbol.
13
+ # * Each Ruby's built-in fiber-local variable leaks some memory forever (it's a Symbol held forever on the fiber),
14
+ # so it's only OK to create a small amount of them.
15
+ # `FiberLocalVar` has no such issue and it is fine to create many of them.
16
+ # * Ruby's built-in fiber-local variables leak forever the value set on each fiber (unless set to nil explicitly).
17
+ # `FiberLocalVar` automatically removes the mapping for each fiber once the `FiberLocalVar` instance is GC'd.
18
+ #
19
+ # @example
20
+ # v = FiberLocalVar.new(14)
21
+ # v.value #=> 14
22
+ # v.value = 2
23
+ # v.value #=> 2
24
+ #
25
+ # @example
26
+ # v = FiberLocalVar.new(14)
27
+ #
28
+ # Fiber.new do
29
+ # v.value #=> 14
30
+ # v.value = 1
31
+ # v.value #=> 1
32
+ # end.resume
33
+ #
34
+ # Fiber.new do
35
+ # v.value #=> 14
36
+ # v.value = 2
37
+ # v.value #=> 2
38
+ # end.resume
39
+ #
40
+ # v.value #=> 14
41
+ class FiberLocalVar
42
+ LOCALS = FiberLocals.new
43
+
44
+ # Creates a fiber local variable.
45
+ #
46
+ # @param [Object] default the default value when otherwise unset
47
+ # @param [Proc] default_block Optional block that gets called to obtain the
48
+ # default value for each fiber
49
+ def initialize(default = nil, &default_block)
50
+ if default && block_given?
51
+ raise ArgumentError, "Cannot use both value and block as default value"
52
+ end
53
+
54
+ if block_given?
55
+ @default_block = default_block
56
+ @default = nil
57
+ else
58
+ @default_block = nil
59
+ @default = default
60
+ end
61
+
62
+ @index = LOCALS.next_index(self)
63
+ end
64
+
65
+ # Returns the value in the current fiber's copy of this fiber-local variable.
66
+ #
67
+ # @return [Object] the current value
68
+ def value
69
+ LOCALS.fetch(@index) { default }
70
+ end
71
+
72
+ # Sets the current fiber's copy of this fiber-local variable to the specified value.
73
+ #
74
+ # @param [Object] value the value to set
75
+ # @return [Object] the new value
76
+ def value=(value)
77
+ LOCALS.set(@index, value)
78
+ end
79
+
80
+ # Bind the given value to fiber local storage during
81
+ # execution of the given block.
82
+ #
83
+ # @param [Object] value the value to bind
84
+ # @yield the operation to be performed with the bound variable
85
+ # @return [Object] the value
86
+ def bind(value)
87
+ if block_given?
88
+ old_value = self.value
89
+ self.value = value
90
+ begin
91
+ yield
92
+ ensure
93
+ self.value = old_value
94
+ end
95
+ end
96
+ end
97
+
98
+ protected
99
+
100
+ # @!visibility private
101
+ def default
102
+ if @default_block
103
+ self.value = @default_block.call
104
+ else
105
+ @default
106
+ end
107
+ end
108
+ end
109
+ end
@@ -1,4 +1,5 @@
1
1
  if Concurrent.on_jruby?
2
+ require 'concurrent/utility/native_extension_loader'
2
3
 
3
4
  module Concurrent
4
5