concurrent-ruby 1.1.10 → 1.3.7
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 +76 -3
- data/Gemfile +6 -7
- data/README.md +28 -23
- data/Rakefile +90 -76
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +3 -38
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +1 -1
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +1 -1
- data/lib/concurrent-ruby/concurrent/agent.rb +4 -3
- data/lib/concurrent-ruby/concurrent/array.rb +3 -13
- data/lib/concurrent-ruby/concurrent/async.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atom.rb +2 -2
- data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +5 -4
- data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +5 -4
- data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +3 -0
- data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +90 -153
- data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/event.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb +109 -0
- data/lib/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 +29 -0
- data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +11 -5
- data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +11 -5
- data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +17 -3
- data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +13 -4
- data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +6 -9
- data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +96 -89
- data/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb +37 -0
- data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +16 -6
- data/lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb +9 -1
- data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +1 -1
- data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +1 -1
- data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +2 -0
- data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +2 -2
- data/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb +16 -8
- data/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb +23 -20
- data/lib/concurrent-ruby/concurrent/collection/ruby_timeout_queue.rb +55 -0
- data/lib/concurrent-ruby/concurrent/collection/timeout_queue.rb +18 -0
- data/lib/concurrent-ruby/concurrent/concern/logging.rb +95 -6
- data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent-ruby/concurrent/configuration.rb +4 -87
- data/lib/concurrent-ruby/concurrent/delay.rb +3 -3
- data/lib/concurrent-ruby/concurrent/errors.rb +5 -0
- data/lib/concurrent-ruby/concurrent/exchanger.rb +1 -0
- data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +1 -1
- data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +6 -4
- data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +7 -9
- data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +7 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb +2 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +61 -31
- data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +1 -1
- data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +1 -1
- data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +4 -1
- data/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb +1 -1
- data/lib/concurrent-ruby/concurrent/executor/timer_set.rb +10 -3
- data/lib/concurrent-ruby/concurrent/executors.rb +0 -1
- data/lib/concurrent-ruby/concurrent/hash.rb +5 -12
- data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/ivar.rb +2 -1
- data/lib/concurrent-ruby/concurrent/map.rb +43 -39
- data/lib/concurrent-ruby/concurrent/maybe.rb +1 -1
- data/lib/concurrent-ruby/concurrent/mutable_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/mvar.rb +5 -5
- data/lib/concurrent-ruby/concurrent/promise.rb +3 -3
- data/lib/concurrent-ruby/concurrent/promises.rb +40 -29
- data/lib/concurrent-ruby/concurrent/re_include.rb +2 -0
- data/lib/concurrent-ruby/concurrent/scheduled_task.rb +2 -2
- data/lib/concurrent-ruby/concurrent/set.rb +0 -10
- data/lib/concurrent-ruby/concurrent/settable_struct.rb +2 -2
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +5 -1
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +1 -3
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +2 -0
- data/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb +29 -0
- data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +3 -1
- data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +2 -0
- data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +5 -2
- data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +6 -5
- data/lib/concurrent-ruby/concurrent/synchronization/object.rb +13 -45
- data/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb +36 -0
- data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +77 -12
- data/lib/concurrent-ruby/concurrent/synchronization.rb +1 -18
- data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +36 -39
- data/lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb +1 -1
- data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +1 -37
- data/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb +1 -1
- data/lib/concurrent-ruby/concurrent/timer_task.rb +65 -10
- data/lib/concurrent-ruby/concurrent/tuple.rb +1 -5
- data/lib/concurrent-ruby/concurrent/tvar.rb +2 -1
- data/lib/concurrent-ruby/concurrent/utility/engine.rb +5 -16
- data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +3 -74
- data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +8 -10
- data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +1 -0
- data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +148 -58
- data/lib/concurrent-ruby/concurrent/version.rb +1 -1
- metadata +14 -19
- data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +0 -66
- data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +0 -37
- data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +0 -181
- data/lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb +0 -927
- data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +0 -45
- data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +0 -44
- data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +0 -71
- data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +0 -49
- data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +0 -47
- data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +0 -118
|
@@ -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,16 +1,21 @@
|
|
|
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
|
-
alias_method :compare_and_swap, :compare_and_set
|
|
9
13
|
|
|
10
14
|
# @!macro atomic_reference_method_initialize
|
|
11
15
|
def initialize(value = nil)
|
|
12
16
|
super()
|
|
13
|
-
|
|
17
|
+
@Lock = ::Mutex.new
|
|
18
|
+
@value = value
|
|
14
19
|
end
|
|
15
20
|
|
|
16
21
|
# @!macro atomic_reference_method_get
|
|
@@ -36,7 +41,7 @@ module Concurrent
|
|
|
36
41
|
alias_method :swap, :get_and_set
|
|
37
42
|
|
|
38
43
|
# @!macro atomic_reference_method_compare_and_set
|
|
39
|
-
def _compare_and_set(old_value, new_value)
|
|
44
|
+
private def _compare_and_set(old_value, new_value)
|
|
40
45
|
synchronize do
|
|
41
46
|
if @value.equal? old_value
|
|
42
47
|
@value = new_value
|
|
@@ -49,8 +54,13 @@ module Concurrent
|
|
|
49
54
|
|
|
50
55
|
protected
|
|
51
56
|
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
# @!visibility private
|
|
58
|
+
def synchronize
|
|
59
|
+
if @Lock.owned?
|
|
60
|
+
yield
|
|
61
|
+
else
|
|
62
|
+
@Lock.synchronize { yield }
|
|
63
|
+
end
|
|
54
64
|
end
|
|
55
65
|
end
|
|
56
66
|
end
|
|
@@ -9,12 +9,18 @@ module Concurrent
|
|
|
9
9
|
# @!macro atomic_reference_method_compare_and_set
|
|
10
10
|
def compare_and_set(old_value, new_value)
|
|
11
11
|
if old_value.kind_of? Numeric
|
|
12
|
+
# NaN is never == to itself; match it explicitly so #update can terminate.
|
|
13
|
+
expected_nan = old_value.respond_to?(:nan?) && old_value.nan?
|
|
12
14
|
while true
|
|
13
15
|
old = get
|
|
14
16
|
|
|
15
17
|
return false unless old.kind_of? Numeric
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
if expected_nan
|
|
20
|
+
return false unless old.respond_to?(:nan?) && old.nan?
|
|
21
|
+
else
|
|
22
|
+
return false unless old == old_value
|
|
23
|
+
end
|
|
18
24
|
|
|
19
25
|
result = _compare_and_set(old, new_value)
|
|
20
26
|
return result if result
|
|
@@ -24,5 +30,7 @@ module Concurrent
|
|
|
24
30
|
end
|
|
25
31
|
end
|
|
26
32
|
|
|
33
|
+
alias_method :compare_and_swap, :compare_and_set
|
|
34
|
+
|
|
27
35
|
end
|
|
28
36
|
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
|
|
|
@@ -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
|
|
|
@@ -8,74 +8,77 @@ module Concurrent
|
|
|
8
8
|
# @!visibility private
|
|
9
9
|
class SynchronizedMapBackend < NonConcurrentMapBackend
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
def initialize(*args, &block)
|
|
12
|
+
super
|
|
13
|
+
|
|
14
|
+
# WARNING: Mutex is a non-reentrant lock, so the synchronized methods are
|
|
15
|
+
# not allowed to call each other.
|
|
16
|
+
@mutex = Mutex.new
|
|
17
|
+
end
|
|
15
18
|
|
|
16
19
|
def [](key)
|
|
17
|
-
synchronize { super }
|
|
20
|
+
@mutex.synchronize { super }
|
|
18
21
|
end
|
|
19
22
|
|
|
20
23
|
def []=(key, value)
|
|
21
|
-
synchronize { super }
|
|
24
|
+
@mutex.synchronize { super }
|
|
22
25
|
end
|
|
23
26
|
|
|
24
27
|
def compute_if_absent(key)
|
|
25
|
-
synchronize { super }
|
|
28
|
+
@mutex.synchronize { super }
|
|
26
29
|
end
|
|
27
30
|
|
|
28
31
|
def compute_if_present(key)
|
|
29
|
-
synchronize { super }
|
|
32
|
+
@mutex.synchronize { super }
|
|
30
33
|
end
|
|
31
34
|
|
|
32
35
|
def compute(key)
|
|
33
|
-
synchronize { super }
|
|
36
|
+
@mutex.synchronize { super }
|
|
34
37
|
end
|
|
35
38
|
|
|
36
39
|
def merge_pair(key, value)
|
|
37
|
-
synchronize { super }
|
|
40
|
+
@mutex.synchronize { super }
|
|
38
41
|
end
|
|
39
42
|
|
|
40
43
|
def replace_pair(key, old_value, new_value)
|
|
41
|
-
synchronize { super }
|
|
44
|
+
@mutex.synchronize { super }
|
|
42
45
|
end
|
|
43
46
|
|
|
44
47
|
def replace_if_exists(key, new_value)
|
|
45
|
-
synchronize { super }
|
|
48
|
+
@mutex.synchronize { super }
|
|
46
49
|
end
|
|
47
50
|
|
|
48
51
|
def get_and_set(key, value)
|
|
49
|
-
synchronize { super }
|
|
52
|
+
@mutex.synchronize { super }
|
|
50
53
|
end
|
|
51
54
|
|
|
52
55
|
def key?(key)
|
|
53
|
-
synchronize { super }
|
|
56
|
+
@mutex.synchronize { super }
|
|
54
57
|
end
|
|
55
58
|
|
|
56
59
|
def delete(key)
|
|
57
|
-
synchronize { super }
|
|
60
|
+
@mutex.synchronize { super }
|
|
58
61
|
end
|
|
59
62
|
|
|
60
63
|
def delete_pair(key, value)
|
|
61
|
-
synchronize { super }
|
|
64
|
+
@mutex.synchronize { super }
|
|
62
65
|
end
|
|
63
66
|
|
|
64
67
|
def clear
|
|
65
|
-
synchronize { super }
|
|
68
|
+
@mutex.synchronize { super }
|
|
66
69
|
end
|
|
67
70
|
|
|
68
71
|
def size
|
|
69
|
-
synchronize { super }
|
|
72
|
+
@mutex.synchronize { super }
|
|
70
73
|
end
|
|
71
74
|
|
|
72
75
|
def get_or_default(key, default_value)
|
|
73
|
-
synchronize { super }
|
|
76
|
+
@mutex.synchronize { super }
|
|
74
77
|
end
|
|
75
78
|
|
|
76
79
|
private
|
|
77
80
|
def dupped_backend
|
|
78
|
-
synchronize { super }
|
|
81
|
+
@mutex.synchronize { super }
|
|
79
82
|
end
|
|
80
83
|
end
|
|
81
84
|
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module Concurrent
|
|
2
|
+
module Collection
|
|
3
|
+
# @!visibility private
|
|
4
|
+
# @!macro ruby_timeout_queue
|
|
5
|
+
class RubyTimeoutQueue < ::Queue
|
|
6
|
+
def initialize(*args)
|
|
7
|
+
if RUBY_VERSION >= '3.2'
|
|
8
|
+
raise "#{self.class.name} is not needed on Ruby 3.2 or later, use ::Queue instead"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
super(*args)
|
|
12
|
+
|
|
13
|
+
@mutex = Mutex.new
|
|
14
|
+
@cond_var = ConditionVariable.new
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def push(obj)
|
|
18
|
+
@mutex.synchronize do
|
|
19
|
+
super(obj)
|
|
20
|
+
@cond_var.signal
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
alias_method :enq, :push
|
|
24
|
+
alias_method :<<, :push
|
|
25
|
+
|
|
26
|
+
def pop(non_block = false, timeout: nil)
|
|
27
|
+
if non_block && timeout
|
|
28
|
+
raise ArgumentError, "can't set a timeout if non_block is enabled"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
if non_block
|
|
32
|
+
super(true)
|
|
33
|
+
elsif timeout
|
|
34
|
+
@mutex.synchronize do
|
|
35
|
+
deadline = Concurrent.monotonic_time + timeout
|
|
36
|
+
while (now = Concurrent.monotonic_time) < deadline && empty?
|
|
37
|
+
@cond_var.wait(@mutex, deadline - now)
|
|
38
|
+
end
|
|
39
|
+
begin
|
|
40
|
+
return super(true)
|
|
41
|
+
rescue ThreadError
|
|
42
|
+
# still empty
|
|
43
|
+
nil
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
else
|
|
47
|
+
super(false)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
alias_method :deq, :pop
|
|
51
|
+
alias_method :shift, :pop
|
|
52
|
+
end
|
|
53
|
+
private_constant :RubyTimeoutQueue
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Concurrent
|
|
2
|
+
module Collection
|
|
3
|
+
# @!visibility private
|
|
4
|
+
# @!macro internal_implementation_note
|
|
5
|
+
TimeoutQueueImplementation = if RUBY_VERSION >= '3.2'
|
|
6
|
+
::Queue
|
|
7
|
+
else
|
|
8
|
+
require 'concurrent/collection/ruby_timeout_queue'
|
|
9
|
+
RubyTimeoutQueue
|
|
10
|
+
end
|
|
11
|
+
private_constant :TimeoutQueueImplementation
|
|
12
|
+
|
|
13
|
+
# @!visibility private
|
|
14
|
+
# @!macro timeout_queue
|
|
15
|
+
class TimeoutQueue < TimeoutQueueImplementation
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require '
|
|
1
|
+
require 'concurrent/atomic/atomic_reference'
|
|
2
2
|
|
|
3
3
|
module Concurrent
|
|
4
4
|
module Concern
|
|
@@ -7,16 +7,16 @@ module Concurrent
|
|
|
7
7
|
#
|
|
8
8
|
# @!visibility private
|
|
9
9
|
module Logging
|
|
10
|
-
|
|
10
|
+
# The same as Logger::Severity but we copy it here to avoid a dependency on the logger gem just for these 7 constants
|
|
11
|
+
DEBUG, INFO, WARN, ERROR, FATAL, UNKNOWN = 0, 1, 2, 3, 4, 5
|
|
12
|
+
SEV_LABEL = %w[DEBUG INFO WARN ERROR FATAL ANY].freeze
|
|
11
13
|
|
|
12
14
|
# Logs through {Concurrent.global_logger}, it can be overridden by setting @logger
|
|
13
|
-
# @param [Integer] level one of
|
|
15
|
+
# @param [Integer] level one of Concurrent::Concern::Logging constants
|
|
14
16
|
# @param [String] progname e.g. a path of an Actor
|
|
15
17
|
# @param [String, nil] message when nil block is used to generate the message
|
|
16
18
|
# @yieldreturn [String] a message
|
|
17
19
|
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
20
|
logger = if defined?(@logger) && @logger
|
|
21
21
|
@logger
|
|
22
22
|
else
|
|
@@ -24,9 +24,98 @@ module Concurrent
|
|
|
24
24
|
end
|
|
25
25
|
logger.call level, progname, message, &block
|
|
26
26
|
rescue => error
|
|
27
|
-
$stderr.puts "`Concurrent.
|
|
27
|
+
$stderr.puts "`Concurrent.global_logger` failed to log #{[level, progname, message, block]}\n" +
|
|
28
28
|
"#{error.message} (#{error.class})\n#{error.backtrace.join "\n"}"
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
|
+
|
|
34
|
+
module Concurrent
|
|
35
|
+
extend Concern::Logging
|
|
36
|
+
|
|
37
|
+
# Create a simple logger with provided level and output.
|
|
38
|
+
def self.create_simple_logger(level = :FATAL, output = $stderr)
|
|
39
|
+
level = Concern::Logging.const_get(level) unless level.is_a?(Integer)
|
|
40
|
+
|
|
41
|
+
# TODO (pitr-ch 24-Dec-2016): figure out why it had to be replaced, stdlogger was deadlocking
|
|
42
|
+
lambda do |severity, progname, message = nil, &block|
|
|
43
|
+
return false if severity < level
|
|
44
|
+
|
|
45
|
+
message = block ? block.call : message
|
|
46
|
+
formatted_message = case message
|
|
47
|
+
when String
|
|
48
|
+
message
|
|
49
|
+
when Exception
|
|
50
|
+
format "%s (%s)\n%s",
|
|
51
|
+
message.message, message.class, (message.backtrace || []).join("\n")
|
|
52
|
+
else
|
|
53
|
+
message.inspect
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
output.print format "[%s] %5s -- %s: %s\n",
|
|
57
|
+
Time.now.strftime('%Y-%m-%d %H:%M:%S.%L'),
|
|
58
|
+
Concern::Logging::SEV_LABEL[severity],
|
|
59
|
+
progname,
|
|
60
|
+
formatted_message
|
|
61
|
+
true
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Use logger created by #create_simple_logger to log concurrent-ruby messages.
|
|
66
|
+
def self.use_simple_logger(level = :FATAL, output = $stderr)
|
|
67
|
+
Concurrent.global_logger = create_simple_logger level, output
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Create a stdlib logger with provided level and output.
|
|
71
|
+
# If you use this deprecated method you might need to add logger to your Gemfile to avoid warnings from Ruby 3.3.5+.
|
|
72
|
+
# @deprecated
|
|
73
|
+
def self.create_stdlib_logger(level = :FATAL, output = $stderr)
|
|
74
|
+
require 'logger'
|
|
75
|
+
logger = Logger.new(output)
|
|
76
|
+
logger.level = level
|
|
77
|
+
logger.formatter = lambda do |severity, datetime, progname, msg|
|
|
78
|
+
formatted_message = case msg
|
|
79
|
+
when String
|
|
80
|
+
msg
|
|
81
|
+
when Exception
|
|
82
|
+
format "%s (%s)\n%s",
|
|
83
|
+
msg.message, msg.class, (msg.backtrace || []).join("\n")
|
|
84
|
+
else
|
|
85
|
+
msg.inspect
|
|
86
|
+
end
|
|
87
|
+
format "[%s] %5s -- %s: %s\n",
|
|
88
|
+
datetime.strftime('%Y-%m-%d %H:%M:%S.%L'),
|
|
89
|
+
severity,
|
|
90
|
+
progname,
|
|
91
|
+
formatted_message
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
lambda do |loglevel, progname, message = nil, &block|
|
|
95
|
+
logger.add loglevel, message, progname, &block
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Use logger created by #create_stdlib_logger to log concurrent-ruby messages.
|
|
100
|
+
# @deprecated
|
|
101
|
+
def self.use_stdlib_logger(level = :FATAL, output = $stderr)
|
|
102
|
+
Concurrent.global_logger = create_stdlib_logger level, output
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# TODO (pitr-ch 27-Dec-2016): remove deadlocking stdlib_logger methods
|
|
106
|
+
|
|
107
|
+
# Suppresses all output when used for logging.
|
|
108
|
+
NULL_LOGGER = lambda { |level, progname, message = nil, &block| }
|
|
109
|
+
|
|
110
|
+
# @!visibility private
|
|
111
|
+
GLOBAL_LOGGER = AtomicReference.new(create_simple_logger(:WARN))
|
|
112
|
+
private_constant :GLOBAL_LOGGER
|
|
113
|
+
|
|
114
|
+
def self.global_logger
|
|
115
|
+
GLOBAL_LOGGER.value
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def self.global_logger=(value)
|
|
119
|
+
GLOBAL_LOGGER.value = value
|
|
120
|
+
end
|
|
121
|
+
end
|
|
Binary file
|
|
@@ -1,102 +1,19 @@
|
|
|
1
1
|
require 'thread'
|
|
2
2
|
require 'concurrent/delay'
|
|
3
3
|
require 'concurrent/errors'
|
|
4
|
-
require 'concurrent/atomic/atomic_reference'
|
|
5
|
-
require 'concurrent/concern/logging'
|
|
6
4
|
require 'concurrent/concern/deprecation'
|
|
7
5
|
require 'concurrent/executor/immediate_executor'
|
|
6
|
+
require 'concurrent/executor/fixed_thread_pool'
|
|
8
7
|
require 'concurrent/executor/cached_thread_pool'
|
|
9
8
|
require 'concurrent/utility/processor_counter'
|
|
10
9
|
|
|
11
10
|
module Concurrent
|
|
12
|
-
extend Concern::Logging
|
|
13
11
|
extend Concern::Deprecation
|
|
14
12
|
|
|
15
13
|
autoload :Options, 'concurrent/options'
|
|
16
14
|
autoload :TimerSet, 'concurrent/executor/timer_set'
|
|
17
15
|
autoload :ThreadPoolExecutor, 'concurrent/executor/thread_pool_executor'
|
|
18
16
|
|
|
19
|
-
# @return [Logger] Logger with provided level and output.
|
|
20
|
-
def self.create_simple_logger(level = Logger::FATAL, output = $stderr)
|
|
21
|
-
# TODO (pitr-ch 24-Dec-2016): figure out why it had to be replaced, stdlogger was deadlocking
|
|
22
|
-
lambda do |severity, progname, message = nil, &block|
|
|
23
|
-
return false if severity < level
|
|
24
|
-
|
|
25
|
-
message = block ? block.call : message
|
|
26
|
-
formatted_message = case message
|
|
27
|
-
when String
|
|
28
|
-
message
|
|
29
|
-
when Exception
|
|
30
|
-
format "%s (%s)\n%s",
|
|
31
|
-
message.message, message.class, (message.backtrace || []).join("\n")
|
|
32
|
-
else
|
|
33
|
-
message.inspect
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
output.print format "[%s] %5s -- %s: %s\n",
|
|
37
|
-
Time.now.strftime('%Y-%m-%d %H:%M:%S.%L'),
|
|
38
|
-
Logger::SEV_LABEL[severity],
|
|
39
|
-
progname,
|
|
40
|
-
formatted_message
|
|
41
|
-
true
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# Use logger created by #create_simple_logger to log concurrent-ruby messages.
|
|
46
|
-
def self.use_simple_logger(level = Logger::FATAL, output = $stderr)
|
|
47
|
-
Concurrent.global_logger = create_simple_logger level, output
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# @return [Logger] Logger with provided level and output.
|
|
51
|
-
# @deprecated
|
|
52
|
-
def self.create_stdlib_logger(level = Logger::FATAL, output = $stderr)
|
|
53
|
-
logger = Logger.new(output)
|
|
54
|
-
logger.level = level
|
|
55
|
-
logger.formatter = lambda do |severity, datetime, progname, msg|
|
|
56
|
-
formatted_message = case msg
|
|
57
|
-
when String
|
|
58
|
-
msg
|
|
59
|
-
when Exception
|
|
60
|
-
format "%s (%s)\n%s",
|
|
61
|
-
msg.message, msg.class, (msg.backtrace || []).join("\n")
|
|
62
|
-
else
|
|
63
|
-
msg.inspect
|
|
64
|
-
end
|
|
65
|
-
format "[%s] %5s -- %s: %s\n",
|
|
66
|
-
datetime.strftime('%Y-%m-%d %H:%M:%S.%L'),
|
|
67
|
-
severity,
|
|
68
|
-
progname,
|
|
69
|
-
formatted_message
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
lambda do |loglevel, progname, message = nil, &block|
|
|
73
|
-
logger.add loglevel, message, progname, &block
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# Use logger created by #create_stdlib_logger to log concurrent-ruby messages.
|
|
78
|
-
# @deprecated
|
|
79
|
-
def self.use_stdlib_logger(level = Logger::FATAL, output = $stderr)
|
|
80
|
-
Concurrent.global_logger = create_stdlib_logger level, output
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
# TODO (pitr-ch 27-Dec-2016): remove deadlocking stdlib_logger methods
|
|
84
|
-
|
|
85
|
-
# Suppresses all output when used for logging.
|
|
86
|
-
NULL_LOGGER = lambda { |level, progname, message = nil, &block| }
|
|
87
|
-
|
|
88
|
-
# @!visibility private
|
|
89
|
-
GLOBAL_LOGGER = AtomicReference.new(create_simple_logger(Logger::WARN))
|
|
90
|
-
private_constant :GLOBAL_LOGGER
|
|
91
|
-
|
|
92
|
-
def self.global_logger
|
|
93
|
-
GLOBAL_LOGGER.value
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def self.global_logger=(value)
|
|
97
|
-
GLOBAL_LOGGER.value = value
|
|
98
|
-
end
|
|
99
|
-
|
|
100
17
|
# @!visibility private
|
|
101
18
|
GLOBAL_FAST_EXECUTOR = Delay.new { Concurrent.new_fast_executor }
|
|
102
19
|
private_constant :GLOBAL_FAST_EXECUTOR
|
|
@@ -136,14 +53,14 @@ module Concurrent
|
|
|
136
53
|
#
|
|
137
54
|
# @return [ThreadPoolExecutor] the thread pool
|
|
138
55
|
def self.global_fast_executor
|
|
139
|
-
GLOBAL_FAST_EXECUTOR.value
|
|
56
|
+
GLOBAL_FAST_EXECUTOR.value!
|
|
140
57
|
end
|
|
141
58
|
|
|
142
59
|
# Global thread pool optimized for long, blocking (IO) *tasks*.
|
|
143
60
|
#
|
|
144
61
|
# @return [ThreadPoolExecutor] the thread pool
|
|
145
62
|
def self.global_io_executor
|
|
146
|
-
GLOBAL_IO_EXECUTOR.value
|
|
63
|
+
GLOBAL_IO_EXECUTOR.value!
|
|
147
64
|
end
|
|
148
65
|
|
|
149
66
|
def self.global_immediate_executor
|
|
@@ -154,7 +71,7 @@ module Concurrent
|
|
|
154
71
|
#
|
|
155
72
|
# @return [Concurrent::TimerSet] the thread pool
|
|
156
73
|
def self.global_timer_set
|
|
157
|
-
GLOBAL_TIMER_SET.value
|
|
74
|
+
GLOBAL_TIMER_SET.value!
|
|
158
75
|
end
|
|
159
76
|
|
|
160
77
|
# General access point to global executors.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require 'thread'
|
|
2
2
|
require 'concurrent/concern/obligation'
|
|
3
3
|
require 'concurrent/executor/immediate_executor'
|
|
4
|
-
require 'concurrent/synchronization'
|
|
4
|
+
require 'concurrent/synchronization/lockable_object'
|
|
5
5
|
|
|
6
6
|
module Concurrent
|
|
7
7
|
|
|
@@ -19,7 +19,7 @@ module Concurrent
|
|
|
19
19
|
#
|
|
20
20
|
# When a `Delay` is created its state is set to `pending`. The value and
|
|
21
21
|
# reason are both `nil`. The first time the `#value` method is called the
|
|
22
|
-
# enclosed
|
|
22
|
+
# enclosed operation will be run and the calling thread will block. Other
|
|
23
23
|
# threads attempting to call `#value` will block as well. Once the operation
|
|
24
24
|
# is complete the *value* will be set to the result of the operation or the
|
|
25
25
|
# *reason* will be set to the raised exception, as appropriate. All threads
|
|
@@ -67,7 +67,7 @@ module Concurrent
|
|
|
67
67
|
|
|
68
68
|
# Return the value this object represents after applying the options
|
|
69
69
|
# specified by the `#set_deref_options` method. If the delayed operation
|
|
70
|
-
# raised an exception this method will return nil. The
|
|
70
|
+
# raised an exception this method will return nil. The exception object
|
|
71
71
|
# can be accessed via the `#reason` method.
|
|
72
72
|
#
|
|
73
73
|
# @param [Numeric] timeout the maximum number of seconds to wait
|
|
@@ -66,4 +66,9 @@ module Concurrent
|
|
|
66
66
|
end
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
+
# @!macro internal_implementation_note
|
|
70
|
+
class ConcurrentUpdateError < ThreadError
|
|
71
|
+
# frozen pre-allocated backtrace to speed ConcurrentUpdateError
|
|
72
|
+
CONC_UP_ERR_BACKTRACE = ['backtrace elided; set verbose to enable'].freeze
|
|
73
|
+
end
|
|
69
74
|
end
|