concurrent-ruby 1.1.10 → 1.3.3
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 +47 -1
- data/Gemfile +1 -2
- data/README.md +23 -20
- data/Rakefile +75 -65
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
- data/lib/concurrent-ruby/concurrent/agent.rb +2 -1
- data/lib/concurrent-ruby/concurrent/array.rb +3 -13
- data/lib/concurrent-ruby/concurrent/atom.rb +1 -1
- 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 +81 -151
- 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 +28 -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 +2 -1
- data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +5 -3
- 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 +15 -4
- 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/concern/logging.rb +86 -2
- 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 +2 -2
- 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 +4 -0
- data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +6 -9
- data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +5 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +7 -0
- 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/timer_set.rb +6 -2
- 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 +1 -1
- data/lib/concurrent-ruby/concurrent/promise.rb +1 -1
- 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 +1 -1
- 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/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 +12 -44
- 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/data_structures.rb +1 -37
- data/lib/concurrent-ruby/concurrent/timer_task.rb +59 -9
- 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 +118 -58
- data/lib/concurrent-ruby/concurrent/version.rb +1 -1
- metadata +13 -17
- 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
@@ -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
|
|
@@ -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
|
@@ -39,6 +39,10 @@ module Concurrent
|
|
39
39
|
# The number of tasks that have been completed by the pool since construction.
|
40
40
|
# @return [Integer] The number of tasks that have been completed by the pool since construction.
|
41
41
|
|
42
|
+
# @!macro thread_pool_executor_method_active_count
|
43
|
+
# The number of threads that are actively executing tasks.
|
44
|
+
# @return [Integer] The number of threads that are actively executing tasks.
|
45
|
+
|
42
46
|
# @!macro thread_pool_executor_attr_reader_idletime
|
43
47
|
# The number of seconds that a thread may be idle before being reclaimed.
|
44
48
|
# @return [Integer] The number of seconds that a thread may be idle before being reclaimed.
|
@@ -1,7 +1,7 @@
|
|
1
|
-
|
1
|
+
require 'concurrent/utility/engine'
|
2
2
|
|
3
|
+
if Concurrent.on_jruby?
|
3
4
|
require 'concurrent/errors'
|
4
|
-
require 'concurrent/utility/engine'
|
5
5
|
require 'concurrent/executor/abstract_executor_service'
|
6
6
|
|
7
7
|
module Concurrent
|
@@ -57,15 +57,11 @@ if Concurrent.on_jruby?
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def ns_shuttingdown?
|
60
|
-
|
61
|
-
@executor.isTerminating
|
62
|
-
else
|
63
|
-
false
|
64
|
-
end
|
60
|
+
@executor.isShutdown && !@executor.isTerminated
|
65
61
|
end
|
66
62
|
|
67
63
|
def ns_shutdown?
|
68
|
-
@executor.
|
64
|
+
@executor.isTerminated
|
69
65
|
end
|
70
66
|
|
71
67
|
class Job
|
@@ -88,10 +84,11 @@ if Concurrent.on_jruby?
|
|
88
84
|
|
89
85
|
def initialize(daemonize = true)
|
90
86
|
@daemonize = daemonize
|
87
|
+
@java_thread_factory = java.util.concurrent.Executors.defaultThreadFactory
|
91
88
|
end
|
92
89
|
|
93
90
|
def newThread(runnable)
|
94
|
-
thread =
|
91
|
+
thread = @java_thread_factory.newThread(runnable)
|
95
92
|
thread.setDaemon(@daemonize)
|
96
93
|
return thread
|
97
94
|
end
|
@@ -73,6 +73,11 @@ if Concurrent.on_jruby?
|
|
73
73
|
@executor.getCompletedTaskCount
|
74
74
|
end
|
75
75
|
|
76
|
+
# @!macro thread_pool_executor_method_active_count
|
77
|
+
def active_count
|
78
|
+
@executor.getActiveCount
|
79
|
+
end
|
80
|
+
|
76
81
|
# @!macro thread_pool_executor_attr_reader_idletime
|
77
82
|
def idletime
|
78
83
|
@executor.getKeepAliveTime(java.util.concurrent.TimeUnit::SECONDS)
|
@@ -61,6 +61,13 @@ module Concurrent
|
|
61
61
|
synchronize { @completed_task_count }
|
62
62
|
end
|
63
63
|
|
64
|
+
# @!macro thread_pool_executor_method_active_count
|
65
|
+
def active_count
|
66
|
+
synchronize do
|
67
|
+
@pool.length - @ready.length
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
64
71
|
# @!macro executor_service_method_can_overflow_question
|
65
72
|
def can_overflow?
|
66
73
|
synchronize { ns_limited_queue? }
|
@@ -1,5 +1,8 @@
|
|
1
|
-
require 'concurrent/
|
1
|
+
require 'concurrent/atomic/atomic_boolean'
|
2
|
+
require 'concurrent/atomic/atomic_fixnum'
|
3
|
+
require 'concurrent/atomic/event'
|
2
4
|
require 'concurrent/executor/executor_service'
|
5
|
+
require 'concurrent/executor/ruby_executor_service'
|
3
6
|
|
4
7
|
module Concurrent
|
5
8
|
|
@@ -3,7 +3,7 @@ require 'concurrent/atomic/event'
|
|
3
3
|
require 'concurrent/collection/non_concurrent_priority_queue'
|
4
4
|
require 'concurrent/executor/executor_service'
|
5
5
|
require 'concurrent/executor/single_thread_executor'
|
6
|
-
|
6
|
+
require 'concurrent/errors'
|
7
7
|
require 'concurrent/options'
|
8
8
|
|
9
9
|
module Concurrent
|
@@ -162,7 +162,11 @@ module Concurrent
|
|
162
162
|
# queue now must have the same pop time, or a closer one, as
|
163
163
|
# when we peeked).
|
164
164
|
task = synchronize { @queue.pop }
|
165
|
-
|
165
|
+
begin
|
166
|
+
task.executor.post { task.process_task }
|
167
|
+
rescue RejectedExecutionError
|
168
|
+
# ignore and continue
|
169
|
+
end
|
166
170
|
else
|
167
171
|
@condition.wait([diff, 60].min)
|
168
172
|
end
|
@@ -15,9 +15,11 @@ module Concurrent
|
|
15
15
|
# @!macro internal_implementation_note
|
16
16
|
HashImplementation = case
|
17
17
|
when Concurrent.on_cruby?
|
18
|
-
# Hash is thread-safe
|
19
|
-
#
|
20
|
-
#
|
18
|
+
# Hash is not fully thread-safe on CRuby, see
|
19
|
+
# https://bugs.ruby-lang.org/issues/19237
|
20
|
+
# https://github.com/ruby/ruby/commit/ffd52412ab
|
21
|
+
# https://github.com/ruby-concurrency/concurrent-ruby/issues/929
|
22
|
+
# So we will need to add synchronization here (similar to Concurrent::Map).
|
21
23
|
::Hash
|
22
24
|
|
23
25
|
when Concurrent.on_jruby?
|
@@ -28,15 +30,6 @@ module Concurrent
|
|
28
30
|
end
|
29
31
|
JRubyHash
|
30
32
|
|
31
|
-
when Concurrent.on_rbx?
|
32
|
-
require 'monitor'
|
33
|
-
require 'concurrent/thread_safe/util/data_structures'
|
34
|
-
|
35
|
-
class RbxHash < ::Hash
|
36
|
-
end
|
37
|
-
ThreadSafe::Util.make_synchronized_on_rbx RbxHash
|
38
|
-
RbxHash
|
39
|
-
|
40
33
|
when Concurrent.on_truffleruby?
|
41
34
|
require 'concurrent/thread_safe/util/data_structures'
|
42
35
|
|
@@ -3,7 +3,8 @@ require 'concurrent/errors'
|
|
3
3
|
require 'concurrent/collection/copy_on_write_observer_set'
|
4
4
|
require 'concurrent/concern/obligation'
|
5
5
|
require 'concurrent/concern/observable'
|
6
|
-
require 'concurrent/
|
6
|
+
require 'concurrent/executor/safe_task_executor'
|
7
|
+
require 'concurrent/synchronization/lockable_object'
|
7
8
|
|
8
9
|
module Concurrent
|
9
10
|
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'thread'
|
2
2
|
require 'concurrent/constants'
|
3
|
-
require 'concurrent/synchronization'
|
4
3
|
require 'concurrent/utility/engine'
|
5
4
|
|
6
5
|
module Concurrent
|
@@ -10,17 +9,20 @@ module Concurrent
|
|
10
9
|
# @!visibility private
|
11
10
|
MapImplementation = case
|
12
11
|
when Concurrent.on_jruby?
|
12
|
+
require 'concurrent/utility/native_extension_loader'
|
13
13
|
# noinspection RubyResolve
|
14
14
|
JRubyMapBackend
|
15
15
|
when Concurrent.on_cruby?
|
16
16
|
require 'concurrent/collection/map/mri_map_backend'
|
17
17
|
MriMapBackend
|
18
|
-
when Concurrent.on_truffleruby?
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
when Concurrent.on_truffleruby?
|
19
|
+
if defined?(::TruffleRuby::ConcurrentMap)
|
20
|
+
require 'concurrent/collection/map/truffleruby_map_backend'
|
21
|
+
TruffleRubyMapBackend
|
22
|
+
else
|
23
|
+
require 'concurrent/collection/map/synchronized_map_backend'
|
24
|
+
SynchronizedMapBackend
|
25
|
+
end
|
24
26
|
else
|
25
27
|
warn 'Concurrent::Map: unsupported Ruby engine, using a fully synchronized Concurrent::Map implementation'
|
26
28
|
require 'concurrent/collection/map/synchronized_map_backend'
|
@@ -44,6 +46,12 @@ module Concurrent
|
|
44
46
|
# @note Atomic methods taking a block do not allow the `self` instance
|
45
47
|
# to be used within the block. Doing so will cause a deadlock.
|
46
48
|
|
49
|
+
# @!method []=(key, value)
|
50
|
+
# Set a value with key
|
51
|
+
# @param [Object] key
|
52
|
+
# @param [Object] value
|
53
|
+
# @return [Object] the new value
|
54
|
+
|
47
55
|
# @!method compute_if_absent(key)
|
48
56
|
# Compute and store new value for key if the key is absent.
|
49
57
|
# @param [Object] key
|
@@ -117,41 +125,38 @@ module Concurrent
|
|
117
125
|
# @return [true, false] true if deleted
|
118
126
|
# @!macro map.atomic_method
|
119
127
|
|
120
|
-
#
|
121
|
-
|
122
|
-
if options.kind_of?(::Hash)
|
123
|
-
validate_options_hash!(options)
|
124
|
-
else
|
125
|
-
options = nil
|
126
|
-
end
|
128
|
+
# NonConcurrentMapBackend handles default_proc natively
|
129
|
+
unless defined?(Collection::NonConcurrentMapBackend) and self < Collection::NonConcurrentMapBackend
|
127
130
|
|
128
|
-
|
129
|
-
@default_proc
|
130
|
-
|
131
|
+
# @param [Hash, nil] options options to set the :initial_capacity or :load_factor. Ignored on some Rubies.
|
132
|
+
# @param [Proc] default_proc Optional block to compute the default value if the key is not set, like `Hash#default_proc`
|
133
|
+
def initialize(options = nil, &default_proc)
|
134
|
+
if options.kind_of?(::Hash)
|
135
|
+
validate_options_hash!(options)
|
136
|
+
else
|
137
|
+
options = nil
|
138
|
+
end
|
131
139
|
|
132
|
-
|
133
|
-
|
134
|
-
# @return [Object] the value
|
135
|
-
def [](key)
|
136
|
-
if value = super # non-falsy value is an existing mapping, return it right away
|
137
|
-
value
|
138
|
-
# re-check is done with get_or_default(key, NULL) instead of a simple !key?(key) in order to avoid a race condition, whereby by the time the current thread gets to the key?(key) call
|
139
|
-
# a key => value mapping might have already been created by a different thread (key?(key) would then return true, this elsif branch wouldn't be taken and an incorrent +nil+ value
|
140
|
-
# would be returned)
|
141
|
-
# note: nil == value check is not technically necessary
|
142
|
-
elsif @default_proc && nil == value && NULL == (value = get_or_default(key, NULL))
|
143
|
-
@default_proc.call(self, key)
|
144
|
-
else
|
145
|
-
value
|
140
|
+
super(options)
|
141
|
+
@default_proc = default_proc
|
146
142
|
end
|
147
|
-
end
|
148
143
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
144
|
+
# Get a value with key
|
145
|
+
# @param [Object] key
|
146
|
+
# @return [Object] the value
|
147
|
+
def [](key)
|
148
|
+
if value = super # non-falsy value is an existing mapping, return it right away
|
149
|
+
value
|
150
|
+
# re-check is done with get_or_default(key, NULL) instead of a simple !key?(key) in order to avoid a race condition, whereby by the time the current thread gets to the key?(key) call
|
151
|
+
# a key => value mapping might have already been created by a different thread (key?(key) would then return true, this elsif branch wouldn't be taken and an incorrent +nil+ value
|
152
|
+
# would be returned)
|
153
|
+
# note: nil == value check is not technically necessary
|
154
|
+
elsif @default_proc && nil == value && NULL == (value = get_or_default(key, NULL))
|
155
|
+
@default_proc.call(self, key)
|
156
|
+
else
|
157
|
+
value
|
158
|
+
end
|
159
|
+
end
|
155
160
|
end
|
156
161
|
|
157
162
|
alias_method :get, :[]
|
@@ -197,7 +202,6 @@ module Concurrent
|
|
197
202
|
# @yieldparam key [Object]
|
198
203
|
# @yieldreturn [Object] default value
|
199
204
|
# @return [Object] the value or default value
|
200
|
-
# @!macro map.atomic_method_with_block
|
201
205
|
def fetch_or_store(key, default_value = NULL)
|
202
206
|
fetch(key) do
|
203
207
|
put(key, block_given? ? yield(key) : (NULL == default_value ? raise_fetch_no_key : default_value))
|