concurrent-ruby 1.1.5

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 (143) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +478 -0
  3. data/Gemfile +41 -0
  4. data/LICENSE.md +23 -0
  5. data/README.md +381 -0
  6. data/Rakefile +327 -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 +159 -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-ruby.rb +1 -0
  23. data/lib/concurrent.rb +134 -0
  24. data/lib/concurrent/agent.rb +587 -0
  25. data/lib/concurrent/array.rb +66 -0
  26. data/lib/concurrent/async.rb +459 -0
  27. data/lib/concurrent/atom.rb +222 -0
  28. data/lib/concurrent/atomic/abstract_thread_local_var.rb +66 -0
  29. data/lib/concurrent/atomic/atomic_boolean.rb +126 -0
  30. data/lib/concurrent/atomic/atomic_fixnum.rb +143 -0
  31. data/lib/concurrent/atomic/atomic_markable_reference.rb +164 -0
  32. data/lib/concurrent/atomic/atomic_reference.rb +204 -0
  33. data/lib/concurrent/atomic/count_down_latch.rb +100 -0
  34. data/lib/concurrent/atomic/cyclic_barrier.rb +128 -0
  35. data/lib/concurrent/atomic/event.rb +109 -0
  36. data/lib/concurrent/atomic/java_count_down_latch.rb +42 -0
  37. data/lib/concurrent/atomic/java_thread_local_var.rb +37 -0
  38. data/lib/concurrent/atomic/mutex_atomic_boolean.rb +62 -0
  39. data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +75 -0
  40. data/lib/concurrent/atomic/mutex_count_down_latch.rb +44 -0
  41. data/lib/concurrent/atomic/mutex_semaphore.rb +115 -0
  42. data/lib/concurrent/atomic/read_write_lock.rb +254 -0
  43. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +379 -0
  44. data/lib/concurrent/atomic/ruby_thread_local_var.rb +161 -0
  45. data/lib/concurrent/atomic/semaphore.rb +145 -0
  46. data/lib/concurrent/atomic/thread_local_var.rb +104 -0
  47. data/lib/concurrent/atomic_reference/mutex_atomic.rb +56 -0
  48. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +28 -0
  49. data/lib/concurrent/atomics.rb +10 -0
  50. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +107 -0
  51. data/lib/concurrent/collection/copy_on_write_observer_set.rb +111 -0
  52. data/lib/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
  53. data/lib/concurrent/collection/lock_free_stack.rb +158 -0
  54. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
  55. data/lib/concurrent/collection/map/mri_map_backend.rb +66 -0
  56. data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +140 -0
  57. data/lib/concurrent/collection/map/synchronized_map_backend.rb +82 -0
  58. data/lib/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
  59. data/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb +150 -0
  60. data/lib/concurrent/concern/deprecation.rb +34 -0
  61. data/lib/concurrent/concern/dereferenceable.rb +73 -0
  62. data/lib/concurrent/concern/logging.rb +32 -0
  63. data/lib/concurrent/concern/obligation.rb +220 -0
  64. data/lib/concurrent/concern/observable.rb +110 -0
  65. data/lib/concurrent/concurrent_ruby.jar +0 -0
  66. data/lib/concurrent/configuration.rb +184 -0
  67. data/lib/concurrent/constants.rb +8 -0
  68. data/lib/concurrent/dataflow.rb +81 -0
  69. data/lib/concurrent/delay.rb +199 -0
  70. data/lib/concurrent/errors.rb +69 -0
  71. data/lib/concurrent/exchanger.rb +352 -0
  72. data/lib/concurrent/executor/abstract_executor_service.rb +134 -0
  73. data/lib/concurrent/executor/cached_thread_pool.rb +62 -0
  74. data/lib/concurrent/executor/executor_service.rb +185 -0
  75. data/lib/concurrent/executor/fixed_thread_pool.rb +206 -0
  76. data/lib/concurrent/executor/immediate_executor.rb +66 -0
  77. data/lib/concurrent/executor/indirect_immediate_executor.rb +44 -0
  78. data/lib/concurrent/executor/java_executor_service.rb +91 -0
  79. data/lib/concurrent/executor/java_single_thread_executor.rb +29 -0
  80. data/lib/concurrent/executor/java_thread_pool_executor.rb +123 -0
  81. data/lib/concurrent/executor/ruby_executor_service.rb +78 -0
  82. data/lib/concurrent/executor/ruby_single_thread_executor.rb +22 -0
  83. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +362 -0
  84. data/lib/concurrent/executor/safe_task_executor.rb +35 -0
  85. data/lib/concurrent/executor/serial_executor_service.rb +34 -0
  86. data/lib/concurrent/executor/serialized_execution.rb +107 -0
  87. data/lib/concurrent/executor/serialized_execution_delegator.rb +28 -0
  88. data/lib/concurrent/executor/simple_executor_service.rb +100 -0
  89. data/lib/concurrent/executor/single_thread_executor.rb +56 -0
  90. data/lib/concurrent/executor/thread_pool_executor.rb +87 -0
  91. data/lib/concurrent/executor/timer_set.rb +173 -0
  92. data/lib/concurrent/executors.rb +20 -0
  93. data/lib/concurrent/future.rb +141 -0
  94. data/lib/concurrent/hash.rb +59 -0
  95. data/lib/concurrent/immutable_struct.rb +93 -0
  96. data/lib/concurrent/ivar.rb +207 -0
  97. data/lib/concurrent/map.rb +337 -0
  98. data/lib/concurrent/maybe.rb +229 -0
  99. data/lib/concurrent/mutable_struct.rb +229 -0
  100. data/lib/concurrent/mvar.rb +242 -0
  101. data/lib/concurrent/options.rb +42 -0
  102. data/lib/concurrent/promise.rb +579 -0
  103. data/lib/concurrent/promises.rb +2167 -0
  104. data/lib/concurrent/re_include.rb +58 -0
  105. data/lib/concurrent/scheduled_task.rb +318 -0
  106. data/lib/concurrent/set.rb +66 -0
  107. data/lib/concurrent/settable_struct.rb +129 -0
  108. data/lib/concurrent/synchronization.rb +30 -0
  109. data/lib/concurrent/synchronization/abstract_lockable_object.rb +98 -0
  110. data/lib/concurrent/synchronization/abstract_object.rb +24 -0
  111. data/lib/concurrent/synchronization/abstract_struct.rb +160 -0
  112. data/lib/concurrent/synchronization/condition.rb +60 -0
  113. data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  114. data/lib/concurrent/synchronization/jruby_object.rb +45 -0
  115. data/lib/concurrent/synchronization/lock.rb +36 -0
  116. data/lib/concurrent/synchronization/lockable_object.rb +74 -0
  117. data/lib/concurrent/synchronization/mri_object.rb +44 -0
  118. data/lib/concurrent/synchronization/mutex_lockable_object.rb +76 -0
  119. data/lib/concurrent/synchronization/object.rb +183 -0
  120. data/lib/concurrent/synchronization/rbx_lockable_object.rb +65 -0
  121. data/lib/concurrent/synchronization/rbx_object.rb +49 -0
  122. data/lib/concurrent/synchronization/truffleruby_object.rb +47 -0
  123. data/lib/concurrent/synchronization/volatile.rb +36 -0
  124. data/lib/concurrent/thread_safe/synchronized_delegator.rb +50 -0
  125. data/lib/concurrent/thread_safe/util.rb +16 -0
  126. data/lib/concurrent/thread_safe/util/adder.rb +74 -0
  127. data/lib/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
  128. data/lib/concurrent/thread_safe/util/data_structures.rb +63 -0
  129. data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
  130. data/lib/concurrent/thread_safe/util/striped64.rb +246 -0
  131. data/lib/concurrent/thread_safe/util/volatile.rb +75 -0
  132. data/lib/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
  133. data/lib/concurrent/timer_task.rb +334 -0
  134. data/lib/concurrent/tuple.rb +86 -0
  135. data/lib/concurrent/tvar.rb +258 -0
  136. data/lib/concurrent/utility/at_exit.rb +97 -0
  137. data/lib/concurrent/utility/engine.rb +56 -0
  138. data/lib/concurrent/utility/monotonic_time.rb +58 -0
  139. data/lib/concurrent/utility/native_extension_loader.rb +79 -0
  140. data/lib/concurrent/utility/native_integer.rb +53 -0
  141. data/lib/concurrent/utility/processor_counter.rb +158 -0
  142. data/lib/concurrent/version.rb +3 -0
  143. metadata +193 -0
