concurrent-ruby 1.0.5 → 1.1.10

Sign up to get free protection for your applications and to get access to all the features.
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