concurrent-ruby 1.1.5 → 1.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +84 -1
- data/Gemfile +5 -10
- data/{LICENSE.md → LICENSE.txt} +18 -20
- data/README.md +55 -31
- data/Rakefile +84 -92
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +0 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +52 -22
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
- data/lib/{concurrent → concurrent-ruby/concurrent}/agent.rb +2 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/array.rb +6 -16
- data/lib/{concurrent → concurrent-ruby/concurrent}/async.rb +10 -20
- data/lib/{concurrent → concurrent-ruby/concurrent}/atom.rb +2 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_boolean.rb +7 -6
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_fixnum.rb +5 -4
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_markable_reference.rb +3 -0
- data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +135 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/cyclic_barrier.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/event.rb +3 -3
- data/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb +109 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/java_count_down_latch.rb +1 -0
- data/lib/concurrent-ruby/concurrent/atomic/locals.rb +189 -0
- data/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb +28 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_boolean.rb +11 -5
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_fixnum.rb +11 -5
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_count_down_latch.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_semaphore.rb +19 -3
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/read_write_lock.rb +2 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/reentrant_read_write_lock.rb +9 -9
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/semaphore.rb +32 -14
- data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +111 -0
- data/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb +37 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/mutex_atomic.rb +15 -4
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_notify_observer_set.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_write_observer_set.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/lock_free_stack.rb +2 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/mri_map_backend.rb +3 -3
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/non_concurrent_map_backend.rb +16 -8
- data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/ruby_non_concurrent_priority_queue.rb +11 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/concern/dereferenceable.rb +2 -2
- data/lib/concurrent-ruby/concurrent/concern/logging.rb +116 -0
- data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent-ruby/concurrent/configuration.rb +105 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/delay.rb +2 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/errors.rb +5 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/exchanger.rb +1 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/abstract_executor_service.rb +34 -37
- 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 +29 -15
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_executor_service.rb +21 -9
- 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 +19 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_executor_service.rb +10 -6
- 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 +46 -42
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/safe_task_executor.rb +6 -6
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/simple_executor_service.rb +5 -2
- 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 +0 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/hash.rb +1 -10
- data/lib/{concurrent → concurrent-ruby/concurrent}/immutable_struct.rb +10 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/ivar.rb +2 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/map.rb +44 -31
- data/lib/{concurrent → concurrent-ruby/concurrent}/maybe.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/mutable_struct.rb +13 -3
- data/lib/{concurrent → concurrent-ruby/concurrent}/mvar.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/promise.rb +2 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/promises.rb +7 -6
- data/lib/{concurrent → concurrent-ruby/concurrent}/re_include.rb +2 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/scheduled_task.rb +30 -17
- data/lib/{concurrent → concurrent-ruby/concurrent}/set.rb +17 -19
- data/lib/{concurrent → concurrent-ruby/concurrent}/settable_struct.rb +13 -3
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_lockable_object.rb +5 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_object.rb +1 -3
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_struct.rb +11 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/condition.rb +2 -0
- data/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb +29 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_lockable_object.rb +3 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lock.rb +2 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lockable_object.rb +8 -7
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/mutex_lockable_object.rb +18 -5
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/object.rb +12 -44
- data/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb +36 -0
- data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +101 -0
- data/lib/concurrent-ruby/concurrent/synchronization.rb +13 -0
- data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +47 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/cheap_lockable.rb +2 -39
- data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +52 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/striped64.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/timer_task.rb +11 -34
- data/lib/{concurrent → concurrent-ruby/concurrent}/tuple.rb +1 -5
- data/lib/{concurrent → concurrent-ruby/concurrent}/tvar.rb +21 -57
- data/lib/{concurrent → concurrent-ruby/concurrent}/utility/engine.rb +5 -16
- data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +19 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/utility/native_extension_loader.rb +8 -10
- data/lib/{concurrent → concurrent-ruby/concurrent}/utility/native_integer.rb +1 -0
- data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +110 -0
- data/lib/concurrent-ruby/concurrent/version.rb +3 -0
- data/lib/concurrent-ruby/concurrent-ruby.rb +5 -0
- metadata +127 -129
- data/lib/concurrent/atomic/abstract_thread_local_var.rb +0 -66
- data/lib/concurrent/atomic/atomic_reference.rb +0 -204
- data/lib/concurrent/atomic/java_thread_local_var.rb +0 -37
- data/lib/concurrent/atomic/ruby_thread_local_var.rb +0 -161
- data/lib/concurrent/atomic/thread_local_var.rb +0 -104
- data/lib/concurrent/concern/logging.rb +0 -32
- data/lib/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent/configuration.rb +0 -184
- data/lib/concurrent/synchronization/jruby_object.rb +0 -45
- data/lib/concurrent/synchronization/mri_object.rb +0 -44
- data/lib/concurrent/synchronization/rbx_lockable_object.rb +0 -65
- data/lib/concurrent/synchronization/rbx_object.rb +0 -49
- data/lib/concurrent/synchronization/truffleruby_object.rb +0 -47
- data/lib/concurrent/synchronization/volatile.rb +0 -36
- data/lib/concurrent/synchronization.rb +0 -30
- data/lib/concurrent/thread_safe/synchronized_delegator.rb +0 -50
- data/lib/concurrent/thread_safe/util/data_structures.rb +0 -63
- data/lib/concurrent/utility/at_exit.rb +0 -97
- data/lib/concurrent/utility/monotonic_time.rb +0 -58
- data/lib/concurrent/utility/processor_counter.rb +0 -158
- data/lib/concurrent/version.rb +0 -3
- data/lib/concurrent-ruby.rb +0 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/count_down_latch.rb +1 -1
- 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/java_non_concurrent_priority_queue.rb +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/atomic_reference_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}/concern/deprecation.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 → concurrent-ruby/concurrent}/constants.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/dataflow.rb +0 -0
- /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/serial_executor_service.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution_delegator.rb +0 -0
- /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}/options.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/power_of_two_tuple.rb +0 -0
- /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}/thread_safe/util.rb +0 -0
- /data/lib/{concurrent.rb → concurrent-ruby/concurrent.rb} +0 -0
@@ -1,161 +0,0 @@
|
|
1
|
-
require 'thread'
|
2
|
-
require 'concurrent/atomic/abstract_thread_local_var'
|
3
|
-
|
4
|
-
module Concurrent
|
5
|
-
|
6
|
-
# @!visibility private
|
7
|
-
# @!macro internal_implementation_note
|
8
|
-
class RubyThreadLocalVar < AbstractThreadLocalVar
|
9
|
-
|
10
|
-
# Each thread has a (lazily initialized) array of thread-local variable values
|
11
|
-
# Each time a new thread-local var is created, we allocate an "index" for it
|
12
|
-
# For example, if the allocated index is 1, that means slot #1 in EVERY
|
13
|
-
# thread's thread-local array will be used for the value of that TLV
|
14
|
-
#
|
15
|
-
# The good thing about using a per-THREAD structure to hold values, rather
|
16
|
-
# than a per-TLV structure, is that no synchronization is needed when
|
17
|
-
# reading and writing those values (since the structure is only ever
|
18
|
-
# accessed by a single thread)
|
19
|
-
#
|
20
|
-
# Of course, when a TLV is GC'd, 1) we need to recover its index for use
|
21
|
-
# by other new TLVs (otherwise the thread-local arrays could get bigger
|
22
|
-
# and bigger with time), and 2) we need to null out all the references
|
23
|
-
# held in the now-unused slots (both to avoid blocking GC of those objects,
|
24
|
-
# and also to prevent "stale" values from being passed on to a new TLV
|
25
|
-
# when the index is reused)
|
26
|
-
# Because we need to null out freed slots, we need to keep references to
|
27
|
-
# ALL the thread-local arrays -- ARRAYS is for that
|
28
|
-
# But when a Thread is GC'd, we need to drop the reference to its thread-local
|
29
|
-
# array, so we don't leak memory
|
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
|
37
|
-
|
38
|
-
# @!macro thread_local_var_method_get
|
39
|
-
def value
|
40
|
-
if array = get_threadlocal_array
|
41
|
-
value = array[@index]
|
42
|
-
if value.nil?
|
43
|
-
default
|
44
|
-
elsif value.equal?(NULL)
|
45
|
-
nil
|
46
|
-
else
|
47
|
-
value
|
48
|
-
end
|
49
|
-
else
|
50
|
-
default
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
# @!macro thread_local_var_method_set
|
55
|
-
def value=(value)
|
56
|
-
me = Thread.current
|
57
|
-
# We could keep the thread-local arrays in a hash, keyed by Thread
|
58
|
-
# But why? That would require locking
|
59
|
-
# Using Ruby's built-in thread-local storage is faster
|
60
|
-
unless array = get_threadlocal_array(me)
|
61
|
-
array = set_threadlocal_array([], me)
|
62
|
-
LOCK.synchronize { ARRAYS[array.object_id] = array }
|
63
|
-
ObjectSpace.define_finalizer(me, self.class.thread_finalizer(array))
|
64
|
-
end
|
65
|
-
array[@index] = (value.nil? ? NULL : value)
|
66
|
-
value
|
67
|
-
end
|
68
|
-
|
69
|
-
protected
|
70
|
-
|
71
|
-
# @!visibility private
|
72
|
-
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))
|
81
|
-
end
|
82
|
-
|
83
|
-
# @!visibility private
|
84
|
-
def self.threadlocal_finalizer(index)
|
85
|
-
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
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
# @!visibility private
|
101
|
-
def self.thread_finalizer(array)
|
102
|
-
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
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
private
|
114
|
-
|
115
|
-
if Thread.instance_methods.include?(:thread_variable_get)
|
116
|
-
|
117
|
-
def get_threadlocal_array(thread = Thread.current)
|
118
|
-
thread.thread_variable_get(:__threadlocal_array__)
|
119
|
-
end
|
120
|
-
|
121
|
-
def set_threadlocal_array(array, thread = Thread.current)
|
122
|
-
thread.thread_variable_set(:__threadlocal_array__, array)
|
123
|
-
end
|
124
|
-
|
125
|
-
else
|
126
|
-
|
127
|
-
def get_threadlocal_array(thread = Thread.current)
|
128
|
-
thread[:__threadlocal_array__]
|
129
|
-
end
|
130
|
-
|
131
|
-
def set_threadlocal_array(array, thread = Thread.current)
|
132
|
-
thread[:__threadlocal_array__] = array
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
# This exists only for use in testing
|
137
|
-
# @!visibility private
|
138
|
-
def value_for(thread)
|
139
|
-
if array = get_threadlocal_array(thread)
|
140
|
-
value = array[@index]
|
141
|
-
if value.nil?
|
142
|
-
default_for(thread)
|
143
|
-
elsif value.equal?(NULL)
|
144
|
-
nil
|
145
|
-
else
|
146
|
-
value
|
147
|
-
end
|
148
|
-
else
|
149
|
-
default_for(thread)
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
def default_for(thread)
|
154
|
-
if @default_block
|
155
|
-
raise "Cannot use default_for with default block"
|
156
|
-
else
|
157
|
-
@default
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
@@ -1,104 +0,0 @@
|
|
1
|
-
require 'concurrent/atomic/ruby_thread_local_var'
|
2
|
-
require 'concurrent/atomic/java_thread_local_var'
|
3
|
-
require 'concurrent/utility/engine'
|
4
|
-
|
5
|
-
module Concurrent
|
6
|
-
|
7
|
-
###################################################################
|
8
|
-
|
9
|
-
# @!macro thread_local_var_method_initialize
|
10
|
-
#
|
11
|
-
# Creates a thread local variable.
|
12
|
-
#
|
13
|
-
# @param [Object] default the default value when otherwise unset
|
14
|
-
# @param [Proc] default_block Optional block that gets called to obtain the
|
15
|
-
# default value for each thread
|
16
|
-
|
17
|
-
# @!macro thread_local_var_method_get
|
18
|
-
#
|
19
|
-
# Returns the value in the current thread's copy of this thread-local variable.
|
20
|
-
#
|
21
|
-
# @return [Object] the current value
|
22
|
-
|
23
|
-
# @!macro thread_local_var_method_set
|
24
|
-
#
|
25
|
-
# Sets the current thread's copy of this thread-local variable to the specified value.
|
26
|
-
#
|
27
|
-
# @param [Object] value the value to set
|
28
|
-
# @return [Object] the new value
|
29
|
-
|
30
|
-
# @!macro thread_local_var_method_bind
|
31
|
-
#
|
32
|
-
# Bind the given value to thread local storage during
|
33
|
-
# execution of the given block.
|
34
|
-
#
|
35
|
-
# @param [Object] value the value to bind
|
36
|
-
# @yield the operation to be performed with the bound variable
|
37
|
-
# @return [Object] the value
|
38
|
-
|
39
|
-
|
40
|
-
###################################################################
|
41
|
-
|
42
|
-
# @!macro thread_local_var_public_api
|
43
|
-
#
|
44
|
-
# @!method initialize(default = nil, &default_block)
|
45
|
-
# @!macro thread_local_var_method_initialize
|
46
|
-
#
|
47
|
-
# @!method value
|
48
|
-
# @!macro thread_local_var_method_get
|
49
|
-
#
|
50
|
-
# @!method value=(value)
|
51
|
-
# @!macro thread_local_var_method_set
|
52
|
-
#
|
53
|
-
# @!method bind(value, &block)
|
54
|
-
# @!macro thread_local_var_method_bind
|
55
|
-
|
56
|
-
###################################################################
|
57
|
-
|
58
|
-
# @!visibility private
|
59
|
-
# @!macro internal_implementation_note
|
60
|
-
ThreadLocalVarImplementation = case
|
61
|
-
when Concurrent.on_jruby?
|
62
|
-
JavaThreadLocalVar
|
63
|
-
else
|
64
|
-
RubyThreadLocalVar
|
65
|
-
end
|
66
|
-
private_constant :ThreadLocalVarImplementation
|
67
|
-
|
68
|
-
# @!macro thread_local_var
|
69
|
-
#
|
70
|
-
# A `ThreadLocalVar` is a variable where the value is different for each thread.
|
71
|
-
# Each variable may have a default value, but when you modify the variable only
|
72
|
-
# the current thread will ever see that change.
|
73
|
-
#
|
74
|
-
# @!macro thread_safe_variable_comparison
|
75
|
-
#
|
76
|
-
# @example
|
77
|
-
# v = ThreadLocalVar.new(14)
|
78
|
-
# v.value #=> 14
|
79
|
-
# v.value = 2
|
80
|
-
# v.value #=> 2
|
81
|
-
#
|
82
|
-
# @example
|
83
|
-
# v = ThreadLocalVar.new(14)
|
84
|
-
#
|
85
|
-
# t1 = Thread.new do
|
86
|
-
# v.value #=> 14
|
87
|
-
# v.value = 1
|
88
|
-
# v.value #=> 1
|
89
|
-
# end
|
90
|
-
#
|
91
|
-
# t2 = Thread.new do
|
92
|
-
# v.value #=> 14
|
93
|
-
# v.value = 2
|
94
|
-
# v.value #=> 2
|
95
|
-
# end
|
96
|
-
#
|
97
|
-
# v.value #=> 14
|
98
|
-
#
|
99
|
-
# @see https://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html Java ThreadLocal
|
100
|
-
#
|
101
|
-
# @!macro thread_local_var_public_api
|
102
|
-
class ThreadLocalVar < ThreadLocalVarImplementation
|
103
|
-
end
|
104
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
require 'logger'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
module Concern
|
5
|
-
|
6
|
-
# Include where logging is needed
|
7
|
-
#
|
8
|
-
# @!visibility private
|
9
|
-
module Logging
|
10
|
-
include Logger::Severity
|
11
|
-
|
12
|
-
# Logs through {Concurrent.global_logger}, it can be overridden by setting @logger
|
13
|
-
# @param [Integer] level one of Logger::Severity constants
|
14
|
-
# @param [String] progname e.g. a path of an Actor
|
15
|
-
# @param [String, nil] message when nil block is used to generate the message
|
16
|
-
# @yieldreturn [String] a message
|
17
|
-
def log(level, progname, message = nil, &block)
|
18
|
-
#NOTE: Cannot require 'concurrent/configuration' above due to circular references.
|
19
|
-
# Assume that the gem has been initialized if we've gotten this far.
|
20
|
-
logger = if defined?(@logger) && @logger
|
21
|
-
@logger
|
22
|
-
else
|
23
|
-
Concurrent.global_logger
|
24
|
-
end
|
25
|
-
logger.call level, progname, message, &block
|
26
|
-
rescue => error
|
27
|
-
$stderr.puts "`Concurrent.configuration.logger` failed to log #{[level, progname, message, block]}\n" +
|
28
|
-
"#{error.message} (#{error.class})\n#{error.backtrace.join "\n"}"
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
Binary file
|
@@ -1,184 +0,0 @@
|
|
1
|
-
require 'thread'
|
2
|
-
require 'concurrent/delay'
|
3
|
-
require 'concurrent/errors'
|
4
|
-
require 'concurrent/atomic/atomic_reference'
|
5
|
-
require 'concurrent/concern/logging'
|
6
|
-
require 'concurrent/executor/immediate_executor'
|
7
|
-
require 'concurrent/executor/cached_thread_pool'
|
8
|
-
require 'concurrent/utility/at_exit'
|
9
|
-
require 'concurrent/utility/processor_counter'
|
10
|
-
|
11
|
-
module Concurrent
|
12
|
-
extend Concern::Logging
|
13
|
-
|
14
|
-
autoload :Options, 'concurrent/options'
|
15
|
-
autoload :TimerSet, 'concurrent/executor/timer_set'
|
16
|
-
autoload :ThreadPoolExecutor, 'concurrent/executor/thread_pool_executor'
|
17
|
-
|
18
|
-
# @return [Logger] Logger with provided level and output.
|
19
|
-
def self.create_simple_logger(level = Logger::FATAL, output = $stderr)
|
20
|
-
# TODO (pitr-ch 24-Dec-2016): figure out why it had to be replaced, stdlogger was deadlocking
|
21
|
-
lambda do |severity, progname, message = nil, &block|
|
22
|
-
return false if severity < level
|
23
|
-
|
24
|
-
message = block ? block.call : message
|
25
|
-
formatted_message = case message
|
26
|
-
when String
|
27
|
-
message
|
28
|
-
when Exception
|
29
|
-
format "%s (%s)\n%s",
|
30
|
-
message.message, message.class, (message.backtrace || []).join("\n")
|
31
|
-
else
|
32
|
-
message.inspect
|
33
|
-
end
|
34
|
-
|
35
|
-
output.print format "[%s] %5s -- %s: %s\n",
|
36
|
-
Time.now.strftime('%Y-%m-%d %H:%M:%S.%L'),
|
37
|
-
Logger::SEV_LABEL[severity],
|
38
|
-
progname,
|
39
|
-
formatted_message
|
40
|
-
true
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# Use logger created by #create_simple_logger to log concurrent-ruby messages.
|
45
|
-
def self.use_simple_logger(level = Logger::FATAL, output = $stderr)
|
46
|
-
Concurrent.global_logger = create_simple_logger level, output
|
47
|
-
end
|
48
|
-
|
49
|
-
# @return [Logger] Logger with provided level and output.
|
50
|
-
# @deprecated
|
51
|
-
def self.create_stdlib_logger(level = Logger::FATAL, output = $stderr)
|
52
|
-
logger = Logger.new(output)
|
53
|
-
logger.level = level
|
54
|
-
logger.formatter = lambda do |severity, datetime, progname, msg|
|
55
|
-
formatted_message = case msg
|
56
|
-
when String
|
57
|
-
msg
|
58
|
-
when Exception
|
59
|
-
format "%s (%s)\n%s",
|
60
|
-
msg.message, msg.class, (msg.backtrace || []).join("\n")
|
61
|
-
else
|
62
|
-
msg.inspect
|
63
|
-
end
|
64
|
-
format "[%s] %5s -- %s: %s\n",
|
65
|
-
datetime.strftime('%Y-%m-%d %H:%M:%S.%L'),
|
66
|
-
severity,
|
67
|
-
progname,
|
68
|
-
formatted_message
|
69
|
-
end
|
70
|
-
|
71
|
-
lambda do |loglevel, progname, message = nil, &block|
|
72
|
-
logger.add loglevel, message, progname, &block
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
# Use logger created by #create_stdlib_logger to log concurrent-ruby messages.
|
77
|
-
# @deprecated
|
78
|
-
def self.use_stdlib_logger(level = Logger::FATAL, output = $stderr)
|
79
|
-
Concurrent.global_logger = create_stdlib_logger level, output
|
80
|
-
end
|
81
|
-
|
82
|
-
# TODO (pitr-ch 27-Dec-2016): remove deadlocking stdlib_logger methods
|
83
|
-
|
84
|
-
# Suppresses all output when used for logging.
|
85
|
-
NULL_LOGGER = lambda { |level, progname, message = nil, &block| }
|
86
|
-
|
87
|
-
# @!visibility private
|
88
|
-
GLOBAL_LOGGER = AtomicReference.new(create_simple_logger(Logger::WARN))
|
89
|
-
private_constant :GLOBAL_LOGGER
|
90
|
-
|
91
|
-
def self.global_logger
|
92
|
-
GLOBAL_LOGGER.value
|
93
|
-
end
|
94
|
-
|
95
|
-
def self.global_logger=(value)
|
96
|
-
GLOBAL_LOGGER.value = value
|
97
|
-
end
|
98
|
-
|
99
|
-
# @!visibility private
|
100
|
-
GLOBAL_FAST_EXECUTOR = Delay.new { Concurrent.new_fast_executor(auto_terminate: true) }
|
101
|
-
private_constant :GLOBAL_FAST_EXECUTOR
|
102
|
-
|
103
|
-
# @!visibility private
|
104
|
-
GLOBAL_IO_EXECUTOR = Delay.new { Concurrent.new_io_executor(auto_terminate: true) }
|
105
|
-
private_constant :GLOBAL_IO_EXECUTOR
|
106
|
-
|
107
|
-
# @!visibility private
|
108
|
-
GLOBAL_TIMER_SET = Delay.new { TimerSet.new(auto_terminate: true) }
|
109
|
-
private_constant :GLOBAL_TIMER_SET
|
110
|
-
|
111
|
-
# @!visibility private
|
112
|
-
GLOBAL_IMMEDIATE_EXECUTOR = ImmediateExecutor.new
|
113
|
-
private_constant :GLOBAL_IMMEDIATE_EXECUTOR
|
114
|
-
|
115
|
-
# Disables AtExit handlers including pool auto-termination handlers.
|
116
|
-
# When disabled it will be the application programmer's responsibility
|
117
|
-
# to ensure that the handlers are shutdown properly prior to application
|
118
|
-
# exit by calling {AtExit.run} method.
|
119
|
-
#
|
120
|
-
# @note this option should be needed only because of `at_exit` ordering
|
121
|
-
# issues which may arise when running some of the testing frameworks.
|
122
|
-
# E.g. Minitest's test-suite runs itself in `at_exit` callback which
|
123
|
-
# executes after the pools are already terminated. Then auto termination
|
124
|
-
# needs to be disabled and called manually after test-suite ends.
|
125
|
-
# @note This method should *never* be called
|
126
|
-
# from within a gem. It should *only* be used from within the main
|
127
|
-
# application and even then it should be used only when necessary.
|
128
|
-
# @see AtExit
|
129
|
-
def self.disable_at_exit_handlers!
|
130
|
-
AtExit.enabled = false
|
131
|
-
end
|
132
|
-
|
133
|
-
# Global thread pool optimized for short, fast *operations*.
|
134
|
-
#
|
135
|
-
# @return [ThreadPoolExecutor] the thread pool
|
136
|
-
def self.global_fast_executor
|
137
|
-
GLOBAL_FAST_EXECUTOR.value
|
138
|
-
end
|
139
|
-
|
140
|
-
# Global thread pool optimized for long, blocking (IO) *tasks*.
|
141
|
-
#
|
142
|
-
# @return [ThreadPoolExecutor] the thread pool
|
143
|
-
def self.global_io_executor
|
144
|
-
GLOBAL_IO_EXECUTOR.value
|
145
|
-
end
|
146
|
-
|
147
|
-
def self.global_immediate_executor
|
148
|
-
GLOBAL_IMMEDIATE_EXECUTOR
|
149
|
-
end
|
150
|
-
|
151
|
-
# Global thread pool user for global *timers*.
|
152
|
-
#
|
153
|
-
# @return [Concurrent::TimerSet] the thread pool
|
154
|
-
def self.global_timer_set
|
155
|
-
GLOBAL_TIMER_SET.value
|
156
|
-
end
|
157
|
-
|
158
|
-
# General access point to global executors.
|
159
|
-
# @param [Symbol, Executor] executor_identifier symbols:
|
160
|
-
# - :fast - {Concurrent.global_fast_executor}
|
161
|
-
# - :io - {Concurrent.global_io_executor}
|
162
|
-
# - :immediate - {Concurrent.global_immediate_executor}
|
163
|
-
# @return [Executor]
|
164
|
-
def self.executor(executor_identifier)
|
165
|
-
Options.executor(executor_identifier)
|
166
|
-
end
|
167
|
-
|
168
|
-
def self.new_fast_executor(opts = {})
|
169
|
-
FixedThreadPool.new(
|
170
|
-
[2, Concurrent.processor_count].max,
|
171
|
-
auto_terminate: opts.fetch(:auto_terminate, true),
|
172
|
-
idletime: 60, # 1 minute
|
173
|
-
max_queue: 0, # unlimited
|
174
|
-
fallback_policy: :abort # shouldn't matter -- 0 max queue
|
175
|
-
)
|
176
|
-
end
|
177
|
-
|
178
|
-
def self.new_io_executor(opts = {})
|
179
|
-
CachedThreadPool.new(
|
180
|
-
auto_terminate: opts.fetch(:auto_terminate, true),
|
181
|
-
fallback_policy: :abort # shouldn't matter -- 0 max queue
|
182
|
-
)
|
183
|
-
end
|
184
|
-
end
|
@@ -1,45 +0,0 @@
|
|
1
|
-
module Concurrent
|
2
|
-
module Synchronization
|
3
|
-
|
4
|
-
if Concurrent.on_jruby? && Concurrent.java_extensions_loaded?
|
5
|
-
|
6
|
-
# @!visibility private
|
7
|
-
module JRubyAttrVolatile
|
8
|
-
def self.included(base)
|
9
|
-
base.extend(ClassMethods)
|
10
|
-
end
|
11
|
-
|
12
|
-
module ClassMethods
|
13
|
-
def attr_volatile(*names)
|
14
|
-
names.each do |name|
|
15
|
-
|
16
|
-
ivar = :"@volatile_#{name}"
|
17
|
-
|
18
|
-
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
19
|
-
def #{name}
|
20
|
-
instance_variable_get_volatile(:#{ivar})
|
21
|
-
end
|
22
|
-
|
23
|
-
def #{name}=(value)
|
24
|
-
instance_variable_set_volatile(:#{ivar}, value)
|
25
|
-
end
|
26
|
-
RUBY
|
27
|
-
|
28
|
-
end
|
29
|
-
names.map { |n| [n, :"#{n}="] }.flatten
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
# @!visibility private
|
35
|
-
# @!macro internal_implementation_note
|
36
|
-
class JRubyObject < AbstractObject
|
37
|
-
include JRubyAttrVolatile
|
38
|
-
|
39
|
-
def initialize
|
40
|
-
# nothing to do
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
module Concurrent
|
2
|
-
module Synchronization
|
3
|
-
|
4
|
-
# @!visibility private
|
5
|
-
module MriAttrVolatile
|
6
|
-
def self.included(base)
|
7
|
-
base.extend(ClassMethods)
|
8
|
-
end
|
9
|
-
|
10
|
-
module ClassMethods
|
11
|
-
def attr_volatile(*names)
|
12
|
-
names.each do |name|
|
13
|
-
ivar = :"@volatile_#{name}"
|
14
|
-
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
15
|
-
def #{name}
|
16
|
-
#{ivar}
|
17
|
-
end
|
18
|
-
|
19
|
-
def #{name}=(value)
|
20
|
-
#{ivar} = value
|
21
|
-
end
|
22
|
-
RUBY
|
23
|
-
end
|
24
|
-
names.map { |n| [n, :"#{n}="] }.flatten
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def full_memory_barrier
|
29
|
-
# relying on undocumented behavior of CRuby, GVL acquire has lock which ensures visibility of ivars
|
30
|
-
# https://github.com/ruby/ruby/blob/ruby_2_2/thread_pthread.c#L204-L211
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
# @!visibility private
|
35
|
-
# @!macro internal_implementation_note
|
36
|
-
class MriObject < AbstractObject
|
37
|
-
include MriAttrVolatile
|
38
|
-
|
39
|
-
def initialize
|
40
|
-
# nothing to do
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
@@ -1,65 +0,0 @@
|
|
1
|
-
module Concurrent
|
2
|
-
module Synchronization
|
3
|
-
|
4
|
-
# @!visibility private
|
5
|
-
# @!macro internal_implementation_note
|
6
|
-
class RbxLockableObject < AbstractLockableObject
|
7
|
-
safe_initialization!
|
8
|
-
|
9
|
-
def initialize(*defaults)
|
10
|
-
super(*defaults)
|
11
|
-
@__Waiters__ = []
|
12
|
-
@__owner__ = nil
|
13
|
-
end
|
14
|
-
|
15
|
-
protected
|
16
|
-
|
17
|
-
def synchronize(&block)
|
18
|
-
if @__owner__ == Thread.current
|
19
|
-
yield
|
20
|
-
else
|
21
|
-
result = nil
|
22
|
-
Rubinius.synchronize(self) do
|
23
|
-
begin
|
24
|
-
@__owner__ = Thread.current
|
25
|
-
result = yield
|
26
|
-
ensure
|
27
|
-
@__owner__ = nil
|
28
|
-
end
|
29
|
-
end
|
30
|
-
result
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def ns_wait(timeout = nil)
|
35
|
-
wchan = Rubinius::Channel.new
|
36
|
-
|
37
|
-
begin
|
38
|
-
@__Waiters__.push wchan
|
39
|
-
Rubinius.unlock(self)
|
40
|
-
signaled = wchan.receive_timeout timeout
|
41
|
-
ensure
|
42
|
-
Rubinius.lock(self)
|
43
|
-
|
44
|
-
if !signaled && !@__Waiters__.delete(wchan)
|
45
|
-
# we timed out, but got signaled afterwards,
|
46
|
-
# so pass that signal on to the next waiter
|
47
|
-
@__Waiters__.shift << true unless @__Waiters__.empty?
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
self
|
52
|
-
end
|
53
|
-
|
54
|
-
def ns_signal
|
55
|
-
@__Waiters__.shift << true unless @__Waiters__.empty?
|
56
|
-
self
|
57
|
-
end
|
58
|
-
|
59
|
-
def ns_broadcast
|
60
|
-
@__Waiters__.shift << true until @__Waiters__.empty?
|
61
|
-
self
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
@@ -1,49 +0,0 @@
|
|
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
|