@@ -0,0 +1,42 @@
1
+ require 'concurrent/configuration'
2
+
3
+ module Concurrent
4
+
5
+ # @!visibility private
6
+ module Options
7
+
8
+ # Get the requested `Executor` based on the values set in the options hash.
9
+ #
10
+ # @param [Hash] opts the options defining the requested executor
11
+ # @option opts [Executor] :executor when set use the given `Executor` instance.
12
+ # Three special values are also supported: `:fast` returns the global fast executor,
13
+ # `:io` returns the global io executor, and `:immediate` returns a new
14
+ # `ImmediateExecutor` object.
15
+ #
16
+ # @return [Executor, nil] the requested thread pool, or nil when no option specified
17
+ #
18
+ # @!visibility private
19
+ def self.executor_from_options(opts = {}) # :nodoc:
20
+ if identifier = opts.fetch(:executor, nil)
21
+ executor(identifier)
22
+ else
23
+ nil
24
+ end
25
+ end
26
+
27
+ def self.executor(executor_identifier)
28
+ case executor_identifier
29
+ when :fast
30
+ Concurrent.global_fast_executor
31
+ when :io
32
+ Concurrent.global_io_executor
33
+ when :immediate
34
+ Concurrent.global_immediate_executor
35
+ when Concurrent::ExecutorService
36
+ executor_identifier
37
+ else
38
+ raise ArgumentError, "executor not recognized by '#{executor_identifier}'"
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,579 @@
1
+ require 'thread'
2
+ require 'concurrent/constants'
3
+ require 'concurrent/errors'
4
+ require 'concurrent/ivar'
5
+ require 'concurrent/executor/safe_task_executor'
6
+
7
+ require 'concurrent/options'
8
+
9
+ module Concurrent
10
+
11
+ PromiseExecutionError = Class.new(StandardError)
12
+
13
+ # Promises are inspired by the JavaScript [Promises/A](http://wiki.commonjs.org/wiki/Promises/A)
14
+ # and [Promises/A+](http://promises-aplus.github.io/promises-spec/) specifications.
15
+ #
16
+ # > A promise represents the eventual value returned from the single
17
+ # > completion of an operation.
18
+ #
19
+ # Promises are similar to futures and share many of the same behaviours.
20
+ # Promises are far more robust, however. Promises can be chained in a tree
21
+ # structure where each promise may have zero or more children. Promises are
22
+ # chained using the `then` method. The result of a call to `then` is always
23
+ # another promise. Promises are resolved asynchronously (with respect to the
24
+ # main thread) but in a strict order: parents are guaranteed to be resolved
25
+ # before their children, children before their younger siblings. The `then`
26
+ # method takes two parameters: an optional block to be executed upon parent
27
+ # resolution and an optional callable to be executed upon parent failure. The
28
+ # result of each promise is passed to each of its children upon resolution.
29
+ # When a promise is rejected all its children will be summarily rejected and
30
+ # will receive the reason.
31
+ #
32
+ # Promises have several possible states: *:unscheduled*, *:pending*,
33
+ # *:processing*, *:rejected*, or *:fulfilled*. These are also aggregated as
34
+ # `#incomplete?` and `#complete?`. When a Promise is created it is set to
35
+ # *:unscheduled*. Once the `#execute` method is called the state becomes
36
+ # *:pending*. Once a job is pulled from the thread pool's queue and is given
37
+ # to a thread for processing (often immediately upon `#post`) the state
38
+ # becomes *:processing*. The future will remain in this state until processing
39
+ # is complete. A future that is in the *:unscheduled*, *:pending*, or
40
+ # *:processing* is considered `#incomplete?`. A `#complete?` Promise is either
41
+ # *:rejected*, indicating that an exception was thrown during processing, or
42
+ # *:fulfilled*, indicating success. If a Promise is *:fulfilled* its `#value`
43
+ # will be updated to reflect the result of the operation. If *:rejected* the
44
+ # `reason` will be updated with a reference to the thrown exception. The
45
+ # predicate methods `#unscheduled?`, `#pending?`, `#rejected?`, and
46
+ # `#fulfilled?` can be called at any time to obtain the state of the Promise,
47
+ # as can the `#state` method, which returns a symbol.
48
+ #
49
+ # Retrieving the value of a promise is done through the `value` (alias:
50
+ # `deref`) method. Obtaining the value of a promise is a potentially blocking
51
+ # operation. When a promise is *rejected* a call to `value` will return `nil`
52
+ # immediately. When a promise is *fulfilled* a call to `value` will
53
+ # immediately return the current value. When a promise is *pending* a call to
54
+ # `value` will block until the promise is either *rejected* or *fulfilled*. A
55
+ # *timeout* value can be passed to `value` to limit how long the call will
56
+ # block. If `nil` the call will block indefinitely. If `0` the call will not
57
+ # block. Any other integer or float value will indicate the maximum number of
58
+ # seconds to block.
59
+ #
60
+ # Promises run on the global thread pool.
61
+ #
62
+ # @!macro copy_options
63
+ #
64
+ # ### Examples
65
+ #
66
+ # Start by requiring promises
67
+ #
68
+ # ```ruby
69
+ # require 'concurrent'
70
+ # ```
71
+ #
72
+ # Then create one
73
+ #
74
+ # ```ruby
75
+ # p = Concurrent::Promise.execute do
76
+ # # do something
77
+ # 42
78
+ # end
79
+ # ```
80
+ #
81
+ # Promises can be chained using the `then` method. The `then` method accepts a
82
+ # block and an executor, to be executed on fulfillment, and a callable argument to be executed
83
+ # on rejection. The result of the each promise is passed as the block argument
84
+ # to chained promises.
85
+ #
86
+ # ```ruby
87
+ # p = Concurrent::Promise.new{10}.then{|x| x * 2}.then{|result| result - 10 }.execute
88
+ # ```
89
+ #
90
+ # And so on, and so on, and so on...
91
+ #
92
+ # ```ruby
93
+ # p = Concurrent::Promise.fulfill(20).
94
+ # then{|result| result - 10 }.
95
+ # then{|result| result * 3 }.
96
+ # then(executor: different_executor){|result| result % 5 }.execute
97
+ # ```
98
+ #
99
+ # The initial state of a newly created Promise depends on the state of its parent:
100
+ # - if parent is *unscheduled* the child will be *unscheduled*
101
+ # - if parent is *pending* the child will be *pending*
102
+ # - if parent is *fulfilled* the child will be *pending*
103
+ # - if parent is *rejected* the child will be *pending* (but will ultimately be *rejected*)
104
+ #
105
+ # Promises are executed asynchronously from the main thread. By the time a
106
+ # child Promise finishes intialization it may be in a different state than its
107
+ # parent (by the time a child is created its parent may have completed
108
+ # execution and changed state). Despite being asynchronous, however, the order
109
+ # of execution of Promise objects in a chain (or tree) is strictly defined.
110
+ #
111
+ # There are multiple ways to create and execute a new `Promise`. Both ways
112
+ # provide identical behavior:
113
+ #
114
+ # ```ruby
115
+ # # create, operate, then execute
116
+ # p1 = Concurrent::Promise.new{ "Hello World!" }
117
+ # p1.state #=> :unscheduled
118
+ # p1.execute
119
+ #
120
+ # # create and immediately execute
121
+ # p2 = Concurrent::Promise.new{ "Hello World!" }.execute
122
+ #
123
+ # # execute during creation
124
+ # p3 = Concurrent::Promise.execute{ "Hello World!" }
125
+ # ```
126
+ #
127
+ # Once the `execute` method is called a `Promise` becomes `pending`:
128
+ #
129
+ # ```ruby
130
+ # p = Concurrent::Promise.execute{ "Hello, world!" }
131
+ # p.state #=> :pending
132
+ # p.pending? #=> true
133
+ # ```
134
+ #
135
+ # Wait a little bit, and the promise will resolve and provide a value:
136
+ #
137
+ # ```ruby
138
+ # p = Concurrent::Promise.execute{ "Hello, world!" }
139
+ # sleep(0.1)
140
+ #
141
+ # p.state #=> :fulfilled
142
+ # p.fulfilled? #=> true
143
+ # p.value #=> "Hello, world!"
144
+ # ```
145
+ #
146
+ # If an exception occurs, the promise will be rejected and will provide
147
+ # a reason for the rejection:
148
+ #
149
+ # ```ruby
150
+ # p = Concurrent::Promise.execute{ raise StandardError.new("Here comes the Boom!") }
151
+ # sleep(0.1)
152
+ #
153
+ # p.state #=> :rejected
154
+ # p.rejected? #=> true
155
+ # p.reason #=> "#<StandardError: Here comes the Boom!>"
156
+ # ```
157
+ #
158
+ # #### Rejection
159
+ #
160
+ # When a promise is rejected all its children will be rejected and will
161
+ # receive the rejection `reason` as the rejection callable parameter:
162
+ #
163
+ # ```ruby
164
+ # p = Concurrent::Promise.execute { Thread.pass; raise StandardError }
165
+ #
166
+ # c1 = p.then(-> reason { 42 })
167
+ # c2 = p.then(-> reason { raise 'Boom!' })
168
+ #
169
+ # c1.wait.state #=> :fulfilled
170
+ # c1.value #=> 45
171
+ # c2.wait.state #=> :rejected
172
+ # c2.reason #=> #<RuntimeError: Boom!>
173
+ # ```
174
+ #
175
+ # Once a promise is rejected it will continue to accept children that will
176
+ # receive immediately rejection (they will be executed asynchronously).
177
+ #
178
+ # #### Aliases
179
+ #
180
+ # The `then` method is the most generic alias: it accepts a block to be
181
+ # executed upon parent fulfillment and a callable to be executed upon parent
182
+ # rejection. At least one of them should be passed. The default block is `{
183
+ # |result| result }` that fulfills the child with the parent value. The
184
+ # default callable is `{ |reason| raise reason }` that rejects the child with
185
+ # the parent reason.
186
+ #
187
+ # - `on_success { |result| ... }` is the same as `then {|result| ... }`
188
+ # - `rescue { |reason| ... }` is the same as `then(Proc.new { |reason| ... } )`
189
+ # - `rescue` is aliased by `catch` and `on_error`
190
+ class Promise < IVar
191
+
192
+ # Initialize a new Promise with the provided options.
193
+ #
194
+ # @!macro executor_and_deref_options
195
+ #
196
+ # @!macro promise_init_options
197
+ #
198
+ # @option opts [Promise] :parent the parent `Promise` when building a chain/tree
199
+ # @option opts [Proc] :on_fulfill fulfillment handler
200
+ # @option opts [Proc] :on_reject rejection handler
201
+ # @option opts [object, Array] :args zero or more arguments to be passed
202
+ # the task block on execution
203
+ #
204
+ # @yield The block operation to be performed asynchronously.
205
+ #
206
+ # @raise [ArgumentError] if no block is given
207
+ #
208
+ # @see http://wiki.commonjs.org/wiki/Promises/A
209
+ # @see http://promises-aplus.github.io/promises-spec/
210
+ def initialize(opts = {}, &block)
211
+ opts.delete_if { |k, v| v.nil? }
212
+ super(NULL, opts.merge(__promise_body_from_block__: block), &nil)
213
+ end
214
+
215
+ # Create a new `Promise` and fulfill it immediately.
216
+ #
217
+ # @!macro executor_and_deref_options
218
+ #
219
+ # @!macro promise_init_options
220
+ #
221
+ # @raise [ArgumentError] if no block is given
222
+ #
223
+ # @return [Promise] the newly created `Promise`
224
+ def self.fulfill(value, opts = {})
225
+ Promise.new(opts).tap { |p| p.send(:synchronized_set_state!, true, value, nil) }
226
+ end
227
+
228
+ # Create a new `Promise` and reject it immediately.
229
+ #
230
+ # @!macro executor_and_deref_options
231
+ #
232
+ # @!macro promise_init_options
233
+ #
234
+ # @raise [ArgumentError] if no block is given
235
+ #
236
+ # @return [Promise] the newly created `Promise`
237
+ def self.reject(reason, opts = {})
238
+ Promise.new(opts).tap { |p| p.send(:synchronized_set_state!, false, nil, reason) }
239
+ end
240
+
241
+ # Execute an `:unscheduled` `Promise`. Immediately sets the state to `:pending` and
242
+ # passes the block to a new thread/thread pool for eventual execution.
243
+ # Does nothing if the `Promise` is in any state other than `:unscheduled`.
244
+ #
245
+ # @return [Promise] a reference to `self`
246
+ def execute
247
+ if root?
248
+ if compare_and_set_state(:pending, :unscheduled)
249
+ set_pending
250
+ realize(@promise_body)
251
+ end
252
+ else
253
+ @parent.execute
254
+ end
255
+ self
256
+ end
257
+
258
+ # @!macro ivar_set_method
259
+ #
260
+ # @raise [Concurrent::PromiseExecutionError] if not the root promise
261
+ def set(value = NULL, &block)
262
+ raise PromiseExecutionError.new('supported only on root promise') unless root?
263
+ check_for_block_or_value!(block_given?, value)
264
+ synchronize do
265
+ if @state != :unscheduled
266
+ raise MultipleAssignmentError
267
+ else
268
+ @promise_body = block || Proc.new { |result| value }
269
+ end
270
+ end
271
+ execute
272
+ end
273
+
274
+ # @!macro ivar_fail_method
275
+ #
276
+ # @raise [Concurrent::PromiseExecutionError] if not the root promise
277
+ def fail(reason = StandardError.new)
278
+ set { raise reason }
279
+ end
280
+
281
+ # Create a new `Promise` object with the given block, execute it, and return the
282
+ # `:pending` object.
283
+ #
284
+ # @!macro executor_and_deref_options
285
+ #
286
+ # @!macro promise_init_options
287
+ #
288
+ # @return [Promise] the newly created `Promise` in the `:pending` state
289
+ #
290
+ # @raise [ArgumentError] if no block is given
291
+ #
292
+ # @example
293
+ # promise = Concurrent::Promise.execute{ sleep(1); 42 }
294
+ # promise.state #=> :pending
295
+ def self.execute(opts = {}, &block)
296
+ new(opts, &block).execute
297
+ end
298
+
299
+ # Chain a new promise off the current promise.
300
+ #
301
+ # @return [Promise] the new promise
302
+ # @yield The block operation to be performed asynchronously.
303
+ # @overload then(rescuer, executor, &block)
304
+ # @param [Proc] rescuer An optional rescue block to be executed if the
305
+ # promise is rejected.
306
+ # @param [ThreadPool] executor An optional thread pool executor to be used
307
+ # in the new Promise
308
+ # @overload then(rescuer, executor: executor, &block)
309
+ # @param [Proc] rescuer An optional rescue block to be executed if the
310
+ # promise is rejected.
311
+ # @param [ThreadPool] executor An optional thread pool executor to be used
312
+ # in the new Promise
313
+ def then(*args, &block)
314
+ if args.last.is_a?(::Hash)
315
+ executor = args.pop[:executor]
316
+ rescuer = args.first
317
+ else
318
+ rescuer, executor = args
319
+ end
320
+
321
+ executor ||= @executor
322
+
323
+ raise ArgumentError.new('rescuers and block are both missing') if rescuer.nil? && !block_given?
324
+ block = Proc.new { |result| result } unless block_given?
325
+ child = Promise.new(
326
+ parent: self,
327
+ executor: executor,
328
+ on_fulfill: block,
329
+ on_reject: rescuer
330
+ )
331
+
332
+ synchronize do
333
+ child.state = :pending if @state == :pending
334
+ child.on_fulfill(apply_deref_options(@value)) if @state == :fulfilled
335
+ child.on_reject(@reason) if @state == :rejected
336
+ @children << child
337
+ end
338
+
339
+ child
340
+ end
341
+
342
+ # Chain onto this promise an action to be undertaken on success
343
+ # (fulfillment).
344
+ #
345
+ # @yield The block to execute
346
+ #
347
+ # @return [Promise] self
348
+ def on_success(&block)
349
+ raise ArgumentError.new('no block given') unless block_given?
350
+ self.then(&block)
351
+ end
352
+
353
+ # Chain onto this promise an action to be undertaken on failure
354
+ # (rejection).
355
+ #
356
+ # @yield The block to execute
357
+ #
358
+ # @return [Promise] self
359
+ def rescue(&block)
360
+ self.then(block)
361
+ end
362
+
363
+ alias_method :catch, :rescue
364
+ alias_method :on_error, :rescue
365
+
366
+ # Yield the successful result to the block that returns a promise. If that
367
+ # promise is also successful the result is the result of the yielded promise.
368
+ # If either part fails the whole also fails.
369
+ #
370
+ # @example
371
+ # Promise.execute { 1 }.flat_map { |v| Promise.execute { v + 2 } }.value! #=> 3
372
+ #
373
+ # @return [Promise]
374
+ def flat_map(&block)
375
+ child = Promise.new(
376
+ parent: self,
377
+ executor: ImmediateExecutor.new,
378
+ )
379
+
380
+ on_error { |e| child.on_reject(e) }
381
+ on_success do |result1|
382
+ begin
383
+ inner = block.call(result1)
384
+ inner.execute
385
+ inner.on_success { |result2| child.on_fulfill(result2) }
386
+ inner.on_error { |e| child.on_reject(e) }
387
+ rescue => e
388
+ child.on_reject(e)
389
+ end
390
+ end
391
+
392
+ child
393
+ end
394
+
395
+ # Builds a promise that produces the result of promises in an Array
396
+ # and fails if any of them fails.
397
+ #
398
+ # @overload zip(*promises)
399
+ # @param [Array<Promise>] promises
400
+ #
401
+ # @overload zip(*promises, opts)
402
+ # @param [Array<Promise>] promises
403
+ # @param [Hash] opts the configuration options
404
+ # @option opts [Executor] :executor (ImmediateExecutor.new) when set use the given `Executor` instance.
405
+ # @option opts [Boolean] :execute (true) execute promise before returning
406
+ #
407
+ # @return [Promise<Array>]
408
+ def self.zip(*promises)
409
+ opts = promises.last.is_a?(::Hash) ? promises.pop.dup : {}
410
+ opts[:executor] ||= ImmediateExecutor.new
411
+ zero = if !opts.key?(:execute) || opts.delete(:execute)
412
+ fulfill([], opts)
413
+ else
414
+ Promise.new(opts) { [] }
415
+ end
416
+
417
+ promises.reduce(zero) do |p1, p2|
418
+ p1.flat_map do |results|
419
+ p2.then do |next_result|
420
+ results << next_result
421
+ end
422
+ end
423
+ end
424
+ end
425
+
426
+ # Builds a promise that produces the result of self and others in an Array
427
+ # and fails if any of them fails.
428
+ #
429
+ # @overload zip(*promises)
430
+ # @param [Array<Promise>] others
431
+ #
432
+ # @overload zip(*promises, opts)
433
+ # @param [Array<Promise>] others
434
+ # @param [Hash] opts the configuration options
435
+ # @option opts [Executor] :executor (ImmediateExecutor.new) when set use the given `Executor` instance.
436
+ # @option opts [Boolean] :execute (true) execute promise before returning
437
+ #
438
+ # @return [Promise<Array>]
439
+ def zip(*others)
440
+ self.class.zip(self, *others)
441
+ end
442
+
443
+ # Aggregates a collection of promises and executes the `then` condition
444
+ # if all aggregated promises succeed. Executes the `rescue` handler with
445
+ # a `Concurrent::PromiseExecutionError` if any of the aggregated promises
446
+ # fail. Upon execution will execute any of the aggregate promises that
447
+ # were not already executed.
448
+ #
449
+ # @!macro promise_self_aggregate
450
+ #
451
+ # The returned promise will not yet have been executed. Additional `#then`
452
+ # and `#rescue` handlers may still be provided. Once the returned promise
453
+ # is execute the aggregate promises will be also be executed (if they have
454
+ # not been executed already). The results of the aggregate promises will
455
+ # be checked upon completion. The necessary `#then` and `#rescue` blocks
456
+ # on the aggregating promise will then be executed as appropriate. If the
457
+ # `#rescue` handlers are executed the raises exception will be
458
+ # `Concurrent::PromiseExecutionError`.
459
+ #
460
+ # @param [Array] promises Zero or more promises to aggregate
461
+ # @return [Promise] an unscheduled (not executed) promise that aggregates
462
+ # the promises given as arguments
463
+ def self.all?(*promises)
464
+ aggregate(:all?, *promises)
465
+ end
466
+
467
+ # Aggregates a collection of promises and executes the `then` condition
468
+ # if any aggregated promises succeed. Executes the `rescue` handler with
469
+ # a `Concurrent::PromiseExecutionError` if any of the aggregated promises
470
+ # fail. Upon execution will execute any of the aggregate promises that
471
+ # were not already executed.
472
+ #
473
+ # @!macro promise_self_aggregate
474
+ def self.any?(*promises)
475
+ aggregate(:any?, *promises)
476
+ end
477
+
478
+ protected
479
+
480
+ def ns_initialize(value, opts)
481
+ super
482
+
483
+ @executor = Options.executor_from_options(opts) || Concurrent.global_io_executor
484
+ @args = get_arguments_from(opts)
485
+
486
+ @parent = opts.fetch(:parent) { nil }
487
+ @on_fulfill = opts.fetch(:on_fulfill) { Proc.new { |result| result } }
488
+ @on_reject = opts.fetch(:on_reject) { Proc.new { |reason| raise reason } }
489
+
490
+ @promise_body = opts[:__promise_body_from_block__] || Proc.new { |result| result }
491
+ @state = :unscheduled
492
+ @children = []
493
+ end
494
+
495
+ # Aggregate a collection of zero or more promises under a composite promise,
496
+ # execute the aggregated promises and collect them into a standard Ruby array,
497
+ # call the given Ruby `Ennnumerable` predicate (such as `any?`, `all?`, `none?`,
498
+ # or `one?`) on the collection checking for the success or failure of each,
499
+ # then executing the composite's `#then` handlers if the predicate returns
500
+ # `true` or executing the composite's `#rescue` handlers if the predicate
501
+ # returns false.
502
+ #
503
+ # @!macro promise_self_aggregate
504
+ def self.aggregate(method, *promises)
505
+ composite = Promise.new do
506
+ completed = promises.collect do |promise|
507
+ promise.execute if promise.unscheduled?
508
+ promise.wait
509
+ promise
510
+ end
511
+ unless completed.empty? || completed.send(method){|promise| promise.fulfilled? }
512
+ raise PromiseExecutionError
513
+ end
514
+ end
515
+ composite
516
+ end
517
+
518
+ # @!visibility private
519
+ def set_pending
520
+ synchronize do
521
+ @state = :pending
522
+ @children.each { |c| c.set_pending }
523
+ end
524
+ end
525
+
526
+ # @!visibility private
527
+ def root? # :nodoc:
528
+ @parent.nil?
529
+ end
530
+
531
+ # @!visibility private
532
+ def on_fulfill(result)
533
+ realize Proc.new { @on_fulfill.call(result) }
534
+ nil
535
+ end
536
+
537
+ # @!visibility private
538
+ def on_reject(reason)
539
+ realize Proc.new { @on_reject.call(reason) }
540
+ nil
541
+ end
542
+
543
+ # @!visibility private
544
+ def notify_child(child)
545
+ if_state(:fulfilled) { child.on_fulfill(apply_deref_options(@value)) }
546
+ if_state(:rejected) { child.on_reject(@reason) }
547
+ end
548
+
549
+ # @!visibility private
550
+ def complete(success, value, reason)
551
+ children_to_notify = synchronize do
552
+ set_state!(success, value, reason)
553
+ @children.dup
554
+ end
555
+
556
+ children_to_notify.each { |child| notify_child(child) }
557
+ observers.notify_and_delete_observers{ [Time.now, self.value, reason] }
558
+ end
559
+
560
+ # @!visibility private
561
+ def realize(task)
562
+ @executor.post do
563
+ success, value, reason = SafeTaskExecutor.new(task, rescue_exception: true).execute(*@args)
564
+ complete(success, value, reason)
565
+ end
566
+ end
567
+
568
+ # @!visibility private
569
+ def set_state!(success, value, reason)
570
+ set_state(success, value, reason)
571
+ event.set
572
+ end
573
+
574
+ # @!visibility private
575
+ def synchronized_set_state!(success, value, reason)
576
+ synchronize { set_state!(success, value, reason) }
577
+ end
578
+ end
579
+ end