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.
Files changed (164) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +155 -0
  3. data/Gemfile +37 -0
  4. data/LICENSE.txt +18 -18
  5. data/README.md +260 -103
  6. data/Rakefile +329 -0
  7. data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
  9. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
  10. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
  11. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
  12. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +189 -0
  13. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +307 -0
  14. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
  15. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
  16. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
  17. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
  18. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
  19. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
  20. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
  21. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
  22. data/lib/{concurrent → concurrent-ruby/concurrent}/agent.rb +7 -7
  23. data/lib/concurrent-ruby/concurrent/array.rb +66 -0
  24. data/lib/{concurrent → concurrent-ruby/concurrent}/async.rb +28 -24
  25. data/lib/{concurrent → concurrent-ruby/concurrent}/atom.rb +10 -10
  26. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_boolean.rb +26 -22
  27. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_fixnum.rb +27 -23
  28. data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +164 -0
  29. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +205 -0
  30. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/count_down_latch.rb +7 -7
  31. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/cyclic_barrier.rb +1 -1
  32. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/event.rb +3 -3
  33. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/java_count_down_latch.rb +9 -6
  34. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_boolean.rb +2 -0
  35. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_count_down_latch.rb +1 -0
  36. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_semaphore.rb +18 -2
  37. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/read_write_lock.rb +2 -1
  38. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/reentrant_read_write_lock.rb +7 -7
  39. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/ruby_thread_local_var.rb +60 -40
  40. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/semaphore.rb +34 -13
  41. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/thread_local_var.rb +8 -8
  42. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/mutex_atomic.rb +3 -8
  43. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/numeric_cas_wrapper.rb +1 -1
  44. data/lib/concurrent-ruby/concurrent/atomics.rb +10 -0
  45. data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +158 -0
  46. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/atomic_reference_map_backend.rb +3 -3
  47. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/mri_map_backend.rb +1 -1
  48. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/non_concurrent_map_backend.rb +1 -2
  49. data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
  50. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/non_concurrent_priority_queue.rb +30 -30
  51. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/ruby_non_concurrent_priority_queue.rb +11 -1
  52. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/dereferenceable.rb +3 -3
  53. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/logging.rb +6 -1
  54. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/observable.rb +7 -7
  55. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  56. data/lib/{concurrent → concurrent-ruby/concurrent}/configuration.rb +15 -15
  57. data/lib/{concurrent → concurrent-ruby/concurrent}/constants.rb +1 -1
  58. data/lib/{concurrent → concurrent-ruby/concurrent}/dataflow.rb +2 -1
  59. data/lib/{concurrent → concurrent-ruby/concurrent}/delay.rb +9 -7
  60. data/lib/{concurrent → concurrent-ruby/concurrent}/exchanger.rb +21 -25
  61. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/abstract_executor_service.rb +35 -38
  62. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/cached_thread_pool.rb +5 -5
  63. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/executor_service.rb +17 -17
  64. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/fixed_thread_pool.rb +47 -33
  65. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_executor_service.rb +20 -17
  66. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_single_thread_executor.rb +4 -3
  67. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_thread_pool_executor.rb +29 -9
  68. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_executor_service.rb +10 -6
  69. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_single_thread_executor.rb +0 -1
  70. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_thread_pool_executor.rb +46 -42
  71. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/safe_task_executor.rb +5 -5
  72. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/simple_executor_service.rb +1 -1
  73. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/single_thread_executor.rb +3 -2
  74. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/thread_pool_executor.rb +7 -6
  75. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/timer_set.rb +14 -17
  76. data/lib/{concurrent → concurrent-ruby/concurrent}/future.rb +4 -1
  77. data/lib/concurrent-ruby/concurrent/hash.rb +59 -0
  78. data/lib/{concurrent → concurrent-ruby/concurrent}/immutable_struct.rb +9 -1
  79. data/lib/{concurrent → concurrent-ruby/concurrent}/ivar.rb +5 -6
  80. data/lib/concurrent-ruby/concurrent/map.rb +346 -0
  81. data/lib/{concurrent → concurrent-ruby/concurrent}/maybe.rb +1 -1
  82. data/lib/{concurrent → concurrent-ruby/concurrent}/mutable_struct.rb +27 -16
  83. data/lib/{concurrent → concurrent-ruby/concurrent}/mvar.rb +2 -2
  84. data/lib/{concurrent → concurrent-ruby/concurrent}/promise.rb +54 -21
  85. data/lib/concurrent-ruby/concurrent/promises.rb +2167 -0
  86. data/lib/concurrent-ruby/concurrent/re_include.rb +58 -0
  87. data/lib/{concurrent → concurrent-ruby/concurrent}/scheduled_task.rb +29 -16
  88. data/lib/concurrent-ruby/concurrent/set.rb +74 -0
  89. data/lib/{concurrent → concurrent-ruby/concurrent}/settable_struct.rb +12 -1
  90. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_lockable_object.rb +5 -5
  91. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_struct.rb +18 -4
  92. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/condition.rb +2 -0
  93. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_object.rb +1 -0
  94. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lock.rb +2 -0
  95. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lockable_object.rb +8 -10
  96. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/mri_object.rb +1 -0
  97. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +88 -0
  98. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/object.rb +53 -23
  99. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/rbx_lockable_object.rb +6 -0
  100. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/rbx_object.rb +1 -0
  101. data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +47 -0
  102. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/volatile.rb +11 -9
  103. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization.rb +4 -5
  104. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +88 -0
  105. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/striped64.rb +9 -4
  106. data/lib/{concurrent → concurrent-ruby/concurrent}/timer_task.rb +15 -35
  107. data/lib/{concurrent → concurrent-ruby/concurrent}/tuple.rb +1 -1
  108. data/lib/{concurrent → concurrent-ruby/concurrent}/tvar.rb +21 -58
  109. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/engine.rb +4 -4
  110. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +90 -0
  111. data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +79 -0
  112. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/processor_counter.rb +5 -35
  113. data/lib/concurrent-ruby/concurrent/version.rb +3 -0
  114. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -0
  115. data/lib/{concurrent.rb → concurrent-ruby/concurrent.rb} +24 -20
  116. metadata +149 -134
  117. data/lib/concurrent/array.rb +0 -39
  118. data/lib/concurrent/atomic/atomic_reference.rb +0 -51
  119. data/lib/concurrent/atomic_reference/concurrent_update_error.rb +0 -8
  120. data/lib/concurrent/atomic_reference/direct_update.rb +0 -81
  121. data/lib/concurrent/atomic_reference/jruby+truffle.rb +0 -2
  122. data/lib/concurrent/atomic_reference/jruby.rb +0 -16
  123. data/lib/concurrent/atomic_reference/rbx.rb +0 -22
  124. data/lib/concurrent/atomic_reference/ruby.rb +0 -32
  125. data/lib/concurrent/atomics.rb +0 -53
  126. data/lib/concurrent/edge.rb +0 -26
  127. data/lib/concurrent/hash.rb +0 -36
  128. data/lib/concurrent/lazy_register.rb +0 -81
  129. data/lib/concurrent/map.rb +0 -240
  130. data/lib/concurrent/synchronization/mri_lockable_object.rb +0 -71
  131. data/lib/concurrent/synchronization/truffle_lockable_object.rb +0 -9
  132. data/lib/concurrent/synchronization/truffle_object.rb +0 -31
  133. data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +0 -30
  134. data/lib/concurrent/utility/at_exit.rb +0 -97
  135. data/lib/concurrent/utility/monotonic_time.rb +0 -58
  136. data/lib/concurrent/utility/native_extension_loader.rb +0 -73
  137. data/lib/concurrent/version.rb +0 -4
  138. /data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/abstract_thread_local_var.rb +0 -0
  139. /data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/java_thread_local_var.rb +0 -0
  140. /data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_fixnum.rb +0 -0
  141. /data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_notify_observer_set.rb +0 -0
  142. /data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_write_observer_set.rb +0 -0
  143. /data/lib/{concurrent → concurrent-ruby/concurrent}/collection/java_non_concurrent_priority_queue.rb +0 -0
  144. /data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/synchronized_map_backend.rb +0 -0
  145. /data/lib/{concurrent → concurrent-ruby/concurrent}/concern/deprecation.rb +0 -0
  146. /data/lib/{concurrent → concurrent-ruby/concurrent}/concern/obligation.rb +0 -0
  147. /data/lib/{concurrent → concurrent-ruby/concurrent}/errors.rb +0 -0
  148. /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/immediate_executor.rb +0 -0
  149. /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/indirect_immediate_executor.rb +0 -0
  150. /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serial_executor_service.rb +0 -0
  151. /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution.rb +0 -0
  152. /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution_delegator.rb +0 -0
  153. /data/lib/{concurrent → concurrent-ruby/concurrent}/executors.rb +0 -0
  154. /data/lib/{concurrent → concurrent-ruby/concurrent}/options.rb +0 -0
  155. /data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_object.rb +0 -0
  156. /data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_lockable_object.rb +0 -0
  157. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/synchronized_delegator.rb +0 -0
  158. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/adder.rb +0 -0
  159. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/cheap_lockable.rb +0 -0
  160. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/power_of_two_tuple.rb +0 -0
  161. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/volatile.rb +0 -0
  162. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/xor_shift_random.rb +0 -0
  163. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util.rb +0 -0
  164. /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-2.2.0/Struct.html Ruby standard library `Struct`
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 [new] struct_new
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-2.2.0/Struct.html#method-c-new Ruby standard library `Struct#new`
43
+ # @see http://ruby-doc.org/core/Struct.html#method-c-new Ruby standard library `Struct#new`
44
44
 
