concurrent-ruby 1.1.4 → 1.1.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -0
  3. data/Gemfile +11 -8
  4. data/{LICENSE.md → LICENSE.txt} +18 -20
  5. data/README.md +40 -5
  6. data/Rakefile +52 -64
  7. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +9 -8
  8. data/lib/{concurrent-ruby.rb → concurrent-ruby/concurrent-ruby.rb} +0 -0
  9. data/lib/{concurrent.rb → concurrent-ruby/concurrent.rb} +0 -0
  10. data/lib/{concurrent → concurrent-ruby/concurrent}/agent.rb +0 -0
  11. data/lib/{concurrent → concurrent-ruby/concurrent}/array.rb +9 -9
  12. data/lib/{concurrent → concurrent-ruby/concurrent}/async.rb +23 -20
  13. data/lib/{concurrent → concurrent-ruby/concurrent}/atom.rb +1 -1
  14. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/abstract_thread_local_var.rb +0 -0
  15. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_boolean.rb +2 -2
  16. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_fixnum.rb +2 -2
  17. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_markable_reference.rb +0 -0
  18. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_reference.rb +0 -0
  19. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/count_down_latch.rb +1 -1
  20. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/cyclic_barrier.rb +0 -0
  21. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/event.rb +0 -0
  22. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/java_count_down_latch.rb +0 -0
  23. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/java_thread_local_var.rb +0 -0
  24. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_boolean.rb +0 -0
  25. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_fixnum.rb +0 -0
  26. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_count_down_latch.rb +0 -0
  27. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_semaphore.rb +0 -0
  28. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/read_write_lock.rb +0 -0
  29. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/reentrant_read_write_lock.rb +0 -0
  30. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/ruby_thread_local_var.rb +60 -40
  31. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/semaphore.rb +0 -0
  32. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/thread_local_var.rb +1 -1
  33. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/mutex_atomic.rb +0 -0
  34. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/numeric_cas_wrapper.rb +0 -0
  35. data/lib/{concurrent → concurrent-ruby/concurrent}/atomics.rb +0 -0
  36. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_notify_observer_set.rb +0 -0
  37. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_write_observer_set.rb +0 -0
  38. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/java_non_concurrent_priority_queue.rb +0 -0
  39. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/lock_free_stack.rb +1 -1
  40. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/atomic_reference_map_backend.rb +0 -0
  41. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/mri_map_backend.rb +1 -1
  42. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/non_concurrent_map_backend.rb +0 -0
  43. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/synchronized_map_backend.rb +0 -0
  44. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/non_concurrent_priority_queue.rb +1 -1
  45. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/ruby_non_concurrent_priority_queue.rb +0 -0
  46. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/deprecation.rb +0 -0
  47. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/dereferenceable.rb +2 -2
  48. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/logging.rb +0 -0
  49. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/obligation.rb +0 -0
  50. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/observable.rb +0 -0
  51. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  52. data/lib/{concurrent → concurrent-ruby/concurrent}/configuration.rb +13 -9
  53. data/lib/{concurrent → concurrent-ruby/concurrent}/constants.rb +0 -0
  54. data/lib/{concurrent → concurrent-ruby/concurrent}/dataflow.rb +0 -0
  55. data/lib/{concurrent → concurrent-ruby/concurrent}/delay.rb +0 -0
  56. data/lib/{concurrent → concurrent-ruby/concurrent}/errors.rb +0 -0
  57. data/lib/{concurrent → concurrent-ruby/concurrent}/exchanger.rb +0 -0
  58. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/abstract_executor_service.rb +17 -23
  59. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/cached_thread_pool.rb +4 -4
  60. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/executor_service.rb +2 -2
  61. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/fixed_thread_pool.rb +16 -12
  62. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/immediate_executor.rb +0 -0
  63. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/indirect_immediate_executor.rb +0 -0
  64. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_executor_service.rb +18 -6
  65. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_single_thread_executor.rb +4 -3
  66. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_thread_pool_executor.rb +15 -2
  67. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_executor_service.rb +0 -2
  68. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_single_thread_executor.rb +0 -1
  69. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_thread_pool_executor.rb +20 -5
  70. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/safe_task_executor.rb +0 -0
  71. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serial_executor_service.rb +0 -0
  72. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution.rb +0 -0
  73. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution_delegator.rb +0 -0
  74. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/simple_executor_service.rb +1 -1
  75. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/single_thread_executor.rb +1 -0
  76. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/thread_pool_executor.rb +2 -1
  77. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/timer_set.rb +13 -16
  78. data/lib/{concurrent → concurrent-ruby/concurrent}/executors.rb +0 -0
  79. data/lib/{concurrent → concurrent-ruby/concurrent}/future.rb +0 -0
  80. data/lib/{concurrent → concurrent-ruby/concurrent}/hash.rb +4 -4
  81. data/lib/{concurrent → concurrent-ruby/concurrent}/immutable_struct.rb +9 -1
  82. data/lib/{concurrent → concurrent-ruby/concurrent}/ivar.rb +0 -0
  83. data/lib/{concurrent → concurrent-ruby/concurrent}/map.rb +1 -1
  84. data/lib/{concurrent → concurrent-ruby/concurrent}/maybe.rb +0 -0
  85. data/lib/{concurrent → concurrent-ruby/concurrent}/mutable_struct.rb +12 -2
  86. data/lib/{concurrent → concurrent-ruby/concurrent}/mvar.rb +0 -0
  87. data/lib/{concurrent → concurrent-ruby/concurrent}/options.rb +0 -0
  88. data/lib/{concurrent → concurrent-ruby/concurrent}/promise.rb +0 -0
  89. data/lib/{concurrent → concurrent-ruby/concurrent}/promises.rb +348 -117
  90. data/lib/{concurrent → concurrent-ruby/concurrent}/re_include.rb +0 -0
  91. data/lib/{concurrent → concurrent-ruby/concurrent}/scheduled_task.rb +0 -0
  92. data/lib/{concurrent → concurrent-ruby/concurrent}/set.rb +5 -5
  93. data/lib/{concurrent → concurrent-ruby/concurrent}/settable_struct.rb +11 -1
  94. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization.rb +0 -0
  95. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_lockable_object.rb +0 -0
  96. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_object.rb +0 -0
  97. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_struct.rb +12 -0
  98. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/condition.rb +2 -0
  99. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_lockable_object.rb +0 -0
  100. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_object.rb +1 -0
  101. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lock.rb +2 -0
  102. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lockable_object.rb +2 -2
  103. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/mri_object.rb +1 -0
  104. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/mutex_lockable_object.rb +0 -0
  105. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/object.rb +46 -20
  106. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/rbx_lockable_object.rb +0 -0
  107. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/rbx_object.rb +1 -0
  108. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/truffleruby_object.rb +1 -0
  109. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/volatile.rb +0 -0
  110. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/synchronized_delegator.rb +0 -0
  111. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util.rb +0 -0
  112. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/adder.rb +0 -0
  113. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/cheap_lockable.rb +0 -0
  114. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/data_structures.rb +0 -0
  115. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/power_of_two_tuple.rb +0 -0
  116. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/striped64.rb +1 -1
  117. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/volatile.rb +0 -0
  118. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/xor_shift_random.rb +0 -0
  119. data/lib/{concurrent → concurrent-ruby/concurrent}/timer_task.rb +0 -1
  120. data/lib/{concurrent → concurrent-ruby/concurrent}/tuple.rb +0 -0
  121. data/lib/{concurrent → concurrent-ruby/concurrent}/tvar.rb +9 -6
  122. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/engine.rb +0 -0
  123. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/monotonic_time.rb +0 -0
  124. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/native_extension_loader.rb +0 -0
  125. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/native_integer.rb +0 -0
  126. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/processor_counter.rb +5 -0
  127. data/lib/concurrent-ruby/concurrent/version.rb +3 -0
  128. metadata +129 -129
  129. data/lib/concurrent/concurrent_ruby.jar +0 -0
  130. data/lib/concurrent/utility/at_exit.rb +0 -97
  131. data/lib/concurrent/version.rb +0 -4
