concurrent-ruby 1.0.5 → 1.1.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +155 -0
- data/Gemfile +37 -0
- data/LICENSE.txt +18 -18
- data/README.md +260 -103
- data/Rakefile +329 -0
- data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +189 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +307 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/agent.rb +7 -7
- data/lib/concurrent-ruby/concurrent/array.rb +66 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/async.rb +28 -24
- data/lib/{concurrent → concurrent-ruby/concurrent}/atom.rb +10 -10
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_boolean.rb +26 -22
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_fixnum.rb +27 -23
- data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +164 -0
- data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +205 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/count_down_latch.rb +7 -7
- 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 → concurrent-ruby/concurrent}/atomic/java_count_down_latch.rb +9 -6
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_boolean.rb +2 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_count_down_latch.rb +1 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_semaphore.rb +18 -2
- 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 +7 -7
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/ruby_thread_local_var.rb +60 -40
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/semaphore.rb +34 -13
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/thread_local_var.rb +8 -8
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/mutex_atomic.rb +3 -8
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/numeric_cas_wrapper.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomics.rb +10 -0
- data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +158 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/atomic_reference_map_backend.rb +3 -3
- 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 +1 -2
- data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/non_concurrent_priority_queue.rb +30 -30
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/ruby_non_concurrent_priority_queue.rb +11 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/concern/dereferenceable.rb +3 -3
- data/lib/{concurrent → concurrent-ruby/concurrent}/concern/logging.rb +6 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/concern/observable.rb +7 -7
- data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/configuration.rb +15 -15
- data/lib/{concurrent → concurrent-ruby/concurrent}/constants.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/dataflow.rb +2 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/delay.rb +9 -7
- data/lib/{concurrent → concurrent-ruby/concurrent}/exchanger.rb +21 -25
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/abstract_executor_service.rb +35 -38
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/cached_thread_pool.rb +5 -5
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/executor_service.rb +17 -17
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/fixed_thread_pool.rb +47 -33
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_executor_service.rb +20 -17
- 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 +29 -9
- 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 +5 -5
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/simple_executor_service.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/single_thread_executor.rb +3 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/thread_pool_executor.rb +7 -6
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/timer_set.rb +14 -17
- data/lib/{concurrent → concurrent-ruby/concurrent}/future.rb +4 -1
- data/lib/concurrent-ruby/concurrent/hash.rb +59 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/immutable_struct.rb +9 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/ivar.rb +5 -6
- data/lib/concurrent-ruby/concurrent/map.rb +346 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/maybe.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/mutable_struct.rb +27 -16
- data/lib/{concurrent → concurrent-ruby/concurrent}/mvar.rb +2 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/promise.rb +54 -21
- data/lib/concurrent-ruby/concurrent/promises.rb +2167 -0
- data/lib/concurrent-ruby/concurrent/re_include.rb +58 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/scheduled_task.rb +29 -16
- data/lib/concurrent-ruby/concurrent/set.rb +74 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/settable_struct.rb +12 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_lockable_object.rb +5 -5
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_struct.rb +18 -4
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/condition.rb +2 -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 +8 -10
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/mri_object.rb +1 -0
- data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +88 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/object.rb +53 -23
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/rbx_lockable_object.rb +6 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/rbx_object.rb +1 -0
- data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +47 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/volatile.rb +11 -9
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization.rb +4 -5
- data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +88 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/striped64.rb +9 -4
- data/lib/{concurrent → concurrent-ruby/concurrent}/timer_task.rb +15 -35
- data/lib/{concurrent → concurrent-ruby/concurrent}/tuple.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/tvar.rb +21 -58
- data/lib/{concurrent → concurrent-ruby/concurrent}/utility/engine.rb +4 -4
- data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +90 -0
- data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +79 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/utility/processor_counter.rb +5 -35
- data/lib/concurrent-ruby/concurrent/version.rb +3 -0
- data/lib/concurrent-ruby/concurrent-ruby.rb +5 -0
- data/lib/{concurrent.rb → concurrent-ruby/concurrent.rb} +24 -20
- metadata +149 -134
- data/lib/concurrent/array.rb +0 -39
- data/lib/concurrent/atomic/atomic_reference.rb +0 -51
- data/lib/concurrent/atomic_reference/concurrent_update_error.rb +0 -8
- data/lib/concurrent/atomic_reference/direct_update.rb +0 -81
- data/lib/concurrent/atomic_reference/jruby+truffle.rb +0 -2
- data/lib/concurrent/atomic_reference/jruby.rb +0 -16
- data/lib/concurrent/atomic_reference/rbx.rb +0 -22
- data/lib/concurrent/atomic_reference/ruby.rb +0 -32
- data/lib/concurrent/atomics.rb +0 -53
- data/lib/concurrent/edge.rb +0 -26
- data/lib/concurrent/hash.rb +0 -36
- data/lib/concurrent/lazy_register.rb +0 -81
- data/lib/concurrent/map.rb +0 -240
- data/lib/concurrent/synchronization/mri_lockable_object.rb +0 -71
- data/lib/concurrent/synchronization/truffle_lockable_object.rb +0 -9
- data/lib/concurrent/synchronization/truffle_object.rb +0 -31
- data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +0 -30
- data/lib/concurrent/utility/at_exit.rb +0 -97
- data/lib/concurrent/utility/monotonic_time.rb +0 -58
- data/lib/concurrent/utility/native_extension_loader.rb +0 -73
- data/lib/concurrent/version.rb +0 -4
- /data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/abstract_thread_local_var.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_fixnum.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/map/synchronized_map_backend.rb +0 -0
- /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}/errors.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.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}/options.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_object.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_lockable_object.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/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/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 → concurrent-ruby/concurrent}/utility/native_integer.rb +0 -0
@@ -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
|
@@ -5,7 +5,7 @@ module Concurrent
|
|
5
5
|
|
6
6
|
###################################################################
|
7
7
|
|
8
|
-
# @!macro
|
8
|
+
# @!macro semaphore_method_initialize
|
9
9
|
#
|
10
10
|
# Create a new `Semaphore` with the initial `count`.
|
11
11
|
#
|
@@ -13,35 +13,39 @@ module Concurrent
|
|
13
13
|
#
|
14
14
|
# @raise [ArgumentError] if `count` is not an integer or is less than zero
|
15
15
|
|
16
|
-
# @!macro
|
16
|
+
# @!macro semaphore_method_acquire
|
17
17
|
#
|
18
18
|
# Acquires the given number of permits from this semaphore,
|
19
|
-
# blocking until all are available.
|
19
|
+
# blocking until all are available. If a block is given,
|
20
|
+
# yields to it and releases the permits afterwards.
|
20
21
|
#
|
21
22
|
# @param [Fixnum] permits Number of permits to acquire
|
22
23
|
#
|
23
24
|
# @raise [ArgumentError] if `permits` is not an integer or is less than
|
24
25
|
# one
|
25
26
|
#
|
26
|
-
# @return [nil]
|
27
|
+
# @return [nil, BasicObject] Without a block, `nil` is returned. If a block
|
28
|
+
# is given, its return value is returned.
|
27
29
|
|
28
|
-
# @!macro
|
30
|
+
# @!macro semaphore_method_available_permits
|
29
31
|
#
|
30
32
|
# Returns the current number of permits available in this semaphore.
|
31
33
|
#
|
32
34
|
# @return [Integer]
|
33
35
|
|
34
|
-
# @!macro
|
36
|
+
# @!macro semaphore_method_drain_permits
|
35
37
|
#
|
36
38
|
# Acquires and returns all permits that are immediately available.
|
37
39
|
#
|
38
40
|
# @return [Integer]
|
39
41
|
|
40
|
-
# @!macro
|
42
|
+
# @!macro semaphore_method_try_acquire
|
41
43
|
#
|
42
44
|
# Acquires the given number of permits from this semaphore,
|
43
45
|
# only if all are available at the time of invocation or within
|
44
|
-
# `timeout` interval
|
46
|
+
# `timeout` interval. If a block is given, yields to it if the permits
|
47
|
+
# were successfully acquired, and releases them afterward, returning the
|
48
|
+
# block's return value.
|
45
49
|
#
|
46
50
|
# @param [Fixnum] permits the number of permits to acquire
|
47
51
|
#
|
@@ -51,10 +55,12 @@ module Concurrent
|
|
51
55
|
# @raise [ArgumentError] if `permits` is not an integer or is less than
|
52
56
|
# one
|
53
57
|
#
|
54
|
-
# @return [
|
55
|
-
# acquired a permit
|
58
|
+
# @return [true, false, nil, BasicObject] `false` if no permits are
|
59
|
+
# available, `true` when acquired a permit. If a block is given, the
|
60
|
+
# block's return value is returned if the permits were acquired; if not,
|
61
|
+
# `nil` is returned.
|
56
62
|
|
57
|
-
# @!macro
|
63
|
+
# @!macro semaphore_method_release
|
58
64
|
#
|
59
65
|
# Releases the given number of permits, returning them to the semaphore.
|
60
66
|
#
|
@@ -66,7 +72,7 @@ module Concurrent
|
|
66
72
|
|
67
73
|
###################################################################
|
68
74
|
|
69
|
-
# @!macro
|
75
|
+
# @!macro semaphore_public_api
|
70
76
|
#
|
71
77
|
# @!method initialize(count)
|
72
78
|
# @!macro semaphore_method_initialize
|
@@ -98,7 +104,7 @@ module Concurrent
|
|
98
104
|
end
|
99
105
|
private_constant :SemaphoreImplementation
|
100
106
|
|
101
|
-
# @!macro
|
107
|
+
# @!macro semaphore
|
102
108
|
#
|
103
109
|
# A counting semaphore. Conceptually, a semaphore maintains a set of
|
104
110
|
# permits. Each {#acquire} blocks if necessary until a permit is
|
@@ -106,6 +112,8 @@ module Concurrent
|
|
106
112
|
# releasing a blocking acquirer.
|
107
113
|
# However, no actual permit objects are used; the Semaphore just keeps a
|
108
114
|
# count of the number available and acts accordingly.
|
115
|
+
# Alternatively, permits may be acquired within a block, and automatically
|
116
|
+
# released after the block finishes executing.
|
109
117
|
#
|
110
118
|
# @!macro semaphore_public_api
|
111
119
|
# @example
|
@@ -140,6 +148,19 @@ module Concurrent
|
|
140
148
|
# # Thread 4 releasing semaphore
|
141
149
|
# # Thread 1 acquired semaphore
|
142
150
|
#
|
151
|
+
# @example
|
152
|
+
# semaphore = Concurrent::Semaphore.new(1)
|
153
|
+
#
|
154
|
+
# puts semaphore.available_permits
|
155
|
+
# semaphore.acquire do
|
156
|
+
# puts semaphore.available_permits
|
157
|
+
# end
|
158
|
+
# puts semaphore.available_permits
|
159
|
+
#
|
160
|
+
# # prints:
|
161
|
+
# # 1
|
162
|
+
# # 0
|
163
|
+
# # 1
|
143
164
|
class Semaphore < SemaphoreImplementation
|
144
165
|
end
|
145
166
|
end
|
@@ -1,12 +1,12 @@
|
|
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
|
|
7
7
|
###################################################################
|
8
8
|
|
9
|
-
# @!macro
|
9
|
+
# @!macro thread_local_var_method_initialize
|
10
10
|
#
|
11
11
|
# Creates a thread local variable.
|
12
12
|
#
|
@@ -14,20 +14,20 @@ module Concurrent
|
|
14
14
|
# @param [Proc] default_block Optional block that gets called to obtain the
|
15
15
|
# default value for each thread
|
16
16
|
|
17
|
-
# @!macro
|
17
|
+
# @!macro thread_local_var_method_get
|
18
18
|
#
|
19
19
|
# Returns the value in the current thread's copy of this thread-local variable.
|
20
20
|
#
|
21
21
|
# @return [Object] the current value
|
22
22
|
|
23
|
-
# @!macro
|
23
|
+
# @!macro thread_local_var_method_set
|
24
24
|
#
|
25
25
|
# Sets the current thread's copy of this thread-local variable to the specified value.
|
26
26
|
#
|
27
27
|
# @param [Object] value the value to set
|
28
28
|
# @return [Object] the new value
|
29
29
|
|
30
|
-
# @!macro
|
30
|
+
# @!macro thread_local_var_method_bind
|
31
31
|
#
|
32
32
|
# Bind the given value to thread local storage during
|
33
33
|
# execution of the given block.
|
@@ -39,9 +39,9 @@ module Concurrent
|
|
39
39
|
|
40
40
|
###################################################################
|
41
41
|
|
42
|
-
# @!macro
|
42
|
+
# @!macro thread_local_var_public_api
|
43
43
|
#
|
44
|
-
# @!method initialize(default = nil)
|
44
|
+
# @!method initialize(default = nil, &default_block)
|
45
45
|
# @!macro thread_local_var_method_initialize
|
46
46
|
#
|
47
47
|
# @!method value
|
@@ -65,7 +65,7 @@ module Concurrent
|
|
65
65
|
end
|
66
66
|
private_constant :ThreadLocalVarImplementation
|
67
67
|
|
68
|
-
# @!macro
|
68
|
+
# @!macro thread_local_var
|
69
69
|
#
|
70
70
|
# A `ThreadLocalVar` is a variable where the value is different for each thread.
|
71
71
|
# Each variable may have a default value, but when you modify the variable only
|
@@ -1,16 +1,11 @@
|
|
1
|
-
require 'concurrent/synchronization'
|
2
|
-
require 'concurrent/atomic_reference/direct_update'
|
3
|
-
require 'concurrent/atomic_reference/numeric_cas_wrapper'
|
4
|
-
|
5
1
|
module Concurrent
|
6
2
|
|
7
|
-
# @!macro atomic_reference
|
8
|
-
#
|
9
3
|
# @!visibility private
|
10
4
|
# @!macro internal_implementation_note
|
11
5
|
class MutexAtomicReference < Synchronization::LockableObject
|
12
|
-
include
|
13
|
-
include
|
6
|
+
include AtomicDirectUpdate
|
7
|
+
include AtomicNumericCompareAndSetWrapper
|
8
|
+
alias_method :compare_and_swap, :compare_and_set
|
14
9
|
|
15
10
|
# @!macro atomic_reference_method_initialize
|
16
11
|
def initialize(value = nil)
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'concurrent/atomic/atomic_reference'
|
2
|
+
require 'concurrent/atomic/atomic_boolean'
|
3
|
+
require 'concurrent/atomic/atomic_fixnum'
|
4
|
+
require 'concurrent/atomic/cyclic_barrier'
|
5
|
+
require 'concurrent/atomic/count_down_latch'
|
6
|
+
require 'concurrent/atomic/event'
|
7
|
+
require 'concurrent/atomic/read_write_lock'
|
8
|
+
require 'concurrent/atomic/reentrant_read_write_lock'
|
9
|
+
require 'concurrent/atomic/semaphore'
|
10
|
+
require 'concurrent/atomic/thread_local_var'
|
@@ -0,0 +1,158 @@
|
|
1
|
+
module Concurrent
|
2
|
+
|
3
|
+
# @!macro warn.edge
|
4
|
+
class LockFreeStack < Synchronization::Object
|
5
|
+
|
6
|
+
safe_initialization!
|
7
|
+
|
8
|
+
class Node
|
9
|
+
# TODO (pitr-ch 20-Dec-2016): Could be unified with Stack class?
|
10
|
+
|
11
|
+
# @return [Node]
|
12
|
+
attr_reader :next_node
|
13
|
+
|
14
|
+
# @return [Object]
|
15
|
+
attr_reader :value
|
16
|
+
|
17
|
+
# @!visibility private
|
18
|
+
# allow to nil-ify to free GC when the entry is no longer relevant, not synchronised
|
19
|
+
attr_writer :value
|
20
|
+
|
21
|
+
def initialize(value, next_node)
|
22
|
+
@value = value
|
23
|
+
@next_node = next_node
|
24
|
+
end
|
25
|
+
|
26
|
+
singleton_class.send :alias_method, :[], :new
|
27
|
+
end
|
28
|
+
|
29
|
+
# The singleton for empty node
|
30
|
+
EMPTY = Node[nil, nil]
|
31
|
+
def EMPTY.next_node
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_atomic(:head)
|
36
|
+
private :head, :head=, :swap_head, :compare_and_set_head, :update_head
|
37
|
+
|
38
|
+
# @!visibility private
|
39
|
+
def self.of1(value)
|
40
|
+
new Node[value, EMPTY]
|
41
|
+
end
|
42
|
+
|
43
|
+
# @!visibility private
|
44
|
+
def self.of2(value1, value2)
|
45
|
+
new Node[value1, Node[value2, EMPTY]]
|
46
|
+
end
|
47
|
+
|
48
|
+
# @param [Node] head
|
49
|
+
def initialize(head = EMPTY)
|
50
|
+
super()
|
51
|
+
self.head = head
|
52
|
+
end
|
53
|
+
|
54
|
+
# @param [Node] head
|
55
|
+
# @return [true, false]
|
56
|
+
def empty?(head = head())
|
57
|
+
head.equal? EMPTY
|
58
|
+
end
|
59
|
+
|
60
|
+
# @param [Node] head
|
61
|
+
# @param [Object] value
|
62
|
+
# @return [true, false]
|
63
|
+
def compare_and_push(head, value)
|
64
|
+
compare_and_set_head head, Node[value, head]
|
65
|
+
end
|
66
|
+
|
67
|
+
# @param [Object] value
|
68
|
+
# @return [self]
|
69
|
+
def push(value)
|
70
|
+
while true
|
71
|
+
current_head = head
|
72
|
+
return self if compare_and_set_head current_head, Node[value, current_head]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [Node]
|
77
|
+
def peek
|
78
|
+
head
|
79
|
+
end
|
80
|
+
|
81
|
+
# @param [Node] head
|
82
|
+
# @return [true, false]
|
83
|
+
def compare_and_pop(head)
|
84
|
+
compare_and_set_head head, head.next_node
|
85
|
+
end
|
86
|
+
|
87
|
+
# @return [Object]
|
88
|
+
def pop
|
89
|
+
while true
|
90
|
+
current_head = head
|
91
|
+
return current_head.value if compare_and_set_head current_head, current_head.next_node
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# @param [Node] head
|
96
|
+
# @return [true, false]
|
97
|
+
def compare_and_clear(head)
|
98
|
+
compare_and_set_head head, EMPTY
|
99
|
+
end
|
100
|
+
|
101
|
+
include Enumerable
|
102
|
+
|
103
|
+
# @param [Node] head
|
104
|
+
# @return [self]
|
105
|
+
def each(head = nil)
|
106
|
+
return to_enum(:each, head) unless block_given?
|
107
|
+
it = head || peek
|
108
|
+
until it.equal?(EMPTY)
|
109
|
+
yield it.value
|
110
|
+
it = it.next_node
|
111
|
+
end
|
112
|
+
self
|
113
|
+
end
|
114
|
+
|
115
|
+
# @return [true, false]
|
116
|
+
def clear
|
117
|
+
while true
|
118
|
+
current_head = head
|
119
|
+
return false if current_head == EMPTY
|
120
|
+
return true if compare_and_set_head current_head, EMPTY
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# @param [Node] head
|
125
|
+
# @return [true, false]
|
126
|
+
def clear_if(head)
|
127
|
+
compare_and_set_head head, EMPTY
|
128
|
+
end
|
129
|
+
|
130
|
+
# @param [Node] head
|
131
|
+
# @param [Node] new_head
|
132
|
+
# @return [true, false]
|
133
|
+
def replace_if(head, new_head)
|
134
|
+
compare_and_set_head head, new_head
|
135
|
+
end
|
136
|
+
|
137
|
+
# @return [self]
|
138
|
+
# @yield over the cleared stack
|
139
|
+
# @yieldparam [Object] value
|
140
|
+
def clear_each(&block)
|
141
|
+
while true
|
142
|
+
current_head = head
|
143
|
+
return self if current_head == EMPTY
|
144
|
+
if compare_and_set_head current_head, EMPTY
|
145
|
+
each current_head, &block
|
146
|
+
return self
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# @return [String] Short string representation.
|
152
|
+
def to_s
|
153
|
+
format '%s %s>', super[0..-2], to_a.to_s
|
154
|
+
end
|
155
|
+
|
156
|
+
alias_method :inspect, :to_s
|
157
|
+
end
|
158
|
+
end
|
data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/atomic_reference_map_backend.rb
RENAMED
@@ -289,7 +289,7 @@ module Concurrent
|
|
289
289
|
end
|
290
290
|
end
|
291
291
|
elsif cas_hash(my_hash, my_hash | WAITING)
|
292
|
-
|
292
|
+
force_acquire_lock(table, i)
|
293
293
|
break
|
294
294
|
end
|
295
295
|
end
|
@@ -330,7 +330,7 @@ module Concurrent
|
|
330
330
|
end
|
331
331
|
|
332
332
|
private
|
333
|
-
def
|
333
|
+
def force_acquire_lock(table, i)
|
334
334
|
cheap_synchronize do
|
335
335
|
if equal?(table.volatile_get(i)) && (hash & WAITING) == WAITING
|
336
336
|
cheap_wait
|
@@ -831,7 +831,7 @@ module Concurrent
|
|
831
831
|
# no lock needed (or available) if bin >= 0, because we're not popping values from locked_indexes until we've run through the whole table
|
832
832
|
redo unless (bin >= 0 ? table.cas(i, nil, forwarder) : lock_and_clean_up_reverse_forwarders(table, old_table_size, new_table, i, forwarder))
|
833
833
|
elsif Node.locked_hash?(node_hash = node.hash)
|
834
|
-
locked_indexes ||= Array.new
|
834
|
+
locked_indexes ||= ::Array.new
|
835
835
|
if bin < 0 && locked_arr_idx > 0
|
836
836
|
locked_arr_idx -= 1
|
837
837
|
i, locked_indexes[locked_arr_idx] = locked_indexes[locked_arr_idx], i # swap with another bin
|
@@ -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 }
|
data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/non_concurrent_map_backend.rb
RENAMED
@@ -10,7 +10,7 @@ module Concurrent
|
|
10
10
|
|
11
11
|
# WARNING: all public methods of the class must operate on the @backend
|
12
12
|
# directly without calling each other. This is important because of the
|
13
|
-
# SynchronizedMapBackend which uses a non-reentrant mutex for
|
13
|
+
# SynchronizedMapBackend which uses a non-reentrant mutex for performance
|
14
14
|
# reasons.
|
15
15
|
def initialize(options = nil)
|
16
16
|
@backend = {}
|
@@ -95,7 +95,6 @@ module Concurrent
|
|
95
95
|
end
|
96
96
|
|
97
97
|
def each_pair
|
98
|
-
return enum_for :each_pair unless block_given?
|
99
98
|
dupped_backend.each_pair do |k, v|
|
100
99
|
yield k, v
|
101
100
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Concurrent
|
2
|
+
|
3
|
+
# @!visibility private
|
4
|
+
module Collection
|
5
|
+
|
6
|
+
# @!visibility private
|
7
|
+
class TruffleRubyMapBackend < TruffleRuby::ConcurrentMap
|
8
|
+
def initialize(options = nil)
|
9
|
+
options ||= {}
|
10
|
+
super(initial_capacity: options[:initial_capacity], load_factor: options[:load_factor])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|