concurrent-ruby 1.0.5 → 1.1.10
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 +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
@@ -0,0 +1,346 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'concurrent/constants'
|
3
|
+
require 'concurrent/synchronization'
|
4
|
+
require 'concurrent/utility/engine'
|
5
|
+
|
6
|
+
module Concurrent
|
7
|
+
# @!visibility private
|
8
|
+
module Collection
|
9
|
+
|
10
|
+
# @!visibility private
|
11
|
+
MapImplementation = case
|
12
|
+
when Concurrent.on_jruby?
|
13
|
+
# noinspection RubyResolve
|
14
|
+
JRubyMapBackend
|
15
|
+
when Concurrent.on_cruby?
|
16
|
+
require 'concurrent/collection/map/mri_map_backend'
|
17
|
+
MriMapBackend
|
18
|
+
when Concurrent.on_truffleruby? && defined?(::TruffleRuby::ConcurrentMap)
|
19
|
+
require 'concurrent/collection/map/truffleruby_map_backend'
|
20
|
+
TruffleRubyMapBackend
|
21
|
+
when Concurrent.on_truffleruby? || Concurrent.on_rbx?
|
22
|
+
require 'concurrent/collection/map/atomic_reference_map_backend'
|
23
|
+
AtomicReferenceMapBackend
|
24
|
+
else
|
25
|
+
warn 'Concurrent::Map: unsupported Ruby engine, using a fully synchronized Concurrent::Map implementation'
|
26
|
+
require 'concurrent/collection/map/synchronized_map_backend'
|
27
|
+
SynchronizedMapBackend
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# `Concurrent::Map` is a hash-like object and should have much better performance
|
32
|
+
# characteristics, especially under high concurrency, than `Concurrent::Hash`.
|
33
|
+
# However, `Concurrent::Map `is not strictly semantically equivalent to a ruby `Hash`
|
34
|
+
# -- for instance, it does not necessarily retain ordering by insertion time as `Hash`
|
35
|
+
# does. For most uses it should do fine though, and we recommend you consider
|
36
|
+
# `Concurrent::Map` instead of `Concurrent::Hash` for your concurrency-safe hash needs.
|
37
|
+
class Map < Collection::MapImplementation
|
38
|
+
|
39
|
+
# @!macro map.atomic_method
|
40
|
+
# This method is atomic.
|
41
|
+
|
42
|
+
# @!macro map.atomic_method_with_block
|
43
|
+
# This method is atomic.
|
44
|
+
# @note Atomic methods taking a block do not allow the `self` instance
|
45
|
+
# to be used within the block. Doing so will cause a deadlock.
|
46
|
+
|
47
|
+
# @!method compute_if_absent(key)
|
48
|
+
# Compute and store new value for key if the key is absent.
|
49
|
+
# @param [Object] key
|
50
|
+
# @yield new value
|
51
|
+
# @yieldreturn [Object] new value
|
52
|
+
# @return [Object] new value or current value
|
53
|
+
# @!macro map.atomic_method_with_block
|
54
|
+
|
55
|
+
# @!method compute_if_present(key)
|
56
|
+
# Compute and store new value for key if the key is present.
|
57
|
+
# @param [Object] key
|
58
|
+
# @yield new value
|
59
|
+
# @yieldparam old_value [Object]
|
60
|
+
# @yieldreturn [Object, nil] new value, when nil the key is removed
|
61
|
+
# @return [Object, nil] new value or nil
|
62
|
+
# @!macro map.atomic_method_with_block
|
63
|
+
|
64
|
+
# @!method compute(key)
|
65
|
+
# Compute and store new value for key.
|
66
|
+
# @param [Object] key
|
67
|
+
# @yield compute new value from old one
|
68
|
+
# @yieldparam old_value [Object, nil] old_value, or nil when key is absent
|
69
|
+
# @yieldreturn [Object, nil] new value, when nil the key is removed
|
70
|
+
# @return [Object, nil] new value or nil
|
71
|
+
# @!macro map.atomic_method_with_block
|
72
|
+
|
73
|
+
# @!method merge_pair(key, value)
|
74
|
+
# If the key is absent, the value is stored, otherwise new value is
|
75
|
+
# computed with a block.
|
76
|
+
# @param [Object] key
|
77
|
+
# @param [Object] value
|
78
|
+
# @yield compute new value from old one
|
79
|
+
# @yieldparam old_value [Object] old value
|
80
|
+
# @yieldreturn [Object, nil] new value, when nil the key is removed
|
81
|
+
# @return [Object, nil] new value or nil
|
82
|
+
# @!macro map.atomic_method_with_block
|
83
|
+
|
84
|
+
# @!method replace_pair(key, old_value, new_value)
|
85
|
+
# Replaces old_value with new_value if key exists and current value
|
86
|
+
# matches old_value
|
87
|
+
# @param [Object] key
|
88
|
+
# @param [Object] old_value
|
89
|
+
# @param [Object] new_value
|
90
|
+
# @return [true, false] true if replaced
|
91
|
+
# @!macro map.atomic_method
|
92
|
+
|
93
|
+
# @!method replace_if_exists(key, new_value)
|
94
|
+
# Replaces current value with new_value if key exists
|
95
|
+
# @param [Object] key
|
96
|
+
# @param [Object] new_value
|
97
|
+
# @return [Object, nil] old value or nil
|
98
|
+
# @!macro map.atomic_method
|
99
|
+
|
100
|
+
# @!method get_and_set(key, value)
|
101
|
+
# Get the current value under key and set new value.
|
102
|
+
# @param [Object] key
|
103
|
+
# @param [Object] value
|
104
|
+
# @return [Object, nil] old value or nil when the key was absent
|
105
|
+
# @!macro map.atomic_method
|
106
|
+
|
107
|
+
# @!method delete(key)
|
108
|
+
# Delete key and its value.
|
109
|
+
# @param [Object] key
|
110
|
+
# @return [Object, nil] old value or nil when the key was absent
|
111
|
+
# @!macro map.atomic_method
|
112
|
+
|
113
|
+
# @!method delete_pair(key, value)
|
114
|
+
# Delete pair and its value if current value equals the provided value.
|
115
|
+
# @param [Object] key
|
116
|
+
# @param [Object] value
|
117
|
+
# @return [true, false] true if deleted
|
118
|
+
# @!macro map.atomic_method
|
119
|
+
|
120
|
+
#
|
121
|
+
def initialize(options = nil, &block)
|
122
|
+
if options.kind_of?(::Hash)
|
123
|
+
validate_options_hash!(options)
|
124
|
+
else
|
125
|
+
options = nil
|
126
|
+
end
|
127
|
+
|
128
|
+
super(options)
|
129
|
+
@default_proc = block
|
130
|
+
end
|
131
|
+
|
132
|
+
# Get a value with key
|
133
|
+
# @param [Object] key
|
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
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Set a value with key
|
150
|
+
# @param [Object] key
|
151
|
+
# @param [Object] value
|
152
|
+
# @return [Object] the new value
|
153
|
+
def []=(key, value)
|
154
|
+
super
|
155
|
+
end
|
156
|
+
|
157
|
+
alias_method :get, :[]
|
158
|
+
alias_method :put, :[]=
|
159
|
+
|
160
|
+
# Get a value with key, or default_value when key is absent,
|
161
|
+
# or fail when no default_value is given.
|
162
|
+
# @param [Object] key
|
163
|
+
# @param [Object] default_value
|
164
|
+
# @yield default value for a key
|
165
|
+
# @yieldparam key [Object]
|
166
|
+
# @yieldreturn [Object] default value
|
167
|
+
# @return [Object] the value or default value
|
168
|
+
# @raise [KeyError] when key is missing and no default_value is provided
|
169
|
+
# @!macro map_method_not_atomic
|
170
|
+
# @note The "fetch-then-act" methods of `Map` are not atomic. `Map` is intended
|
171
|
+
# to be use as a concurrency primitive with strong happens-before
|
172
|
+
# guarantees. It is not intended to be used as a high-level abstraction
|
173
|
+
# supporting complex operations. All read and write operations are
|
174
|
+
# thread safe, but no guarantees are made regarding race conditions
|
175
|
+
# between the fetch operation and yielding to the block. Additionally,
|
176
|
+
# this method does not support recursion. This is due to internal
|
177
|
+
# constraints that are very unlikely to change in the near future.
|
178
|
+
def fetch(key, default_value = NULL)
|
179
|
+
if NULL != (value = get_or_default(key, NULL))
|
180
|
+
value
|
181
|
+
elsif block_given?
|
182
|
+
yield key
|
183
|
+
elsif NULL != default_value
|
184
|
+
default_value
|
185
|
+
else
|
186
|
+
raise_fetch_no_key
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Fetch value with key, or store default value when key is absent,
|
191
|
+
# or fail when no default_value is given. This is a two step operation,
|
192
|
+
# therefore not atomic. The store can overwrite other concurrently
|
193
|
+
# stored value.
|
194
|
+
# @param [Object] key
|
195
|
+
# @param [Object] default_value
|
196
|
+
# @yield default value for a key
|
197
|
+
# @yieldparam key [Object]
|
198
|
+
# @yieldreturn [Object] default value
|
199
|
+
# @return [Object] the value or default value
|
200
|
+
# @!macro map.atomic_method_with_block
|
201
|
+
def fetch_or_store(key, default_value = NULL)
|
202
|
+
fetch(key) do
|
203
|
+
put(key, block_given? ? yield(key) : (NULL == default_value ? raise_fetch_no_key : default_value))
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Insert value into map with key if key is absent in one atomic step.
|
208
|
+
# @param [Object] key
|
209
|
+
# @param [Object] value
|
210
|
+
# @return [Object, nil] the previous value when key was present or nil when there was no key
|
211
|
+
def put_if_absent(key, value)
|
212
|
+
computed = false
|
213
|
+
result = compute_if_absent(key) do
|
214
|
+
computed = true
|
215
|
+
value
|
216
|
+
end
|
217
|
+
computed ? nil : result
|
218
|
+
end unless method_defined?(:put_if_absent)
|
219
|
+
|
220
|
+
# Is the value stored in the map. Iterates over all values.
|
221
|
+
# @param [Object] value
|
222
|
+
# @return [true, false]
|
223
|
+
def value?(value)
|
224
|
+
each_value do |v|
|
225
|
+
return true if value.equal?(v)
|
226
|
+
end
|
227
|
+
false
|
228
|
+
end
|
229
|
+
|
230
|
+
# All keys
|
231
|
+
# @return [::Array<Object>] keys
|
232
|
+
def keys
|
233
|
+
arr = []
|
234
|
+
each_pair { |k, v| arr << k }
|
235
|
+
arr
|
236
|
+
end unless method_defined?(:keys)
|
237
|
+
|
238
|
+
# All values
|
239
|
+
# @return [::Array<Object>] values
|
240
|
+
def values
|
241
|
+
arr = []
|
242
|
+
each_pair { |k, v| arr << v }
|
243
|
+
arr
|
244
|
+
end unless method_defined?(:values)
|
245
|
+
|
246
|
+
# Iterates over each key.
|
247
|
+
# @yield for each key in the map
|
248
|
+
# @yieldparam key [Object]
|
249
|
+
# @return [self]
|
250
|
+
# @!macro map.atomic_method_with_block
|
251
|
+
def each_key
|
252
|
+
each_pair { |k, v| yield k }
|
253
|
+
end unless method_defined?(:each_key)
|
254
|
+
|
255
|
+
# Iterates over each value.
|
256
|
+
# @yield for each value in the map
|
257
|
+
# @yieldparam value [Object]
|
258
|
+
# @return [self]
|
259
|
+
# @!macro map.atomic_method_with_block
|
260
|
+
def each_value
|
261
|
+
each_pair { |k, v| yield v }
|
262
|
+
end unless method_defined?(:each_value)
|
263
|
+
|
264
|
+
# Iterates over each key value pair.
|
265
|
+
# @yield for each key value pair in the map
|
266
|
+
# @yieldparam key [Object]
|
267
|
+
# @yieldparam value [Object]
|
268
|
+
# @return [self]
|
269
|
+
# @!macro map.atomic_method_with_block
|
270
|
+
def each_pair
|
271
|
+
return enum_for :each_pair unless block_given?
|
272
|
+
super
|
273
|
+
end
|
274
|
+
|
275
|
+
alias_method :each, :each_pair unless method_defined?(:each)
|
276
|
+
|
277
|
+
# Find key of a value.
|
278
|
+
# @param [Object] value
|
279
|
+
# @return [Object, nil] key or nil when not found
|
280
|
+
def key(value)
|
281
|
+
each_pair { |k, v| return k if v == value }
|
282
|
+
nil
|
283
|
+
end unless method_defined?(:key)
|
284
|
+
|
285
|
+
# Is map empty?
|
286
|
+
# @return [true, false]
|
287
|
+
def empty?
|
288
|
+
each_pair { |k, v| return false }
|
289
|
+
true
|
290
|
+
end unless method_defined?(:empty?)
|
291
|
+
|
292
|
+
# The size of map.
|
293
|
+
# @return [Integer] size
|
294
|
+
def size
|
295
|
+
count = 0
|
296
|
+
each_pair { |k, v| count += 1 }
|
297
|
+
count
|
298
|
+
end unless method_defined?(:size)
|
299
|
+
|
300
|
+
# @!visibility private
|
301
|
+
def marshal_dump
|
302
|
+
raise TypeError, "can't dump hash with default proc" if @default_proc
|
303
|
+
h = {}
|
304
|
+
each_pair { |k, v| h[k] = v }
|
305
|
+
h
|
306
|
+
end
|
307
|
+
|
308
|
+
# @!visibility private
|
309
|
+
def marshal_load(hash)
|
310
|
+
initialize
|
311
|
+
populate_from(hash)
|
312
|
+
end
|
313
|
+
|
314
|
+
undef :freeze
|
315
|
+
|
316
|
+
# @!visibility private
|
317
|
+
def inspect
|
318
|
+
format '%s entries=%d default_proc=%s>', to_s[0..-2], size.to_s, @default_proc.inspect
|
319
|
+
end
|
320
|
+
|
321
|
+
private
|
322
|
+
|
323
|
+
def raise_fetch_no_key
|
324
|
+
raise KeyError, 'key not found'
|
325
|
+
end
|
326
|
+
|
327
|
+
def initialize_copy(other)
|
328
|
+
super
|
329
|
+
populate_from(other)
|
330
|
+
end
|
331
|
+
|
332
|
+
def populate_from(hash)
|
333
|
+
hash.each_pair { |k, v| self[k] = v }
|
334
|
+
self
|
335
|
+
end
|
336
|
+
|
337
|
+
def validate_options_hash!(options)
|
338
|
+
if (initial_capacity = options[:initial_capacity]) && (!initial_capacity.kind_of?(Integer) || initial_capacity < 0)
|
339
|
+
raise ArgumentError, ":initial_capacity must be a positive Integer"
|
340
|
+
end
|
341
|
+
if (load_factor = options[:load_factor]) && (!load_factor.kind_of?(Numeric) || load_factor <= 0 || load_factor > 1)
|
342
|
+
raise ArgumentError, ":load_factor must be a number between 0 and 1"
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
@@ -108,7 +108,7 @@ module Concurrent
|
|
108
108
|
# Indicates that the given attribute has not been set.
|
109
109
|
# When `Just` the {#nothing} getter will return `NONE`.
|
110
110
|
# When `Nothing` the {#just} getter will return `NONE`.
|
111
|
-
NONE = Object.new.freeze
|
111
|
+
NONE = ::Object.new.freeze
|
112
112
|
|
113
113
|
# The value of a `Maybe` when `Just`. Will be `NONE` when `Nothing`.
|
114
114
|
attr_reader :just
|
@@ -6,11 +6,11 @@ module Concurrent
|
|
6
6
|
# An thread-safe variation of Ruby's standard `Struct`. Values can be set at
|
7
7
|
# construction or safely changed at any time during the object's lifecycle.
|
8
8
|
#
|
9
|
-
# @see http://ruby-doc.org/core
|
9
|
+
# @see http://ruby-doc.org/core/Struct.html Ruby standard library `Struct`
|
10
10
|
module MutableStruct
|
11
11
|
include Synchronization::AbstractStruct
|
12
12
|
|
13
|
-
# @!macro
|
13
|
+
# @!macro struct_new
|
14
14
|
#
|
15
15
|
# Factory for creating new struct classes.
|
16
16
|
#
|
@@ -40,9 +40,9 @@ module Concurrent
|
|
40
40
|
# struct. Unset parameters default to nil. Passing more parameters than number of attributes
|
41
41
|
# will raise an `ArgumentError`.
|
42
42
|
#
|
43
|
-
# @see http://ruby-doc.org/core
|
43
|
+
# @see http://ruby-doc.org/core/Struct.html#method-c-new Ruby standard library `Struct#new`
|
44
44
|
|
45
|
-
# @!macro
|
45
|
+
# @!macro struct_values
|
46
46
|
#
|
47
47
|
# Returns the values for this struct as an Array.
|
48
48
|
#
|
@@ -53,7 +53,7 @@ module Concurrent
|
|
53
53
|
end
|
54
54
|
alias_method :to_a, :values
|
55
55
|
|
56
|
-
# @!macro
|
56
|
+
# @!macro struct_values_at
|
57
57
|
#
|
58
58
|
# Returns the struct member values for each selector as an Array.
|
59
59
|
#
|
@@ -64,7 +64,7 @@ module Concurrent
|
|
64
64
|
synchronize { ns_values_at(indexes) }
|
65
65
|
end
|
66
66
|
|
67
|
-
# @!macro
|
67
|
+
# @!macro struct_inspect
|
68
68
|
#
|
69
69
|
# Describe the contents of this struct in a string.
|
70
70
|
#
|
@@ -74,7 +74,7 @@ module Concurrent
|
|
74
74
|
end
|
75
75
|
alias_method :to_s, :inspect
|
76
76
|
|
77
|
-
# @!macro
|
77
|
+
# @!macro struct_merge
|
78
78
|
#
|
79
79
|
# Returns a new struct containing the contents of `other` and the contents
|
80
80
|
# of `self`. If no block is specified, the value for entries with duplicate
|
@@ -95,7 +95,7 @@ module Concurrent
|
|
95
95
|
synchronize { ns_merge(other, &block) }
|
96
96
|
end
|
97
97
|
|
98
|
-
# @!macro
|
98
|
+
# @!macro struct_to_h
|
99
99
|
#
|
100
100
|
# Returns a hash containing the names and values for the struct’s members.
|
101
101
|
#
|
@@ -104,11 +104,11 @@ module Concurrent
|
|
104
104
|
synchronize { ns_to_h }
|
105
105
|
end
|
106
106
|
|
107
|
-
# @!macro
|
107
|
+
# @!macro struct_get
|
108
108
|
#
|
109
109
|
# Attribute Reference
|
110
110
|
#
|
111
|
-
# @param [Symbol, String, Integer] member the string or symbol name of the
|
111
|
+
# @param [Symbol, String, Integer] member the string or symbol name of the member
|
112
112
|
# for which to obtain the value or the member's index
|
113
113
|
#
|
114
114
|
# @return [Object] the value of the given struct member or the member at the given index.
|
@@ -119,7 +119,7 @@ module Concurrent
|
|
119
119
|
synchronize { ns_get(member) }
|
120
120
|
end
|
121
121
|
|
122
|
-
# @!macro
|
122
|
+
# @!macro struct_equality
|
123
123
|
#
|
124
124
|
# Equality
|
125
125
|
#
|
@@ -129,7 +129,7 @@ module Concurrent
|
|
129
129
|
synchronize { ns_equality(other) }
|
130
130
|
end
|
131
131
|
|
132
|
-
# @!macro
|
132
|
+
# @!macro struct_each
|
133
133
|
#
|
134
134
|
# Yields the value of each struct member in order. If no block is given
|
135
135
|
# an enumerator is returned.
|
@@ -141,7 +141,7 @@ module Concurrent
|
|
141
141
|
synchronize { ns_each(&block) }
|
142
142
|
end
|
143
143
|
|
144
|
-
# @!macro
|
144
|
+
# @!macro struct_each_pair
|
145
145
|
#
|
146
146
|
# Yields the name and value of each struct member in order. If no block is
|
147
147
|
# given an enumerator is returned.
|
@@ -154,7 +154,7 @@ module Concurrent
|
|
154
154
|
synchronize { ns_each_pair(&block) }
|
155
155
|
end
|
156
156
|
|
157
|
-
# @!macro
|
157
|
+
# @!macro struct_select
|
158
158
|
#
|
159
159
|
# Yields each member value from the struct to the block and returns an Array
|
160
160
|
# containing the member values from the struct for which the given block
|
@@ -169,13 +169,13 @@ module Concurrent
|
|
169
169
|
synchronize { ns_select(&block) }
|
170
170
|
end
|
171
171
|
|
172
|
-
# @!macro
|
172
|
+
# @!macro struct_set
|
173
173
|
#
|
174
174
|
# Attribute Assignment
|
175
175
|
#
|
176
176
|
# Sets the value of the given struct member or the member at the given index.
|
177
177
|
#
|
178
|
-
# @param [Symbol, String, Integer] member the string or symbol name of the
|
178
|
+
# @param [Symbol, String, Integer] member the string or symbol name of the member
|
179
179
|
# for which to obtain the value or the member's index
|
180
180
|
#
|
181
181
|
# @return [Object] the value of the given struct member or the member at the given index.
|
@@ -196,6 +196,16 @@ module Concurrent
|
|
196
196
|
raise NameError.new("no member '#{member}' in struct")
|
197
197
|
end
|
198
198
|
|
199
|
+
private
|
200
|
+
|
201
|
+
# @!visibility private
|
202
|
+
def initialize_copy(original)
|
203
|
+
synchronize do
|
204
|
+
super(original)
|
205
|
+
ns_initialize_copy
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
199
209
|
# @!macro struct_new
|
200
210
|
def self.new(*args, &block)
|
201
211
|
clazz_name = nil
|
@@ -212,6 +222,7 @@ module Concurrent
|
|
212
222
|
synchronize do
|
213
223
|
clazz = Synchronization::AbstractStruct.define_struct_class(MutableStruct, Synchronization::LockableObject, name, members, &block)
|
214
224
|
members.each_with_index do |member, index|
|
225
|
+
clazz.send :remove_method, member
|
215
226
|
clazz.send(:define_method, member) do
|
216
227
|
synchronize { @values[index] }
|
217
228
|
end
|
@@ -40,11 +40,11 @@ module Concurrent
|
|
40
40
|
safe_initialization!
|
41
41
|
|
42
42
|
# Unique value that represents that an `MVar` was empty
|
43
|
-
EMPTY = Object.new
|
43
|
+
EMPTY = ::Object.new
|
44
44
|
|
45
45
|
# Unique value that represents that an `MVar` timed out before it was able
|
46
46
|
# to produce a value.
|
47
|
-
TIMEOUT = Object.new
|
47
|
+
TIMEOUT = ::Object.new
|
48
48
|
|
49
49
|
# Create a new `MVar`, either empty or with an initial value.
|
50
50
|
#
|
@@ -161,15 +161,15 @@ module Concurrent
|
|
161
161
|
# receive the rejection `reason` as the rejection callable parameter:
|
162
162
|
#
|
163
163
|
# ```ruby
|
164
|
-
# p =
|
164
|
+
# p = Concurrent::Promise.execute { Thread.pass; raise StandardError }
|
165
165
|
#
|
166
|
-
# c1 = p.then(
|
167
|
-
# c2 = p.then(
|
166
|
+
# c1 = p.then(-> reason { 42 })
|
167
|
+
# c2 = p.then(-> reason { raise 'Boom!' })
|
168
168
|
#
|
169
|
-
#
|
170
|
-
#
|
171
|
-
#
|
172
|
-
# c2.
|
169
|
+
# c1.wait.state #=> :fulfilled
|
170
|
+
# c1.value #=> 45
|
171
|
+
# c2.wait.state #=> :rejected
|
172
|
+
# c2.reason #=> #<RuntimeError: Boom!>
|
173
173
|
# ```
|
174
174
|
#
|
175
175
|
# Once a promise is rejected it will continue to accept children that will
|
@@ -193,7 +193,7 @@ module Concurrent
|
|
193
193
|
#
|
194
194
|
# @!macro executor_and_deref_options
|
195
195
|
#
|
196
|
-
# @!macro
|
196
|
+
# @!macro promise_init_options
|
197
197
|
#
|
198
198
|
# @option opts [Promise] :parent the parent `Promise` when building a chain/tree
|
199
199
|
# @option opts [Proc] :on_fulfill fulfillment handler
|
@@ -250,6 +250,7 @@ module Concurrent
|
|
250
250
|
realize(@promise_body)
|
251
251
|
end
|
252
252
|
else
|
253
|
+
compare_and_set_state(:pending, :unscheduled)
|
253
254
|
@parent.execute
|
254
255
|
end
|
255
256
|
self
|
@@ -298,16 +299,28 @@ module Concurrent
|
|
298
299
|
|
299
300
|
# Chain a new promise off the current promise.
|
300
301
|
#
|
301
|
-
# @param [Proc] rescuer An optional rescue block to be executed if the
|
302
|
-
# promise is rejected.
|
303
|
-
#
|
304
|
-
# @param [ThreadPool] executor An optional thread pool executor to be used
|
305
|
-
# in the new Promise
|
306
|
-
#
|
307
|
-
# @yield The block operation to be performed asynchronously.
|
308
|
-
#
|
309
302
|
# @return [Promise] the new promise
|
310
|
-
|
303
|
+
# @yield The block operation to be performed asynchronously.
|
304
|
+
# @overload then(rescuer, executor, &block)
|
305
|
+
# @param [Proc] rescuer An optional rescue block to be executed if the
|
306
|
+
# promise is rejected.
|
307
|
+
# @param [ThreadPool] executor An optional thread pool executor to be used
|
308
|
+
# in the new Promise
|
309
|
+
# @overload then(rescuer, executor: executor, &block)
|
310
|
+
# @param [Proc] rescuer An optional rescue block to be executed if the
|
311
|
+
# promise is rejected.
|
312
|
+
# @param [ThreadPool] executor An optional thread pool executor to be used
|
313
|
+
# in the new Promise
|
314
|
+
def then(*args, &block)
|
315
|
+
if args.last.is_a?(::Hash)
|
316
|
+
executor = args.pop[:executor]
|
317
|
+
rescuer = args.first
|
318
|
+
else
|
319
|
+
rescuer, executor = args
|
320
|
+
end
|
321
|
+
|
322
|
+
executor ||= @executor
|
323
|
+
|
311
324
|
raise ArgumentError.new('rescuers and block are both missing') if rescuer.nil? && !block_given?
|
312
325
|
block = Proc.new { |result| result } unless block_given?
|
313
326
|
child = Promise.new(
|
@@ -383,11 +396,24 @@ module Concurrent
|
|
383
396
|
# Builds a promise that produces the result of promises in an Array
|
384
397
|
# and fails if any of them fails.
|
385
398
|
#
|
386
|
-
# @
|
399
|
+
# @overload zip(*promises)
|
400
|
+
# @param [Array<Promise>] promises
|
401
|
+
#
|
402
|
+
# @overload zip(*promises, opts)
|
403
|
+
# @param [Array<Promise>] promises
|
404
|
+
# @param [Hash] opts the configuration options
|
405
|
+
# @option opts [Executor] :executor (ImmediateExecutor.new) when set use the given `Executor` instance.
|
406
|
+
# @option opts [Boolean] :execute (true) execute promise before returning
|
387
407
|
#
|
388
408
|
# @return [Promise<Array>]
|
389
409
|
def self.zip(*promises)
|
390
|
-
|
410
|
+
opts = promises.last.is_a?(::Hash) ? promises.pop.dup : {}
|
411
|
+
opts[:executor] ||= ImmediateExecutor.new
|
412
|
+
zero = if !opts.key?(:execute) || opts.delete(:execute)
|
413
|
+
fulfill([], opts)
|
414
|
+
else
|
415
|
+
Promise.new(opts) { [] }
|
416
|
+
end
|
391
417
|
|
392
418
|
promises.reduce(zero) do |p1, p2|
|
393
419
|
p1.flat_map do |results|
|
@@ -401,7 +427,14 @@ module Concurrent
|
|
401
427
|
# Builds a promise that produces the result of self and others in an Array
|
402
428
|
# and fails if any of them fails.
|
403
429
|
#
|
404
|
-
# @
|
430
|
+
# @overload zip(*promises)
|
431
|
+
# @param [Array<Promise>] others
|
432
|
+
#
|
433
|
+
# @overload zip(*promises, opts)
|
434
|
+
# @param [Array<Promise>] others
|
435
|
+
# @param [Hash] opts the configuration options
|
436
|
+
# @option opts [Executor] :executor (ImmediateExecutor.new) when set use the given `Executor` instance.
|
437
|
+
# @option opts [Boolean] :execute (true) execute promise before returning
|
405
438
|
#
|
406
439
|
# @return [Promise<Array>]
|
407
440
|
def zip(*others)
|
@@ -414,7 +447,7 @@ module Concurrent
|
|
414
447
|
# fail. Upon execution will execute any of the aggregate promises that
|
415
448
|
# were not already executed.
|
416
449
|
#
|
417
|
-
# @!macro
|
450
|
+
# @!macro promise_self_aggregate
|
418
451
|
#
|
419
452
|
# The returned promise will not yet have been executed. Additional `#then`
|
420
453
|
# and `#rescue` handlers may still be provided. Once the returned promise
|