@@ -23,6 +23,7 @@ import java.lang.reflect.Method;
23
23
  public class SynchronizationLibrary implements Library {
24
24
 
25
25
  private static final Unsafe UNSAFE = loadUnsafe();
26
+ private static final boolean FULL_FENCE = supportsFences();
26
27
 
27
28
  private static Unsafe loadUnsafe() {
28
29
  try {
@@ -140,17 +141,17 @@ public class SynchronizationLibrary implements Library {
140
141
  // volatile threadContext is used as a memory barrier per the JVM memory model happens-before semantic
141
142
  // on volatile fields. any volatile field could have been used but using the thread context is an
142
143
  // attempt to avoid code elimination.
143
- private static volatile ThreadContext threadContext = null;
144
+ private static volatile int volatileField;
144
145
 
145
146
  @JRubyMethod(name = "full_memory_barrier", visibility = Visibility.PUBLIC)
146
147
  public static IRubyObject fullMemoryBarrier(ThreadContext context, IRubyObject self) {
147
148
  // Prevent reordering of ivar writes with publication of this instance
148
- if (!supportsFences()) {
149
+ if (!FULL_FENCE) {
149
150
  // Assuming that following volatile read and write is not eliminated it simulates fullFence.
150
151
  // If it's eliminated it'll cause problems only on non-x86 platforms.
151
152
  // http://shipilev.net/blog/2014/jmm-pragmatics/#_happens_before_test_your_understanding
152
- final ThreadContext oldContext = threadContext;
153
- threadContext = context;
153
+ final int volatileRead = volatileField;
154
+ volatileField = context.getLine();
154
155
  } else {
155
156
  UNSAFE.fullFence();
156
157
  }
@@ -163,9 +164,9 @@ public class SynchronizationLibrary implements Library {
163
164
  IRubyObject self,
164
165
  IRubyObject name) {
165
166
  // Ensure we ses latest value with loadFence
166
- if (!supportsFences()) {
167
+ if (!FULL_FENCE) {
167
168
  // piggybacking on volatile read, simulating loadFence
168
- final ThreadContext oldContext = threadContext;
169
+ final int volatileRead = volatileField;
169
170
  return ((RubyBasicObject) self).instance_variable_get(context, name);
170
171
  } else {
171
172
  UNSAFE.loadFence();
@@ -180,10 +181,10 @@ public class SynchronizationLibrary implements Library {
180
181
  IRubyObject name,
181
182
  IRubyObject value) {
182
183
  // Ensure we make last update visible
183
- if (!supportsFences()) {
184
+ if (!FULL_FENCE) {
184
185
  // piggybacking on volatile write, simulating storeFence
185
186
  final IRubyObject result = ((RubyBasicObject) self).instance_variable_set(name, value);
186
- threadContext = context;
187
+ volatileField = context.getLine();
187
188
  return result;
188
189
  } else {
189
190
  // JRuby uses StampedVariableAccessor which calls fullFence
@@ -10,19 +10,20 @@ module Concurrent
10
10
  # or writing at a time. This includes iteration methods like `#each`.
11
11
  #
12
12
  # @note `a += b` is **not** a **thread-safe** operation on
13
- # `Concurrent::Array`. It reads array `a`, then it creates new `Concurrent::Array`
14
- # which is concatenation of `a` and `b`, then it writes the concatenation to `a`.
15
- # The read and write are independent operations they do not form a single atomic
16
- # operation therefore when two `+=` operations are executed concurrently updates
17
- # may be lost. Use `#concat` instead.
13
+ # `Concurrent::Array`. It reads array `a`, then it creates new `Concurrent::Array`
14
+ # which is concatenation of `a` and `b`, then it writes the concatenation to `a`.
15
+ # The read and write are independent operations they do not form a single atomic
16
+ # operation therefore when two `+=` operations are executed concurrently updates
17
+ # may be lost. Use `#concat` instead.
18
18
  #
19
- # @see http://ruby-doc.org/core-2.2.0/Array.html Ruby standard library `Array`
19
+ # @see http://ruby-doc.org/core/Array.html Ruby standard library `Array`
20
20
 
21
21
  # @!macro internal_implementation_note
22
22
  ArrayImplementation = case
23
23
  when Concurrent.on_cruby?
24
- # Because MRI never runs code in parallel, the existing
25
- # non-thread-safe structures should usually work fine.
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.
26
27
  ::Array
27
28
 
28
29
  when Concurrent.on_jruby?
@@ -63,4 +64,3 @@ module Concurrent
63
64
  end
64
65
 
65
66
  end
66
-
@@ -58,26 +58,6 @@ module Concurrent
58
58
  # end
59
59
  # ```
60
60
  #
61
- # When defining a constructor it is critical that the first line be a call to
62
- # `super` with no arguments. The `super` method initializes the background
63
- # thread and other asynchronous components.
64
- #
65
- # ```
66
- # class BackgroundLogger
67
- # include Concurrent::Async
68
- #
69
- # def initialize(level)
70
- # super()
71
- # @logger = Logger.new(STDOUT)
72
- # @logger.level = level
73
- # end
74
- #
75
- # def info(msg)
76
- # @logger.info(msg)
77
- # end
78
- # end
79
- # ```
80
- #
81
61
  # Mixing this module into a class provides each object two proxy methods:
82
62
  # `async` and `await`. These methods are thread safe with respect to the
83
63
  # enclosing object. The former proxy allows methods to be called
@@ -309,6 +289,7 @@ module Concurrent
309
289
  @delegate = delegate
310
290
  @queue = []
311
291
  @executor = Concurrent.global_io_executor
292
+ @ruby_pid = $$
312
293
  end
313
294
 
314
295
  # Delegates method calls to the wrapped object.
@@ -326,6 +307,7 @@ module Concurrent
326
307
 
327
308
  ivar = Concurrent::IVar.new
328
309
  synchronize do
310
+ reset_if_forked
329
311
  @queue.push [ivar, method, args, block]
330
312
  @executor.post { perform } if @queue.length == 1
331
313
  end
@@ -333,6 +315,13 @@ module Concurrent
333
315
  ivar
334
316
  end
335
317
 
318
+ # Check whether the method is responsive
319
+ #
320
+ # @param [Symbol] method the method being called
321
+ def respond_to_missing?(method, include_private = false)
322
+ @delegate.respond_to?(method) || super
323
+ end
324
+
336
325
  # Perform all enqueued tasks.
337
326
  #
338
327
  # This method must be called from within the executor. It must not be
@@ -354,6 +343,13 @@ module Concurrent
354
343
  end
355
344
  end
356
345
  end
346
+
347
+ def reset_if_forked
348
+ if $$ != @ruby_pid
349
+ @queue.clear
350
+ @ruby_pid = $$
351
+ end
352
+ end
357
353
  end
358
354
  private_constant :AsyncDelegator
359
355
 
@@ -383,6 +379,13 @@ module Concurrent
383
379
  ivar.wait
384
380
  ivar
385
381
  end
382
+
383
+ # Check whether the method is responsive
384
+ #
385
+ # @param [Symbol] method the method being called
386
+ def respond_to_missing?(method, include_private = false)
387
+ @delegate.respond_to?(method) || super
388
+ end
386
389
  end
387
390
  private_constant :AwaitDelegator
388
391
 
@@ -18,7 +18,7 @@ require 'concurrent/synchronization'
18
18
  # uncoordinated, *synchronous* change of individual values. Best used when
19
19
  # the value will undergo frequent reads but only occasional, though complex,
20
20
  # updates. Suitable when the result of an update must be known immediately.
21
- # * *{Concurrent::AtomicReference}:* A simple object reference that can be
21
+ # * *{Concurrent::AtomicReference}:* A simple object reference that can be updated
22
22
  # atomically. Updates are synchronous but fast. Best used when updates a
23
23
  # simple set operations. Not suitable when updates are complex.
24
24
  # {Concurrent::AtomicBoolean} and {Concurrent::AtomicFixnum} are similar
@@ -41,13 +41,13 @@ module Concurrent
41
41
  #
42
42
  # Explicitly sets the value to true.
43
43
  #
44
- # @return [Boolean] true is value has changed, otherwise false
44
+ # @return [Boolean] true if value has changed, otherwise false
45
45
 
46
46
  # @!macro atomic_boolean_method_make_false
47
47
  #
48
48
  # Explicitly sets the value to false.
49
49
  #
50
- # @return [Boolean] true is value has changed, otherwise false
50
+ # @return [Boolean] true if value has changed, otherwise false
51
51
 
52
52
  ###################################################################
53
53
 
@@ -79,10 +79,10 @@ module Concurrent
79
79
  # @!method value=(value)
80
80
  # @!macro atomic_fixnum_method_value_set
81
81
  #
82
- # @!method increment(delta)
82
+ # @!method increment(delta = 1)
83
83
  # @!macro atomic_fixnum_method_increment
84
84
  #
85
- # @!method decrement(delta)
85
+ # @!method decrement(delta = 1)
86
86
  # @!macro atomic_fixnum_method_decrement
87
87
  #
88
88
  # @!method compare_and_set(expect, update)
@@ -1,6 +1,6 @@
1
+ require 'concurrent/utility/engine'
1
2
  require 'concurrent/atomic/mutex_count_down_latch'
2
3
  require 'concurrent/atomic/java_count_down_latch'
3
- require 'concurrent/utility/engine'
4
4
 
5
5
  module Concurrent
6
6
 
@@ -28,16 +28,31 @@ module Concurrent
28
28
  # But when a Thread is GC'd, we need to drop the reference to its thread-local
29
29
  # array, so we don't leak memory
30
30
 
31
- # @!visibility private
32
- FREE = []
33
- LOCK = Mutex.new
34
- ARRAYS = {} # used as a hash set
35
- @@next = 0
36
- private_constant :FREE, :LOCK, :ARRAYS
31
+ FREE = []
32
+ LOCK = Mutex.new
33
+ THREAD_LOCAL_ARRAYS = {} # used as a hash set
34
+
35
+ # synchronize when not on MRI
36
+ # on MRI using lock in finalizer leads to "can't be called from trap context" error
37
+ # so the code is carefully written to be tread-safe on MRI relying on GIL
38
+
39
+ if Concurrent.on_cruby?
40
+ # @!visibility private
41
+ def self.semi_sync(&block)
42
+ block.call
43
+ end
44
+ else
45
+ # @!visibility private
46
+ def self.semi_sync(&block)
47
+ LOCK.synchronize(&block)
48
+ end
49
+ end
50
+
51
+ private_constant :FREE, :LOCK, :THREAD_LOCAL_ARRAYS
37
52
 
38
53
  # @!macro thread_local_var_method_get
39
54
  def value
40
- if array = get_threadlocal_array
55
+ if (array = get_threadlocal_array)
41
56
  value = array[@index]
42
57
  if value.nil?
43
58
  default
@@ -57,10 +72,10 @@ module Concurrent
57
72
  # We could keep the thread-local arrays in a hash, keyed by Thread
58
73
  # But why? That would require locking
59
74
  # Using Ruby's built-in thread-local storage is faster
60
- unless array = get_threadlocal_array(me)
75
+ unless (array = get_threadlocal_array(me))
61
76
  array = set_threadlocal_array([], me)
62
- LOCK.synchronize { ARRAYS[array.object_id] = array }
63
- ObjectSpace.define_finalizer(me, self.class.thread_finalizer(array))
77
+ self.class.semi_sync { THREAD_LOCAL_ARRAYS[array.object_id] = array }
78
+ ObjectSpace.define_finalizer(me, self.class.thread_finalizer(array.object_id))
64
79
  end
65
80
  array[@index] = (value.nil? ? NULL : value)
66
81
  value
@@ -70,48 +85,52 @@ module Concurrent
70
85
 
71
86
  # @!visibility private
72
87
  def allocate_storage
73
- @index = LOCK.synchronize do
74
- FREE.pop || begin
75
- result = @@next
76
- @@next += 1
77
- result
78
- end
79
- end
80
- ObjectSpace.define_finalizer(self, self.class.threadlocal_finalizer(@index))
88
+ @index = FREE.pop || next_index
89
+
90
+ ObjectSpace.define_finalizer(self, self.class.thread_local_finalizer(@index))
81
91
  end
82
92
 
83
93
  # @!visibility private
84
- def self.threadlocal_finalizer(index)
94
+ def self.thread_local_finalizer(index)
85
95
  proc do
86
- Thread.new do # avoid error: can't be called from trap context
87
- LOCK.synchronize do
88
- FREE.push(index)
89
- # The cost of GC'ing a TLV is linear in the number of threads using TLVs
90
- # But that is natural! More threads means more storage is used per TLV
91
- # So naturally more CPU time is required to free more storage
92
- ARRAYS.each_value do |array|
93
- array[index] = nil
94
- end
95
- end
96
+ semi_sync do
97
+ # The cost of GC'ing a TLV is linear in the number of threads using TLVs
98
+ # But that is natural! More threads means more storage is used per TLV
99
+ # So naturally more CPU time is required to free more storage
100
+ #
101
+ # DO NOT use each_value which might conflict with new pair assignment
102
+ # into the hash in #value= method
103
+ THREAD_LOCAL_ARRAYS.values.each { |array| array[index] = nil }
104
+ # free index has to be published after the arrays are cleared
105
+ FREE.push(index)
96
106
  end
97
107
  end
98
108
  end
99
109
 
100
110
  # @!visibility private
101
- def self.thread_finalizer(array)
111
+ def self.thread_finalizer(id)
102
112
  proc do
103
- Thread.new do # avoid error: can't be called from trap context
104
- LOCK.synchronize do
105
- # The thread which used this thread-local array is now gone
106
- # So don't hold onto a reference to the array (thus blocking GC)
107
- ARRAYS.delete(array.object_id)
108
- end
113
+ semi_sync do
114
+ # The thread which used this thread-local array is now gone
115
+ # So don't hold onto a reference to the array (thus blocking GC)
116
+ THREAD_LOCAL_ARRAYS.delete(id)
109
117
  end
110
118
  end
111
119
  end
112
120
 
113
121
  private
114
122
 
123
+ # noinspection RubyClassVariableUsageInspection
124
+ @@next = 0
125
+ # noinspection RubyClassVariableUsageInspection
126
+ def next_index
127
+ LOCK.synchronize do
128
+ result = @@next
129
+ @@next += 1
130
+ result
131
+ end
132
+ end
133
+
115
134
  if Thread.instance_methods.include?(:thread_variable_get)
116
135
 
117
136
  def get_threadlocal_array(thread = Thread.current)
@@ -136,21 +155,22 @@ module Concurrent
136
155
  # This exists only for use in testing
137
156
  # @!visibility private
138
157
  def value_for(thread)
139
- if array = get_threadlocal_array(thread)
158
+ if (array = get_threadlocal_array(thread))
140
159
  value = array[@index]
141
160
  if value.nil?
142
- default_for(thread)
161
+ get_default
143
162
  elsif value.equal?(NULL)
144
163
  nil
145
164
  else
146
165
  value
147
166
  end
148
167
  else
149
- default_for(thread)
168
+ get_default
150
169
  end
151
170
  end
152
171
 
153
- def default_for(thread)
172
+ # @!visibility private
173
+ def get_default
154
174
  if @default_block
155
175
  raise "Cannot use default_for with default block"
156
176
  else
@@ -1,6 +1,6 @@
1
+ require 'concurrent/utility/engine'
1
2
  require 'concurrent/atomic/ruby_thread_local_var'
2
3
  require 'concurrent/atomic/java_thread_local_var'
3
- require 'concurrent/utility/engine'
4
4
 
5
5
  module Concurrent
6
6
 
@@ -53,7 +53,7 @@ module Concurrent
53
53
 
54
54
  # @param [Node] head
55
55
  # @return [true, false]
56
- def empty?(head = self.head)
56
+ def empty?(head = head())
57
57
  head.equal? EMPTY
58
58
  end
59
59
 
@@ -19,7 +19,7 @@ module Concurrent
19
19
  end
20
20
 
21
21
  def compute_if_absent(key)
22
- if stored_value = _get(key) # fast non-blocking path for the most likely case
22
+ if NULL != (stored_value = @backend.fetch(key, NULL)) # fast non-blocking path for the most likely case
23
23
  stored_value
24
24
  else
25
25
  @write_lock.synchronize { super }