concurrent-ruby 1.1.4 → 1.1.8
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 +4 -4
- data/CHANGELOG.md +51 -0
- data/Gemfile +11 -8
- data/{LICENSE.md → LICENSE.txt} +18 -20
- data/README.md +40 -5
- data/Rakefile +52 -64
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +9 -8
- data/lib/{concurrent-ruby.rb → concurrent-ruby/concurrent-ruby.rb} +0 -0
- data/lib/{concurrent.rb → concurrent-ruby/concurrent.rb} +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/agent.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/array.rb +9 -9
- data/lib/{concurrent → concurrent-ruby/concurrent}/async.rb +23 -20
- data/lib/{concurrent → concurrent-ruby/concurrent}/atom.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/abstract_thread_local_var.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_boolean.rb +2 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_fixnum.rb +2 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_markable_reference.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_reference.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/count_down_latch.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/cyclic_barrier.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/event.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/java_count_down_latch.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/java_thread_local_var.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_boolean.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_fixnum.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_count_down_latch.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_semaphore.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/read_write_lock.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/reentrant_read_write_lock.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/ruby_thread_local_var.rb +60 -40
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/semaphore.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/thread_local_var.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/mutex_atomic.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/numeric_cas_wrapper.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomics.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_notify_observer_set.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_write_observer_set.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/java_non_concurrent_priority_queue.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/lock_free_stack.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/atomic_reference_map_backend.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/mri_map_backend.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/non_concurrent_map_backend.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/synchronized_map_backend.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/non_concurrent_priority_queue.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/ruby_non_concurrent_priority_queue.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/concern/deprecation.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/concern/dereferenceable.rb +2 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/concern/logging.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/concern/obligation.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/concern/observable.rb +0 -0
- data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/configuration.rb +13 -9
- data/lib/{concurrent → concurrent-ruby/concurrent}/constants.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/dataflow.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/delay.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/errors.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/exchanger.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/abstract_executor_service.rb +17 -23
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/cached_thread_pool.rb +4 -4
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/executor_service.rb +2 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/fixed_thread_pool.rb +16 -12
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/immediate_executor.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/indirect_immediate_executor.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_executor_service.rb +18 -6
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_single_thread_executor.rb +4 -3
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_thread_pool_executor.rb +15 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_executor_service.rb +0 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_single_thread_executor.rb +0 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_thread_pool_executor.rb +20 -5
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/safe_task_executor.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serial_executor_service.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution_delegator.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/simple_executor_service.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/single_thread_executor.rb +1 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/thread_pool_executor.rb +2 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/timer_set.rb +13 -16
- data/lib/{concurrent → concurrent-ruby/concurrent}/executors.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/future.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/hash.rb +4 -4
- data/lib/{concurrent → concurrent-ruby/concurrent}/immutable_struct.rb +9 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/ivar.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/map.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/maybe.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/mutable_struct.rb +12 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/mvar.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/options.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/promise.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/promises.rb +348 -117
- data/lib/{concurrent → concurrent-ruby/concurrent}/re_include.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/scheduled_task.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/set.rb +5 -5
- data/lib/{concurrent → concurrent-ruby/concurrent}/settable_struct.rb +11 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_lockable_object.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_object.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_struct.rb +12 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/condition.rb +2 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_lockable_object.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_object.rb +1 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lock.rb +2 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lockable_object.rb +2 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/mri_object.rb +1 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/mutex_lockable_object.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/object.rb +46 -20
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/rbx_lockable_object.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/rbx_object.rb +1 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/truffleruby_object.rb +1 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/volatile.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/synchronized_delegator.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/adder.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/cheap_lockable.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/data_structures.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/power_of_two_tuple.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/striped64.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/volatile.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/xor_shift_random.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/timer_task.rb +0 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/tuple.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/tvar.rb +9 -6
- data/lib/{concurrent → concurrent-ruby/concurrent}/utility/engine.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/utility/monotonic_time.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/utility/native_extension_loader.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/utility/native_integer.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/utility/processor_counter.rb +5 -0
- data/lib/concurrent-ruby/concurrent/version.rb +3 -0
- metadata +129 -129
- data/lib/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent/utility/at_exit.rb +0 -97
- 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
|
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 (!
|
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
|
153
|
-
|
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 (!
|
167
|
+
if (!FULL_FENCE) {
|
167
168
|
// piggybacking on volatile read, simulating loadFence
|
168
|
-
final
|
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 (!
|
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
|
-
|
187
|
+
volatileField = context.getLine();
|
187
188
|
return result;
|
188
189
|
} else {
|
189
190
|
// JRuby uses StampedVariableAccessor which calls fullFence
|
File without changes
|
File without changes
|
File without changes
|
@@ -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
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
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
|
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
|
-
#
|
25
|
-
#
|
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
|
File without changes
|
@@ -41,13 +41,13 @@ module Concurrent
|
|
41
41
|
#
|
42
42
|
# Explicitly sets the value to true.
|
43
43
|
#
|
44
|
-
# @return [Boolean] true
|
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
|
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)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
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 =
|
74
|
-
|
75
|
-
|
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.
|
94
|
+
def self.thread_local_finalizer(index)
|
85
95
|
proc do
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
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(
|
111
|
+
def self.thread_finalizer(id)
|
102
112
|
proc do
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
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
|
-
|
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
|
-
|
168
|
+
get_default
|
150
169
|
end
|
151
170
|
end
|
152
171
|
|
153
|
-
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_notify_observer_set.rb
RENAMED
File without changes
|
File without changes
|
data/lib/{concurrent → concurrent-ruby/concurrent}/collection/java_non_concurrent_priority_queue.rb
RENAMED
File without changes
|
data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/atomic_reference_map_backend.rb
RENAMED
File without changes
|
@@ -19,7 +19,7 @@ module Concurrent
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def compute_if_absent(key)
|
22
|
-
if stored_value =
|
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 }
|