concurrent-ruby 1.1.5 → 1.2.2
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 +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,4 +1,4 @@
|
|
1
|
-
require 'concurrent/synchronization'
|
1
|
+
require 'concurrent/synchronization/lockable_object'
|
2
2
|
require 'concurrent/utility/native_integer'
|
3
3
|
|
4
4
|
module Concurrent
|
@@ -23,7 +23,14 @@ module Concurrent
|
|
23
23
|
|
24
24
|
synchronize do
|
25
25
|
try_acquire_timed(permits, nil)
|
26
|
-
|
26
|
+
end
|
27
|
+
|
28
|
+
return unless block_given?
|
29
|
+
|
30
|
+
begin
|
31
|
+
yield
|
32
|
+
ensure
|
33
|
+
release(permits)
|
27
34
|
end
|
28
35
|
end
|
29
36
|
|
@@ -48,13 +55,22 @@ module Concurrent
|
|
48
55
|
Utility::NativeInteger.ensure_integer_and_bounds permits
|
49
56
|
Utility::NativeInteger.ensure_positive permits
|
50
57
|
|
51
|
-
synchronize do
|
58
|
+
acquired = synchronize do
|
52
59
|
if timeout.nil?
|
53
60
|
try_acquire_now(permits)
|
54
61
|
else
|
55
62
|
try_acquire_timed(permits, timeout)
|
56
63
|
end
|
57
64
|
end
|
65
|
+
|
66
|
+
return acquired unless block_given?
|
67
|
+
return unless acquired
|
68
|
+
|
69
|
+
begin
|
70
|
+
yield
|
71
|
+
ensure
|
72
|
+
release(permits)
|
73
|
+
end
|
58
74
|
end
|
59
75
|
|
60
76
|
# @!macro semaphore_method_release
|
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'thread'
|
2
2
|
require 'concurrent/atomic/atomic_reference'
|
3
|
+
require 'concurrent/atomic/atomic_fixnum'
|
3
4
|
require 'concurrent/errors'
|
4
|
-
require 'concurrent/synchronization'
|
5
|
-
require 'concurrent/
|
5
|
+
require 'concurrent/synchronization/object'
|
6
|
+
require 'concurrent/synchronization/lock'
|
7
|
+
require 'concurrent/atomic/lock_local_var'
|
6
8
|
|
7
9
|
module Concurrent
|
8
10
|
|
@@ -109,7 +111,7 @@ module Concurrent
|
|
109
111
|
@Counter = AtomicFixnum.new(0) # single integer which represents lock state
|
110
112
|
@ReadQueue = Synchronization::Lock.new # used to queue waiting readers
|
111
113
|
@WriteQueue = Synchronization::Lock.new # used to queue waiting writers
|
112
|
-
@HeldCount =
|
114
|
+
@HeldCount = LockLocalVar.new(0) # indicates # of R & W locks held by this thread
|
113
115
|
end
|
114
116
|
|
115
117
|
# Execute a block operation within a read lock.
|
@@ -267,12 +269,10 @@ module Concurrent
|
|
267
269
|
# running right now, AND no writers who came before us still waiting to
|
268
270
|
# acquire the lock
|
269
271
|
# Additionally, if any read locks have been taken, we must hold all of them
|
270
|
-
if
|
271
|
-
# If we successfully swap the RUNNING_WRITER bit on, then we can go ahead
|
272
|
-
|
273
|
-
|
274
|
-
return true
|
275
|
-
end
|
272
|
+
if held > 0 && @Counter.compare_and_set(1, c+RUNNING_WRITER)
|
273
|
+
# If we are the only one reader and successfully swap the RUNNING_WRITER bit on, then we can go ahead
|
274
|
+
@HeldCount.value = held + WRITE_LOCK_HELD
|
275
|
+
return true
|
276
276
|
elsif @Counter.compare_and_set(c, c+WAITING_WRITER)
|
277
277
|
while true
|
278
278
|
# Now we have successfully incremented, so no more readers will be able to increment
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'concurrent/atomic/mutex_semaphore'
|
2
|
-
require 'concurrent/synchronization'
|
3
2
|
|
4
3
|
module Concurrent
|
5
4
|
|
@@ -11,19 +10,20 @@ module Concurrent
|
|
11
10
|
#
|
12
11
|
# @param [Fixnum] count the initial count
|
13
12
|
#
|
14
|
-
# @raise [ArgumentError] if `count` is not an integer
|
13
|
+
# @raise [ArgumentError] if `count` is not an integer
|
15
14
|
|
16
15
|
# @!macro semaphore_method_acquire
|
17
16
|
#
|
18
17
|
# Acquires the given number of permits from this semaphore,
|
19
|
-
# blocking until all are available.
|
18
|
+
# blocking until all are available. If a block is given,
|
19
|
+
# yields to it and releases the permits afterwards.
|
20
20
|
#
|
21
21
|
# @param [Fixnum] permits Number of permits to acquire
|
22
22
|
#
|
23
|
-
# @raise [ArgumentError] if `permits` is not an integer or is less than
|
24
|
-
# one
|
23
|
+
# @raise [ArgumentError] if `permits` is not an integer or is less than zero
|
25
24
|
#
|
26
|
-
# @return [nil]
|
25
|
+
# @return [nil, BasicObject] Without a block, `nil` is returned. If a block
|
26
|
+
# is given, its return value is returned.
|
27
27
|
|
28
28
|
# @!macro semaphore_method_available_permits
|
29
29
|
#
|
@@ -41,18 +41,21 @@ module Concurrent
|
|
41
41
|
#
|
42
42
|
# Acquires the given number of permits from this semaphore,
|
43
43
|
# only if all are available at the time of invocation or within
|
44
|
-
# `timeout` interval
|
44
|
+
# `timeout` interval. If a block is given, yields to it if the permits
|
45
|
+
# were successfully acquired, and releases them afterward, returning the
|
46
|
+
# block's return value.
|
45
47
|
#
|
46
48
|
# @param [Fixnum] permits the number of permits to acquire
|
47
49
|
#
|
48
50
|
# @param [Fixnum] timeout the number of seconds to wait for the counter
|
49
51
|
# or `nil` to return immediately
|
50
52
|
#
|
51
|
-
# @raise [ArgumentError] if `permits` is not an integer or is less than
|
52
|
-
# one
|
53
|
+
# @raise [ArgumentError] if `permits` is not an integer or is less than zero
|
53
54
|
#
|
54
|
-
# @return [
|
55
|
-
# acquired a permit
|
55
|
+
# @return [true, false, nil, BasicObject] `false` if no permits are
|
56
|
+
# available, `true` when acquired a permit. If a block is given, the
|
57
|
+
# block's return value is returned if the permits were acquired; if not,
|
58
|
+
# `nil` is returned.
|
56
59
|
|
57
60
|
# @!macro semaphore_method_release
|
58
61
|
#
|
@@ -60,7 +63,7 @@ module Concurrent
|
|
60
63
|
#
|
61
64
|
# @param [Fixnum] permits Number of permits to return to the semaphore.
|
62
65
|
#
|
63
|
-
# @raise [ArgumentError] if `permits` is not a number or is less than
|
66
|
+
# @raise [ArgumentError] if `permits` is not a number or is less than zero
|
64
67
|
#
|
65
68
|
# @return [nil]
|
66
69
|
|
@@ -90,8 +93,8 @@ module Concurrent
|
|
90
93
|
|
91
94
|
# @!visibility private
|
92
95
|
# @!macro internal_implementation_note
|
93
|
-
SemaphoreImplementation =
|
94
|
-
|
96
|
+
SemaphoreImplementation = if Concurrent.on_jruby?
|
97
|
+
require 'concurrent/utility/native_extension_loader'
|
95
98
|
JavaSemaphore
|
96
99
|
else
|
97
100
|
MutexSemaphore
|
@@ -106,6 +109,8 @@ module Concurrent
|
|
106
109
|
# releasing a blocking acquirer.
|
107
110
|
# However, no actual permit objects are used; the Semaphore just keeps a
|
108
111
|
# count of the number available and acts accordingly.
|
112
|
+
# Alternatively, permits may be acquired within a block, and automatically
|
113
|
+
# released after the block finishes executing.
|
109
114
|
#
|
110
115
|
# @!macro semaphore_public_api
|
111
116
|
# @example
|
@@ -140,6 +145,19 @@ module Concurrent
|
|
140
145
|
# # Thread 4 releasing semaphore
|
141
146
|
# # Thread 1 acquired semaphore
|
142
147
|
#
|
148
|
+
# @example
|
149
|
+
# semaphore = Concurrent::Semaphore.new(1)
|
150
|
+
#
|
151
|
+
# puts semaphore.available_permits
|
152
|
+
# semaphore.acquire do
|
153
|
+
# puts semaphore.available_permits
|
154
|
+
# end
|
155
|
+
# puts semaphore.available_permits
|
156
|
+
#
|
157
|
+
# # prints:
|
158
|
+
# # 1
|
159
|
+
# # 0
|
160
|
+
# # 1
|
143
161
|
class Semaphore < SemaphoreImplementation
|
144
162
|
end
|
145
163
|
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'concurrent/constants'
|
2
|
+
require_relative 'locals'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
# A `ThreadLocalVar` is a variable where the value is different for each thread.
|
7
|
+
# Each variable may have a default value, but when you modify the variable only
|
8
|
+
# the current thread will ever see that change.
|
9
|
+
#
|
10
|
+
# This is similar to Ruby's built-in thread-local variables (`Thread#thread_variable_get`),
|
11
|
+
# but with these major advantages:
|
12
|
+
# * `ThreadLocalVar` has its own identity, it doesn't need a Symbol.
|
13
|
+
# * Each Ruby's built-in thread-local variable leaks some memory forever (it's a Symbol held forever on the thread),
|
14
|
+
# so it's only OK to create a small amount of them.
|
15
|
+
# `ThreadLocalVar` has no such issue and it is fine to create many of them.
|
16
|
+
# * Ruby's built-in thread-local variables leak forever the value set on each thread (unless set to nil explicitly).
|
17
|
+
# `ThreadLocalVar` automatically removes the mapping for each thread once the `ThreadLocalVar` instance is GC'd.
|
18
|
+
#
|
19
|
+
# @!macro thread_safe_variable_comparison
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# v = ThreadLocalVar.new(14)
|
23
|
+
# v.value #=> 14
|
24
|
+
# v.value = 2
|
25
|
+
# v.value #=> 2
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# v = ThreadLocalVar.new(14)
|
29
|
+
#
|
30
|
+
# t1 = Thread.new do
|
31
|
+
# v.value #=> 14
|
32
|
+
# v.value = 1
|
33
|
+
# v.value #=> 1
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# t2 = Thread.new do
|
37
|
+
# v.value #=> 14
|
38
|
+
# v.value = 2
|
39
|
+
# v.value #=> 2
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# v.value #=> 14
|
43
|
+
class ThreadLocalVar
|
44
|
+
LOCALS = ThreadLocals.new
|
45
|
+
|
46
|
+
# Creates a thread local variable.
|
47
|
+
#
|
48
|
+
# @param [Object] default the default value when otherwise unset
|
49
|
+
# @param [Proc] default_block Optional block that gets called to obtain the
|
50
|
+
# default value for each thread
|
51
|
+
def initialize(default = nil, &default_block)
|
52
|
+
if default && block_given?
|
53
|
+
raise ArgumentError, "Cannot use both value and block as default value"
|
54
|
+
end
|
55
|
+
|
56
|
+
if block_given?
|
57
|
+
@default_block = default_block
|
58
|
+
@default = nil
|
59
|
+
else
|
60
|
+
@default_block = nil
|
61
|
+
@default = default
|
62
|
+
end
|
63
|
+
|
64
|
+
@index = LOCALS.next_index(self)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns the value in the current thread's copy of this thread-local variable.
|
68
|
+
#
|
69
|
+
# @return [Object] the current value
|
70
|
+
def value
|
71
|
+
LOCALS.fetch(@index) { default }
|
72
|
+
end
|
73
|
+
|
74
|
+
# Sets the current thread's copy of this thread-local variable to the specified value.
|
75
|
+
#
|
76
|
+
# @param [Object] value the value to set
|
77
|
+
# @return [Object] the new value
|
78
|
+
def value=(value)
|
79
|
+
LOCALS.set(@index, value)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Bind the given value to thread local storage during
|
83
|
+
# execution of the given block.
|
84
|
+
#
|
85
|
+
# @param [Object] value the value to bind
|
86
|
+
# @yield the operation to be performed with the bound variable
|
87
|
+
# @return [Object] the value
|
88
|
+
def bind(value)
|
89
|
+
if block_given?
|
90
|
+
old_value = self.value
|
91
|
+
self.value = value
|
92
|
+
begin
|
93
|
+
yield
|
94
|
+
ensure
|
95
|
+
self.value = old_value
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
protected
|
101
|
+
|
102
|
+
# @!visibility private
|
103
|
+
def default
|
104
|
+
if @default_block
|
105
|
+
self.value = @default_block.call
|
106
|
+
else
|
107
|
+
@default
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'concurrent/errors'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
# Define update methods that use direct paths
|
6
|
+
#
|
7
|
+
# @!visibility private
|
8
|
+
# @!macro internal_implementation_note
|
9
|
+
module AtomicDirectUpdate
|
10
|
+
def update
|
11
|
+
true until compare_and_set(old_value = get, new_value = yield(old_value))
|
12
|
+
new_value
|
13
|
+
end
|
14
|
+
|
15
|
+
def try_update
|
16
|
+
old_value = get
|
17
|
+
new_value = yield old_value
|
18
|
+
|
19
|
+
return unless compare_and_set old_value, new_value
|
20
|
+
|
21
|
+
new_value
|
22
|
+
end
|
23
|
+
|
24
|
+
def try_update!
|
25
|
+
old_value = get
|
26
|
+
new_value = yield old_value
|
27
|
+
unless compare_and_set(old_value, new_value)
|
28
|
+
if $VERBOSE
|
29
|
+
raise ConcurrentUpdateError, "Update failed"
|
30
|
+
else
|
31
|
+
raise ConcurrentUpdateError, "Update failed", ConcurrentUpdateError::CONC_UP_ERR_BACKTRACE
|
32
|
+
end
|
33
|
+
end
|
34
|
+
new_value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -1,8 +1,13 @@
|
|
1
|
+
require 'concurrent/atomic_reference/atomic_direct_update'
|
2
|
+
require 'concurrent/atomic_reference/numeric_cas_wrapper'
|
3
|
+
require 'concurrent/synchronization/safe_initialization'
|
4
|
+
|
1
5
|
module Concurrent
|
2
6
|
|
3
7
|
# @!visibility private
|
4
8
|
# @!macro internal_implementation_note
|
5
|
-
class MutexAtomicReference
|
9
|
+
class MutexAtomicReference
|
10
|
+
extend Concurrent::Synchronization::SafeInitialization
|
6
11
|
include AtomicDirectUpdate
|
7
12
|
include AtomicNumericCompareAndSetWrapper
|
8
13
|
alias_method :compare_and_swap, :compare_and_set
|
@@ -10,7 +15,8 @@ module Concurrent
|
|
10
15
|
# @!macro atomic_reference_method_initialize
|
11
16
|
def initialize(value = nil)
|
12
17
|
super()
|
13
|
-
|
18
|
+
@Lock = ::Mutex.new
|
19
|
+
@value = value
|
14
20
|
end
|
15
21
|
|
16
22
|
# @!macro atomic_reference_method_get
|
@@ -49,8 +55,13 @@ module Concurrent
|
|
49
55
|
|
50
56
|
protected
|
51
57
|
|
52
|
-
|
53
|
-
|
58
|
+
# @!visibility private
|
59
|
+
def synchronize
|
60
|
+
if @Lock.owned?
|
61
|
+
yield
|
62
|
+
else
|
63
|
+
@Lock.synchronize { yield }
|
64
|
+
end
|
54
65
|
end
|
55
66
|
end
|
56
67
|
end
|
@@ -9,8 +9,8 @@ module Concurrent
|
|
9
9
|
# @!visibility private
|
10
10
|
class MriMapBackend < NonConcurrentMapBackend
|
11
11
|
|
12
|
-
def initialize(options = nil)
|
13
|
-
super(options)
|
12
|
+
def initialize(options = nil, &default_proc)
|
13
|
+
super(options, &default_proc)
|
14
14
|
@write_lock = Mutex.new
|
15
15
|
end
|
16
16
|
|
@@ -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
@@ -12,8 +12,10 @@ module Concurrent
|
|
12
12
|
# directly without calling each other. This is important because of the
|
13
13
|
# SynchronizedMapBackend which uses a non-reentrant mutex for performance
|
14
14
|
# reasons.
|
15
|
-
def initialize(options = nil)
|
16
|
-
|
15
|
+
def initialize(options = nil, &default_proc)
|
16
|
+
validate_options_hash!(options) if options.kind_of?(::Hash)
|
17
|
+
set_backend(default_proc)
|
18
|
+
@default_proc = default_proc
|
17
19
|
end
|
18
20
|
|
19
21
|
def [](key)
|
@@ -55,7 +57,7 @@ module Concurrent
|
|
55
57
|
end
|
56
58
|
|
57
59
|
def compute(key)
|
58
|
-
store_computed_value(key, yield(
|
60
|
+
store_computed_value(key, yield(get_or_default(key, nil)))
|
59
61
|
end
|
60
62
|
|
61
63
|
def merge_pair(key, value)
|
@@ -67,7 +69,7 @@ module Concurrent
|
|
67
69
|
end
|
68
70
|
|
69
71
|
def get_and_set(key, value)
|
70
|
-
stored_value =
|
72
|
+
stored_value = get_or_default(key, nil)
|
71
73
|
@backend[key] = value
|
72
74
|
stored_value
|
73
75
|
end
|
@@ -109,13 +111,19 @@ module Concurrent
|
|
109
111
|
@backend.fetch(key, default_value)
|
110
112
|
end
|
111
113
|
|
112
|
-
alias_method :_get, :[]
|
113
|
-
alias_method :_set, :[]=
|
114
|
-
private :_get, :_set
|
115
114
|
private
|
115
|
+
|
116
|
+
def set_backend(default_proc)
|
117
|
+
if default_proc
|
118
|
+
@backend = ::Hash.new { |_h, key| default_proc.call(self, key) }
|
119
|
+
else
|
120
|
+
@backend = {}
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
116
124
|
def initialize_copy(other)
|
117
125
|
super
|
118
|
-
@
|
126
|
+
set_backend(@default_proc)
|
119
127
|
self
|
120
128
|
end
|
121
129
|
|
@@ -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
|
data/lib/{concurrent → concurrent-ruby/concurrent}/collection/ruby_non_concurrent_priority_queue.rb
RENAMED
@@ -30,7 +30,7 @@ module Concurrent
|
|
30
30
|
if @queue[k] == item
|
31
31
|
swap(k, @length)
|
32
32
|
@length -= 1
|
33
|
-
sink(k)
|
33
|
+
sink(k) || swim(k)
|
34
34
|
@queue.pop
|
35
35
|
else
|
36
36
|
k += 1
|
@@ -126,12 +126,17 @@ module Concurrent
|
|
126
126
|
#
|
127
127
|
# @!visibility private
|
128
128
|
def sink(k)
|
129
|
+
success = false
|
130
|
+
|
129
131
|
while (j = (2 * k)) <= @length do
|
130
132
|
j += 1 if j < @length && ! ordered?(j, j+1)
|
131
133
|
break if ordered?(k, j)
|
132
134
|
swap(k, j)
|
135
|
+
success = true
|
133
136
|
k = j
|
134
137
|
end
|
138
|
+
|
139
|
+
success
|
135
140
|
end
|
136
141
|
|
137
142
|
# Percolate up to maintain heap invariant.
|
@@ -140,10 +145,15 @@ module Concurrent
|
|
140
145
|
#
|
141
146
|
# @!visibility private
|
142
147
|
def swim(k)
|
148
|
+
success = false
|
149
|
+
|
143
150
|
while k > 1 && ! ordered?(k/2, k) do
|
144
151
|
swap(k, k/2)
|
145
152
|
k = k/2
|
153
|
+
success = true
|
146
154
|
end
|
155
|
+
|
156
|
+
success
|
147
157
|
end
|
148
158
|
end
|
149
159
|
end
|
@@ -37,8 +37,8 @@ module Concurrent
|
|
37
37
|
# returning data to the caller (dereferencing).
|
38
38
|
#
|
39
39
|
# @note Most classes that include this module will call `#set_deref_options`
|
40
|
-
#
|
41
|
-
#
|
40
|
+
# from within the constructor, thus allowing these options to be set at
|
41
|
+
# object creation.
|
42
42
|
#
|
43
43
|
# @param [Hash] opts the options defining dereference behavior.
|
44
44
|
# @option opts [String] :dup_on_deref (false) call `#dup` before returning the data
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'concurrent/atomic/atomic_reference'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
module Concern
|
6
|
+
|
7
|
+
# Include where logging is needed
|
8
|
+
#
|
9
|
+
# @!visibility private
|
10
|
+
module Logging
|
11
|
+
include Logger::Severity
|
12
|
+
|
13
|
+
# Logs through {Concurrent.global_logger}, it can be overridden by setting @logger
|
14
|
+
# @param [Integer] level one of Logger::Severity constants
|
15
|
+
# @param [String] progname e.g. a path of an Actor
|
16
|
+
# @param [String, nil] message when nil block is used to generate the message
|
17
|
+
# @yieldreturn [String] a message
|
18
|
+
def log(level, progname, message = nil, &block)
|
19
|
+
logger = if defined?(@logger) && @logger
|
20
|
+
@logger
|
21
|
+
else
|
22
|
+
Concurrent.global_logger
|
23
|
+
end
|
24
|
+
logger.call level, progname, message, &block
|
25
|
+
rescue => error
|
26
|
+
$stderr.puts "`Concurrent.configuration.logger` failed to log #{[level, progname, message, block]}\n" +
|
27
|
+
"#{error.message} (#{error.class})\n#{error.backtrace.join "\n"}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module Concurrent
|
34
|
+
extend Concern::Logging
|
35
|
+
|
36
|
+
# @return [Logger] Logger with provided level and output.
|
37
|
+
def self.create_simple_logger(level = Logger::FATAL, output = $stderr)
|
38
|
+
# TODO (pitr-ch 24-Dec-2016): figure out why it had to be replaced, stdlogger was deadlocking
|
39
|
+
lambda do |severity, progname, message = nil, &block|
|
40
|
+
return false if severity < level
|
41
|
+
|
42
|
+
message = block ? block.call : message
|
43
|
+
formatted_message = case message
|
44
|
+
when String
|
45
|
+
message
|
46
|
+
when Exception
|
47
|
+
format "%s (%s)\n%s",
|
48
|
+
message.message, message.class, (message.backtrace || []).join("\n")
|
49
|
+
else
|
50
|
+
message.inspect
|
51
|
+
end
|
52
|
+
|
53
|
+
output.print format "[%s] %5s -- %s: %s\n",
|
54
|
+
Time.now.strftime('%Y-%m-%d %H:%M:%S.%L'),
|
55
|
+
Logger::SEV_LABEL[severity],
|
56
|
+
progname,
|
57
|
+
formatted_message
|
58
|
+
true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Use logger created by #create_simple_logger to log concurrent-ruby messages.
|
63
|
+
def self.use_simple_logger(level = Logger::FATAL, output = $stderr)
|
64
|
+
Concurrent.global_logger = create_simple_logger level, output
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return [Logger] Logger with provided level and output.
|
68
|
+
# @deprecated
|
69
|
+
def self.create_stdlib_logger(level = Logger::FATAL, output = $stderr)
|
70
|
+
logger = Logger.new(output)
|
71
|
+
logger.level = level
|
72
|
+
logger.formatter = lambda do |severity, datetime, progname, msg|
|
73
|
+
formatted_message = case msg
|
74
|
+
when String
|
75
|
+
msg
|
76
|
+
when Exception
|
77
|
+
format "%s (%s)\n%s",
|
78
|
+
msg.message, msg.class, (msg.backtrace || []).join("\n")
|
79
|
+
else
|
80
|
+
msg.inspect
|
81
|
+
end
|
82
|
+
format "[%s] %5s -- %s: %s\n",
|
83
|
+
datetime.strftime('%Y-%m-%d %H:%M:%S.%L'),
|
84
|
+
severity,
|
85
|
+
progname,
|
86
|
+
formatted_message
|
87
|
+
end
|
88
|
+
|
89
|
+
lambda do |loglevel, progname, message = nil, &block|
|
90
|
+
logger.add loglevel, message, progname, &block
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Use logger created by #create_stdlib_logger to log concurrent-ruby messages.
|
95
|
+
# @deprecated
|
96
|
+
def self.use_stdlib_logger(level = Logger::FATAL, output = $stderr)
|
97
|
+
Concurrent.global_logger = create_stdlib_logger level, output
|
98
|
+
end
|
99
|
+
|
100
|
+
# TODO (pitr-ch 27-Dec-2016): remove deadlocking stdlib_logger methods
|
101
|
+
|
102
|
+
# Suppresses all output when used for logging.
|
103
|
+
NULL_LOGGER = lambda { |level, progname, message = nil, &block| }
|
104
|
+
|
105
|
+
# @!visibility private
|
106
|
+
GLOBAL_LOGGER = AtomicReference.new(create_simple_logger(Logger::WARN))
|
107
|
+
private_constant :GLOBAL_LOGGER
|
108
|
+
|
109
|
+
def self.global_logger
|
110
|
+
GLOBAL_LOGGER.value
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.global_logger=(value)
|
114
|
+
GLOBAL_LOGGER.value = value
|
115
|
+
end
|
116
|
+
end
|
Binary file
|