45
- # @!macro [attach] struct_values
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 [attach] struct_values_at
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 [attach] struct_inspect
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 [attach] struct_merge
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 [attach] struct_to_h
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 [attach] struct_get
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 memeber
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 [attach] struct_equality
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 [attach] struct_each
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 [attach] struct_each_pair
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 [attach] struct_select
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 [new] struct_set
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 memeber
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 = [ Concurrent::Promise.execute{ Thread.pass; raise StandardError } ]
164
+ # p = Concurrent::Promise.execute { Thread.pass; raise StandardError }
165
165
  #
166
- # c1 = p.then(Proc.new{ |reason| 42 })
167
- # c2 = p.then(Proc.new{ |reason| raise 'Boom!' })
166
+ # c1 = p.then(-> reason { 42 })
167
+ # c2 = p.then(-> reason { raise 'Boom!' })
168
168
  #
169
- # sleep(0.1)
170
- #
171
- # c1.state #=> :rejected
172
- # c2.state #=> :rejected
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 [attach] promise_init_options
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
- def then(rescuer = nil, executor = @executor, &block)
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
- # @param [Array<Promise>] promises
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
- zero = fulfill([], executor: ImmediateExecutor.new)
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
- # @param [Array<Promise>] others
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 [attach] promise_self_aggregate
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