concurrent-ruby 1.0.5 → 1.1.0.pre1

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 (107) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +42 -0
  3. data/Gemfile +39 -0
  4. data/{LICENSE.txt → LICENSE.md} +2 -0
  5. data/README.md +203 -105
  6. data/Rakefile +278 -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 +304 -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 +24 -20
  24. data/lib/concurrent/agent.rb +7 -7
  25. data/lib/concurrent/array.rb +59 -32
  26. data/lib/concurrent/async.rb +4 -4
  27. data/lib/concurrent/atom.rb +9 -9
  28. data/lib/concurrent/atomic/atomic_boolean.rb +24 -20
  29. data/lib/concurrent/atomic/atomic_fixnum.rb +27 -23
  30. data/lib/concurrent/atomic/atomic_markable_reference.rb +164 -0
  31. data/lib/concurrent/atomic/atomic_reference.rb +176 -33
  32. data/lib/concurrent/atomic/count_down_latch.rb +6 -6
  33. data/lib/concurrent/atomic/cyclic_barrier.rb +1 -1
  34. data/lib/concurrent/atomic/event.rb +1 -1
  35. data/lib/concurrent/atomic/java_count_down_latch.rb +6 -5
  36. data/lib/concurrent/atomic/mutex_count_down_latch.rb +1 -0
  37. data/lib/concurrent/atomic/read_write_lock.rb +2 -1
  38. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
  39. data/lib/concurrent/atomic/semaphore.rb +8 -8
  40. data/lib/concurrent/atomic/thread_local_var.rb +7 -7
  41. data/lib/concurrent/atomic_reference/mutex_atomic.rb +3 -8
  42. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +1 -1
  43. data/lib/concurrent/atomics.rb +0 -43
  44. data/lib/concurrent/collection/lock_free_stack.rb +127 -0
  45. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +3 -3
  46. data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +1 -2
  47. data/lib/concurrent/collection/non_concurrent_priority_queue.rb +29 -29
  48. data/lib/concurrent/concern/dereferenceable.rb +1 -1
  49. data/lib/concurrent/concern/logging.rb +6 -1
  50. data/lib/concurrent/concern/observable.rb +7 -7
  51. data/lib/concurrent/concurrent_ruby.jar +0 -0
  52. data/lib/concurrent/configuration.rb +1 -6
  53. data/lib/concurrent/constants.rb +1 -1
  54. data/lib/concurrent/dataflow.rb +2 -1
  55. data/lib/concurrent/delay.rb +9 -7
  56. data/lib/concurrent/exchanger.rb +13 -21
  57. data/lib/concurrent/executor/abstract_executor_service.rb +2 -2
  58. data/lib/concurrent/executor/cached_thread_pool.rb +1 -1
  59. data/lib/concurrent/executor/executor_service.rb +15 -15
  60. data/lib/concurrent/executor/fixed_thread_pool.rb +18 -18
  61. data/lib/concurrent/executor/java_thread_pool_executor.rb +10 -7
  62. data/lib/concurrent/executor/single_thread_executor.rb +2 -2
  63. data/lib/concurrent/executor/thread_pool_executor.rb +6 -6
  64. data/lib/concurrent/executor/timer_set.rb +1 -1
  65. data/lib/concurrent/future.rb +4 -1
  66. data/lib/concurrent/hash.rb +53 -30
  67. data/lib/concurrent/ivar.rb +5 -6
  68. data/lib/concurrent/map.rb +20 -25
  69. data/lib/concurrent/maybe.rb +1 -1
  70. data/lib/concurrent/mutable_struct.rb +15 -14
  71. data/lib/concurrent/mvar.rb +2 -2
  72. data/lib/concurrent/promise.rb +53 -21
  73. data/lib/concurrent/promises.rb +1938 -0
  74. data/lib/concurrent/re_include.rb +58 -0
  75. data/lib/concurrent/set.rb +66 -0
  76. data/lib/concurrent/settable_struct.rb +1 -0
  77. data/lib/concurrent/synchronization.rb +4 -5
  78. data/lib/concurrent/synchronization/abstract_lockable_object.rb +5 -5
  79. data/lib/concurrent/synchronization/abstract_struct.rb +6 -4
  80. data/lib/concurrent/synchronization/lockable_object.rb +6 -6
  81. data/lib/concurrent/synchronization/{mri_lockable_object.rb → mutex_lockable_object.rb} +19 -14
  82. data/lib/concurrent/synchronization/object.rb +8 -4
  83. data/lib/concurrent/synchronization/truffleruby_object.rb +46 -0
  84. data/lib/concurrent/synchronization/volatile.rb +11 -9
  85. data/lib/concurrent/thread_safe/util/data_structures.rb +55 -0
  86. data/lib/concurrent/thread_safe/util/striped64.rb +9 -4
  87. data/lib/concurrent/timer_task.rb +5 -2
  88. data/lib/concurrent/tuple.rb +1 -1
  89. data/lib/concurrent/tvar.rb +2 -2
  90. data/lib/concurrent/utility/at_exit.rb +1 -1
  91. data/lib/concurrent/utility/engine.rb +2 -2
  92. data/lib/concurrent/utility/monotonic_time.rb +3 -3
  93. data/lib/concurrent/utility/native_extension_loader.rb +31 -33
  94. data/lib/concurrent/utility/processor_counter.rb +0 -2
  95. data/lib/concurrent/version.rb +2 -2
  96. metadata +35 -21
  97. data/lib/concurrent/atomic_reference/concurrent_update_error.rb +0 -8
  98. data/lib/concurrent/atomic_reference/direct_update.rb +0 -81
  99. data/lib/concurrent/atomic_reference/jruby+truffle.rb +0 -2
  100. data/lib/concurrent/atomic_reference/jruby.rb +0 -16
  101. data/lib/concurrent/atomic_reference/rbx.rb +0 -22
  102. data/lib/concurrent/atomic_reference/ruby.rb +0 -32
  103. data/lib/concurrent/edge.rb +0 -26
  104. data/lib/concurrent/lazy_register.rb +0 -81
  105. data/lib/concurrent/synchronization/truffle_lockable_object.rb +0 -9
  106. data/lib/concurrent/synchronization/truffle_object.rb +0 -31
  107. data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +0 -30
@@ -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
@@ -298,16 +298,28 @@ module Concurrent
298
298
 
299
299
  # Chain a new promise off the current promise.
300
300
  #
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
301
  # @return [Promise] the new promise
310
- def then(rescuer = nil, executor = @executor, &block)
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
+
311
323
  raise ArgumentError.new('rescuers and block are both missing') if rescuer.nil? && !block_given?
312
324
  block = Proc.new { |result| result } unless block_given?
313
325
  child = Promise.new(
@@ -383,11 +395,24 @@ module Concurrent
383
395
  # Builds a promise that produces the result of promises in an Array
384
396
  # and fails if any of them fails.
385
397
  #
386
- # @param [Array<Promise>] promises
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
387
406
  #
388
407
  # @return [Promise<Array>]
389
408
  def self.zip(*promises)
390
- zero = fulfill([], executor: ImmediateExecutor.new)
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
391
416
 
392
417
  promises.reduce(zero) do |p1, p2|
393
418
  p1.flat_map do |results|
@@ -401,7 +426,14 @@ module Concurrent
401
426
  # Builds a promise that produces the result of self and others in an Array
402
427
  # and fails if any of them fails.
403
428
  #
404
- # @param [Array<Promise>] others
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
405
437
  #
406
438
  # @return [Promise<Array>]
407
439
  def zip(*others)
@@ -414,7 +446,7 @@ module Concurrent
414
446
  # fail. Upon execution will execute any of the aggregate promises that
415
447
  # were not already executed.
416
448
  #
417
- # @!macro [attach] promise_self_aggregate
449
+ # @!macro promise_self_aggregate
418
450
  #
419
451
  # The returned promise will not yet have been executed. Additional `#then`
420
452
  # and `#rescue` handlers may still be provided. Once the returned promise
@@ -0,0 +1,1938 @@
1
+ require 'concurrent/synchronization'
2
+ require 'concurrent/atomic/atomic_boolean'
3
+ require 'concurrent/atomic/atomic_fixnum'
4
+ require 'concurrent/collection/lock_free_stack'
5
+ require 'concurrent/errors'
6
+ require 'concurrent/re_include'
7
+
8
+ module Concurrent
9
+
10
+ # {include:file:docs-source/promises-main.md}
11
+ module Promises
12
+
13
+ # @!macro promises.param.default_executor
14
+ # @param [Executor, :io, :fast] default_executor Instance of an executor or a name of the
15
+ # global executor. Default executor propagates to chained futures unless overridden with
16
+ # executor parameter or changed with {AbstractEventFuture#with_default_executor}.
17
+ #
18
+ # @!macro promises.param.executor
19
+ # @param [Executor, :io, :fast] executor Instance of an executor or a name of the
20
+ # global executor. The task is executed on it, default executor remains unchanged.
21
+ #
22
+ # @!macro promises.param.args
23
+ # @param [Object] args arguments which are passed to the task when it's executed.
24
+ # (It might be prepended with other arguments, see the @yeild section).
25
+ #
26
+ # @!macro promises.shortcut.on
27
+ # Shortcut of {#$0_on} with default `:io` executor supplied.
28
+ # @see #$0_on
29
+ #
30
+ # @!macro promises.shortcut.using
31
+ # Shortcut of {#$0_using} with default `:io` executor supplied.
32
+ # @see #$0_using
33
+ #
34
+ # @!macro promise.param.task-future
35
+ # @yieldreturn will become result of the returned Future.
36
+ # Its returned value becomes {Future#value} fulfilling it,
37
+ # raised exception becomes {Future#reason} rejecting it.
38
+ #
39
+ # @!macro promise.param.callback
40
+ # @yieldreturn is forgotten.
41
+
42
+ # Container of all {Future}, {Event} factory methods. They are never constructed directly with
43
+ # new.
44
+ module FactoryMethods
45
+ extend ReInclude
46
+
47
+ module Configuration
48
+ # @return [Executor, :io, :fast] the executor which is used when none is supplied
49
+ # to a factory method. The method can be overridden in the receivers of
50
+ # `include FactoryMethod`
51
+ def default_executor
52
+ :io
53
+ end
54
+ end
55
+
56
+ include Configuration
57
+
58
+ # @!macro promises.shortcut.on
59
+ # @return [ResolvableEvent]
60
+ def resolvable_event
61
+ resolvable_event_on default_executor
62
+ end
63
+
64
+ # Created resolvable event, user is responsible for resolving the event once by
65
+ # {Promises::ResolvableEvent#resolve}.
66
+ #
67
+ # @!macro promises.param.default_executor
68
+ # @return [ResolvableEvent]
69
+ def resolvable_event_on(default_executor = self.default_executor)
70
+ ResolvableEventPromise.new(default_executor).future
71
+ end
72
+
73
+ # @!macro promises.shortcut.on
74
+ # @return [ResolvableFuture]
75
+ def resolvable_future
76
+ resolvable_future_on default_executor
77
+ end
78
+
79
+ # Creates resolvable future, user is responsible for resolving the future once by
80
+ # {Promises::ResolvableFuture#resolve}, {Promises::ResolvableFuture#fulfill},
81
+ # or {Promises::ResolvableFuture#reject}
82
+ #
83
+ # @!macro promises.param.default_executor
84
+ # @return [ResolvableFuture]
85
+ def resolvable_future_on(default_executor = self.default_executor)
86
+ ResolvableFuturePromise.new(default_executor).future
87
+ end
88
+
89
+ # @!macro promises.shortcut.on
90
+ # @return [Future]
91
+ def future(*args, &task)
92
+ future_on(default_executor, *args, &task)
93
+ end
94
+
95
+ # @!macro promises.future-on1
96
+ # Constructs new Future which will be resolved after block is evaluated on default executor.
97
+ # Evaluation begins immediately.
98
+ #
99
+ # @!macro promises.future-on2
100
+ # @!macro promises.param.default_executor
101
+ # @!macro promises.param.args
102
+ # @yield [*args] to the task.
103
+ # @!macro promise.param.task-future
104
+ # @return [Future]
105
+ def future_on(default_executor, *args, &task)
106
+ ImmediateEventPromise.new(default_executor).future.then(*args, &task)
107
+ end
108
+
109
+ # Creates resolved future with will be either fulfilled with the given value or rejection with
110
+ # the given reason.
111
+ #
112
+ # @!macro promises.param.default_executor
113
+ # @return [Future]
114
+ def resolved_future(fulfilled, value, reason, default_executor = self.default_executor)
115
+ ImmediateFuturePromise.new(default_executor, fulfilled, value, reason).future
116
+ end
117
+
118
+ # Creates resolved future with will be fulfilled with the given value.
119
+ #
120
+ # @!macro promises.param.default_executor
121
+ # @return [Future]
122
+ def fulfilled_future(value, default_executor = self.default_executor)
123
+ resolved_future true, value, nil, default_executor
124
+ end
125
+
126
+ # Creates resolved future with will be rejected with the given reason.
127
+ #
128
+ # @!macro promises.param.default_executor
129
+ # @return [Future]
130
+ def rejected_future(reason, default_executor = self.default_executor)
131
+ resolved_future false, nil, reason, default_executor
132
+ end
133
+
134
+ # Creates resolved event.
135
+ #
136
+ # @!macro promises.param.default_executor
137
+ # @return [Event]
138
+ def resolved_event(default_executor = self.default_executor)
139
+ ImmediateEventPromise.new(default_executor).event
140
+ end
141
+
142
+ # General constructor. Behaves differently based on the argument's type. It's provided for convenience
143
+ # but it's better to be explicit.
144
+ #
145
+ # @see rejected_future, resolved_event, fulfilled_future
146
+ # @!macro promises.param.default_executor
147
+ # @return [Event, Future]
148
+ #
149
+ # @overload create(nil, default_executor = self.default_executor)
150
+ # @param [nil] nil
151
+ # @return [Event] resolved event.
152
+ #
153
+ # @overload create(a_future, default_executor = self.default_executor)
154
+ # @param [Future] a_future
155
+ # @return [Future] a future which will be resolved when a_future is.
156
+ #
157
+ # @overload create(an_event, default_executor = self.default_executor)
158
+ # @param [Event] an_event
159
+ # @return [Event] an event which will be resolved when an_event is.
160
+ #
161
+ # @overload create(exception, default_executor = self.default_executor)
162
+ # @param [Exception] exception
163
+ # @return [Future] a rejected future with the exception as its reason.
164
+ #
165
+ # @overload create(value, default_executor = self.default_executor)
166
+ # @param [Object] value when none of the above overloads fits
167
+ # @return [Future] a fulfilled future with the value.
168
+ def make_future(argument = nil, default_executor = self.default_executor)
169
+ case argument
170
+ when AbstractEventFuture
171
+ # returning wrapper would change nothing
172
+ argument
173
+ when Exception
174
+ rejected_future argument, default_executor
175
+ when nil
176
+ resolved_event default_executor
177
+ else
178
+ fulfilled_future argument, default_executor
179
+ end
180
+ end
181
+
182
+ # @!macro promises.shortcut.on
183
+ # @return [Future]
184
+ def delay(*args, &task)
185
+ delay_on default_executor, *args, &task
186
+ end
187
+
188
+ # @!macro promises.future-on1
189
+ # The task will be evaluated only after the future is touched, see {AbstractEventFuture#touch}
190
+ #
191
+ # @!macro promises.future-on2
192
+ def delay_on(default_executor, *args, &task)
193
+ DelayPromise.new(default_executor).event.chain(*args, &task)
194
+ end
195
+
196
+ # @!macro promises.shortcut.on
197
+ # @return [Future]
198
+ def schedule(intended_time, *args, &task)
199
+ schedule_on default_executor, intended_time, *args, &task
200
+ end
201
+
202
+ # @!macro promises.future-on1
203
+ # The task is planned for execution in intended_time.
204
+ #
205
+ # @!macro promises.future-on2
206
+ # @!macro promises.param.intended_time
207
+ # @param [Numeric, Time] intended_time `Numeric` means to run in `intended_time` seconds.
208
+ # `Time` means to run on `intended_time`.
209
+ def schedule_on(default_executor, intended_time, *args, &task)
210
+ ScheduledPromise.new(default_executor, intended_time).event.chain(*args, &task)
211
+ end
212
+
213
+ # @!macro promises.shortcut.on
214
+ # @return [Future]
215
+ def zip_futures(*futures_and_or_events)
216
+ zip_futures_on default_executor, *futures_and_or_events
217
+ end
218
+
219
+ # Creates new future which is resolved after all futures_and_or_events are resolved.
220
+ # Its value is array of zipped future values. Its reason is array of reasons for rejection.
221
+ # If there is an error it rejects.
222
+ # @!macro promises.event-conversion
223
+ # If event is supplied, which does not have value and can be only resolved, it's
224
+ # represented as `:fulfilled` with value `nil`.
225
+ #
226
+ # @!macro promises.param.default_executor
227
+ # @param [AbstractEventFuture] futures_and_or_events
228
+ # @return [Future]
229
+ def zip_futures_on(default_executor, *futures_and_or_events)
230
+ ZipFuturesPromise.new_blocked_by(futures_and_or_events, default_executor).future
231
+ end
232
+
233
+ alias_method :zip, :zip_futures
234
+
235
+ # @!macro promises.shortcut.on
236
+ # @return [Event]
237
+ def zip_events(*futures_and_or_events)
238
+ zip_events_on default_executor, *futures_and_or_events
239
+ end
240
+
241
+ # Creates new event which is resolved after all futures_and_or_events are resolved.
242
+ # (Future is resolved when fulfilled or rejected.)
243
+ #
244
+ # @!macro promises.param.default_executor
245
+ # @param [AbstractEventFuture] futures_and_or_events
246
+ # @return [Event]
247
+ def zip_events_on(default_executor, *futures_and_or_events)
248
+ ZipEventsPromise.new_blocked_by(futures_and_or_events, default_executor).event
249
+ end
250
+
251
+ # @!macro promises.shortcut.on
252
+ # @return [Future]
253
+ def any_resolved_future(*futures_and_or_events)
254
+ any_resolved_future_on default_executor, *futures_and_or_events
255
+ end
256
+
257
+ alias_method :any, :any_resolved_future
258
+
259
+ # Creates new future which is resolved after first futures_and_or_events is resolved.
260
+ # Its result equals result of the first resolved future.
261
+ # @!macro promises.any-touch
262
+ # If resolved it does not propagate {AbstractEventFuture#touch}, leaving delayed
263
+ # futures un-executed if they are not required any more.
264
+ # @!macro promises.event-conversion
265
+ #
266
+ # @!macro promises.param.default_executor
267
+ # @param [AbstractEventFuture] futures_and_or_events
268
+ # @return [Future]
269
+ def any_resolved_future_on(default_executor, *futures_and_or_events)
270
+ AnyResolvedFuturePromise.new_blocked_by(futures_and_or_events, default_executor).future
271
+ end
272
+
273
+ # @!macro promises.shortcut.on
274
+ # @return [Future]
275
+ def any_fulfilled_future(*futures_and_or_events)
276
+ any_fulfilled_future_on default_executor, *futures_and_or_events
277
+ end
278
+
279
+ # Creates new future which is resolved after first of futures_and_or_events is fulfilled.
280
+ # Its result equals result of the first resolved future or if all futures_and_or_events reject,
281
+ # it has reason of the last resolved future.
282
+ # @!macro promises.any-touch
283
+ # @!macro promises.event-conversion
284
+ #
285
+ # @!macro promises.param.default_executor
286
+ # @param [AbstractEventFuture] futures_and_or_events
287
+ # @return [Future]
288
+ def any_fulfilled_future_on(default_executor, *futures_and_or_events)
289
+ AnyFulfilledFuturePromise.new_blocked_by(futures_and_or_events, default_executor).future
290
+ end
291
+
292
+ # @!macro promises.shortcut.on
293
+ # @return [Future]
294
+ def any_event(*futures_and_or_events)
295
+ any_event_on default_executor, *futures_and_or_events
296
+ end
297
+
298
+ # Creates new event which becomes resolved after first of the futures_and_or_events resolves.
299
+ # @!macro promises.any-touch
300
+ #
301
+ # @!macro promises.param.default_executor
302
+ # @param [AbstractEventFuture] futures_and_or_events
303
+ # @return [Event]
304
+ def any_event_on(default_executor, *futures_and_or_events)
305
+ AnyResolvedEventPromise.new_blocked_by(futures_and_or_events, default_executor).event
306
+ end
307
+
308
+ # TODO consider adding first(count, *futures)
309
+ # TODO consider adding zip_by(slice, *futures) processing futures in slices
310
+ # TODO or rather a generic aggregator taking a function
311
+ end
312
+
313
+ module InternalStates
314
+ # @private
315
+ class State
316
+ def resolved?
317
+ raise NotImplementedError
318
+ end
319
+
320
+ def to_sym
321
+ raise NotImplementedError
322
+ end
323
+ end
324
+
325
+ private_constant :State
326
+
327
+ # @private
328
+ class Pending < State
329
+ def resolved?
330
+ false
331
+ end
332
+
333
+ def to_sym
334
+ :pending
335
+ end
336
+ end
337
+
338
+ private_constant :Pending
339
+
340
+ # @private
341
+ class ResolvedWithResult < State
342
+ def resolved?
343
+ true
344
+ end
345
+
346
+ def to_sym
347
+ :resolved
348
+ end
349
+
350
+ def result
351
+ [fulfilled?, value, reason]
352
+ end
353
+
354
+ def fulfilled?
355
+ raise NotImplementedError
356
+ end
357
+
358
+ def value
359
+ raise NotImplementedError
360
+ end
361
+
362
+ def reason
363
+ raise NotImplementedError
364
+ end
365
+
366
+ def apply
367
+ raise NotImplementedError
368
+ end
369
+ end
370
+
371
+ private_constant :ResolvedWithResult
372
+
373
+ # @private
374
+ class Fulfilled < ResolvedWithResult
375
+
376
+ def initialize(value)
377
+ @Value = value
378
+ end
379
+
380
+ def fulfilled?
381
+ true
382
+ end
383
+
384
+ def apply(args, block)
385
+ block.call value, *args
386
+ end
387
+
388
+ def value
389
+ @Value
390
+ end
391
+
392
+ def reason
393
+ nil
394
+ end
395
+
396
+ def to_sym
397
+ :fulfilled
398
+ end
399
+ end
400
+
401
+ private_constant :Fulfilled
402
+
403
+ # @private
404
+ class FulfilledArray < Fulfilled
405
+ def apply(args, block)
406
+ block.call(*value, *args)
407
+ end
408
+ end
409
+
410
+ private_constant :FulfilledArray
411
+
412
+ # @private
413
+ class Rejected < ResolvedWithResult
414
+ def initialize(reason)
415
+ @Reason = reason
416
+ end
417
+
418
+ def fulfilled?
419
+ false
420
+ end
421
+
422
+ def value
423
+ nil
424
+ end
425
+
426
+ def reason
427
+ @Reason
428
+ end
429
+
430
+ def to_sym
431
+ :rejected
432
+ end
433
+
434
+ def apply(args, block)
435
+ block.call reason, *args
436
+ end
437
+ end
438
+
439
+ private_constant :Rejected
440
+
441
+ # @private
442
+ class PartiallyRejected < ResolvedWithResult
443
+ def initialize(value, reason)
444
+ super()
445
+ @Value = value
446
+ @Reason = reason
447
+ end
448
+
449
+ def fulfilled?
450
+ false
451
+ end
452
+
453
+ def to_sym
454
+ :rejected
455
+ end
456
+
457
+ def value
458
+ @Value
459
+ end
460
+
461
+ def reason
462
+ @Reason
463
+ end
464
+
465
+ def apply(args, block)
466
+ block.call(*reason, *args)
467
+ end
468
+ end
469
+
470
+ private_constant :PartiallyRejected
471
+
472
+ PENDING = Pending.new
473
+ RESOLVED = Fulfilled.new(nil)
474
+
475
+ def RESOLVED.to_sym
476
+ :resolved
477
+ end
478
+
479
+ private_constant :PENDING, :RESOLVED
480
+ end
481
+
482
+ private_constant :InternalStates
483
+
484
+ # Common ancestor of {Event} and {Future} classes, many shared methods are defined here.
485
+ class AbstractEventFuture < Synchronization::Object
486
+ safe_initialization!
487
+ private(*attr_atomic(:internal_state) - [:internal_state])
488
+
489
+ include InternalStates
490
+
491
+ def initialize(promise, default_executor)
492
+ super()
493
+ @Lock = Mutex.new
494
+ @Condition = ConditionVariable.new
495
+ @Promise = promise
496
+ @DefaultExecutor = default_executor
497
+ @Callbacks = LockFreeStack.new
498
+ # noinspection RubyArgCount
499
+ @Waiters = AtomicFixnum.new 0
500
+ self.internal_state = PENDING
501
+ end
502
+
503
+ private :initialize
504
+
505
+ # @!macro promises.shortcut.event-future
506
+ # @see Event#$0
507
+ # @see Future#$0
508
+
509
+ # @!macro promises.param.timeout
510
+ # @param [Numeric] timeout the maximum time in second to wait.
511
+
512
+ # @!macro promises.warn.blocks
513
+ # @note This function potentially blocks current thread until the Future is resolved.
514
+ # Be careful it can deadlock. Try to chain instead.
515
+
516
+ # Returns its state.
517
+ # @return [Symbol]
518
+ #
519
+ # @overload an_event.state
520
+ # @return [:pending, :resolved]
521
+ # @overload a_future.state
522
+ # Both :fulfilled, :rejected implies :resolved.
523
+ # @return [:pending, :fulfilled, :rejected]
524
+ def state
525
+ internal_state.to_sym
526
+ end
527
+
528
+ # Is it in pending state?
529
+ # @return [Boolean]
530
+ def pending?
531
+ !internal_state.resolved?
532
+ end
533
+
534
+ # Is it in resolved state?
535
+ # @return [Boolean]
536
+ def resolved?
537
+ internal_state.resolved?
538
+ end
539
+
540
+ # Propagates touch. Requests all the delayed futures, which it depends on, to be
541
+ # executed. This method is called by any other method requiring resolved state, like {#wait}.
542
+ # @return [self]
543
+ def touch
544
+ @Promise.touch
545
+ self
546
+ end
547
+
548
+ # @!macro promises.touches
549
+ # Calls {AbstractEventFuture#touch}.
550
+
551
+ # @!macro promises.method.wait
552
+ # Wait (block the Thread) until receiver is {#resolved?}.
553
+ # @!macro promises.touches
554
+ #
555
+ # @!macro promises.warn.blocks
556
+ # @!macro promises.param.timeout
557
+ # @return [Future, true, false] self implies timeout was not used, true implies timeout was used
558
+ # and it was resolved, false implies it was not resolved within timeout.
559
+ def wait(timeout = nil)
560
+ result = wait_until_resolved(timeout)
561
+ timeout ? result : self
562
+ end
563
+
564
+ # Returns default executor.
565
+ # @return [Executor] default executor
566
+ # @see #with_default_executor
567
+ # @see FactoryMethods#future_on
568
+ # @see FactoryMethods#resolvable_future
569
+ # @see FactoryMethods#any_fulfilled_future_on
570
+ # @see similar
571
+ def default_executor
572
+ @DefaultExecutor
573
+ end
574
+
575
+ # @!macro promises.shortcut.on
576
+ # @return [Future]
577
+ def chain(*args, &task)
578
+ chain_on @DefaultExecutor, *args, &task
579
+ end
580
+
581
+ # Chains the task to be executed asynchronously on executor after it is resolved.
582
+ #
583
+ # @!macro promises.param.executor
584
+ # @!macro promises.param.args
585
+ # @return [Future]
586
+ # @!macro promise.param.task-future
587
+ #
588
+ # @overload an_event.chain_on(executor, *args, &task)
589
+ # @yield [*args] to the task.
590
+ # @overload a_future.chain_on(executor, *args, &task)
591
+ # @yield [fulfilled, value, reason, *args] to the task.
592
+ # @yieldparam [true, false] fulfilled
593
+ # @yieldparam [Object] value
594
+ # @yieldparam [Exception] reason
595
+ def chain_on(executor, *args, &task)
596
+ ChainPromise.new_blocked_by1(self, @DefaultExecutor, executor, args, &task).future
597
+ end
598
+
599
+ # @return [String] Short string representation.
600
+ def to_s
601
+ format '%s %s>', super[0..-2], state
602
+ end
603
+
604
+ alias_method :inspect, :to_s
605
+
606
+ # Resolves the resolvable when receiver is resolved.
607
+ #
608
+ # @param [Resolvable] resolvable
609
+ # @return [self]
610
+ def chain_resolvable(resolvable)
611
+ on_resolution! { resolvable.resolve_with internal_state }
612
+ end
613
+
614
+ alias_method :tangle, :chain_resolvable
615
+
616
+ # @!macro promises.shortcut.using
617
+ # @return [self]
618
+ def on_resolution(*args, &callback)
619
+ on_resolution_using @DefaultExecutor, *args, &callback
620
+ end
621
+
622
+ # Stores the callback to be executed synchronously on resolving thread after it is
623
+ # resolved.
624
+ #
625
+ # @!macro promises.param.args
626
+ # @!macro promise.param.callback
627
+ # @return [self]
628
+ #
629
+ # @overload an_event.on_resolution!(*args, &callback)
630
+ # @yield [*args] to the callback.
631
+ # @overload a_future.on_resolution!(*args, &callback)
632
+ # @yield [fulfilled, value, reason, *args] to the callback.
633
+ # @yieldparam [true, false] fulfilled
634
+ # @yieldparam [Object] value
635
+ # @yieldparam [Exception] reason
636
+ def on_resolution!(*args, &callback)
637
+ add_callback :callback_on_resolution, args, callback
638
+ end
639
+
640
+ # Stores the callback to be executed asynchronously on executor after it is resolved.
641
+ #
642
+ # @!macro promises.param.executor
643
+ # @!macro promises.param.args
644
+ # @!macro promise.param.callback
645
+ # @return [self]
646
+ #
647
+ # @overload an_event.on_resolution_using(executor, *args, &callback)
648
+ # @yield [*args] to the callback.
649
+ # @overload a_future.on_resolution_using(executor, *args, &callback)
650
+ # @yield [fulfilled, value, reason, *args] to the callback.
651
+ # @yieldparam [true, false] fulfilled
652
+ # @yieldparam [Object] value
653
+ # @yieldparam [Exception] reason
654
+ def on_resolution_using(executor, *args, &callback)
655
+ add_callback :async_callback_on_resolution, executor, args, callback
656
+ end
657
+
658
+ # @!macro promises.method.with_default_executor
659
+ # Crates new object with same class with the executor set as its new default executor.
660
+ # Any futures depending on it will use the new default executor.
661
+ # @!macro promises.shortcut.event-future
662
+ # @abstract
663
+ # @return [AbstractEventFuture]
664
+ def with_default_executor(executor)
665
+ raise NotImplementedError
666
+ end
667
+
668
+ # @!visibility private
669
+ def resolve_with(state, raise_on_reassign = true)
670
+ if compare_and_set_internal_state(PENDING, state)
671
+ # go to synchronized block only if there were waiting threads
672
+ @Lock.synchronize { @Condition.broadcast } unless @Waiters.value == 0
673
+ call_callbacks state
674
+ else
675
+ return rejected_resolution(raise_on_reassign, state)
676
+ end
677
+ self
678
+ end
679
+
680
+ # For inspection.
681
+ # @!visibility private
682
+ # @return [Array<AbstractPromise>]
683
+ def blocks
684
+ @Callbacks.each_with_object([]) do |(method, args), promises|
685
+ promises.push(args[0]) if method == :callback_notify_blocked
686
+ end
687
+ end
688
+
689
+ # For inspection.
690
+ # @!visibility private
691
+ def callbacks
692
+ @Callbacks.each.to_a
693
+ end
694
+
695
+ # For inspection.
696
+ # @!visibility private
697
+ def promise
698
+ @Promise
699
+ end
700
+
701
+ # For inspection.
702
+ # @!visibility private
703
+ def touched?
704
+ promise.touched?
705
+ end
706
+
707
+ # For inspection.
708
+ # @!visibility private
709
+ def waiting_threads
710
+ @Waiters.each.to_a
711
+ end
712
+
713
+ # @!visibility private
714
+ def add_callback_notify_blocked(promise, index)
715
+ add_callback :callback_notify_blocked, promise, index
716
+ end
717
+
718
+ # @!visibility private
719
+ def add_callback_clear_delayed_node(node)
720
+ add_callback(:callback_clear_delayed_node, node)
721
+ end
722
+
723
+ private
724
+
725
+ def add_callback(method, *args)
726
+ state = internal_state
727
+ if state.resolved?
728
+ call_callback method, state, args
729
+ else
730
+ @Callbacks.push [method, args]
731
+ state = internal_state
732
+ # take back if it was resolved in the meanwhile
733
+ call_callbacks state if state.resolved?
734
+ end
735
+ self
736
+ end
737
+
738
+ def callback_clear_delayed_node(state, node)
739
+ node.value = nil
740
+ end
741
+
742
+ # @return [Boolean]
743
+ def wait_until_resolved(timeout)
744
+ return true if resolved?
745
+
746
+ touch
747
+
748
+ @Lock.synchronize do
749
+ @Waiters.increment
750
+ begin
751
+ unless resolved?
752
+ @Condition.wait @Lock, timeout
753
+ end
754
+ ensure
755
+ # JRuby may raise ConcurrencyError
756
+ @Waiters.decrement
757
+ end
758
+ end
759
+ resolved?
760
+ end
761
+
762
+ def call_callback(method, state, args)
763
+ self.send method, state, *args
764
+ end
765
+
766
+ def call_callbacks(state)
767
+ method, args = @Callbacks.pop
768
+ while method
769
+ call_callback method, state, args
770
+ method, args = @Callbacks.pop
771
+ end
772
+ end
773
+
774
+ def with_async(executor, *args, &block)
775
+ Concurrent.executor(executor).post(*args, &block)
776
+ end
777
+
778
+ def async_callback_on_resolution(state, executor, args, callback)
779
+ with_async(executor, state, args, callback) do |st, ar, cb|
780
+ callback_on_resolution st, ar, cb
781
+ end
782
+ end
783
+
784
+ def callback_notify_blocked(state, promise, index)
785
+ promise.on_blocker_resolution self, index
786
+ end
787
+ end
788
+
789
+ # Represents an event which will happen in future (will be resolved). The event is either
790
+ # pending or resolved. It should be always resolved. Use {Future} to communicate rejections and
791
+ # cancellation.
792
+ class Event < AbstractEventFuture
793
+
794
+ alias_method :then, :chain
795
+
796
+
797
+ # @!macro promises.method.zip
798
+ # Creates a new event or a future which will be resolved when receiver and other are.
799
+ # Returns an event if receiver and other are events, otherwise returns a future.
800
+ # If just one of the parties is Future then the result
801
+ # of the returned future is equal to the result of the supplied future. If both are futures
802
+ # then the result is as described in {FactoryMethods#zip_futures_on}.
803
+ #
804
+ # @return [Future, Event]
805
+ def zip(other)
806
+ if other.is_a?(Future)
807
+ ZipFutureEventPromise.new_blocked_by2(other, self, @DefaultExecutor).future
808
+ else
809
+ ZipEventEventPromise.new_blocked_by2(self, other, @DefaultExecutor).event
810
+ end
811
+ end
812
+
813
+ alias_method :&, :zip
814
+
815
+ # Creates a new event which will be resolved when the first of receiver, `event_or_future`
816
+ # resolves.
817
+ #
818
+ # @return [Event]
819
+ def any(event_or_future)
820
+ AnyResolvedEventPromise.new_blocked_by2(self, event_or_future, @DefaultExecutor).event
821
+ end
822
+
823
+ alias_method :|, :any
824
+
825
+ # Creates new event dependent on receiver which will not evaluate until touched, see {#touch}.
826
+ # In other words, it inserts delay into the chain of Futures making rest of it lazy evaluated.
827
+ #
828
+ # @return [Event]
829
+ def delay
830
+ event = DelayPromise.new(@DefaultExecutor).event
831
+ ZipEventEventPromise.new_blocked_by2(self, event, @DefaultExecutor).event
832
+ end
833
+
834
+ # @!macro promise.method.schedule
835
+ # Creates new event dependent on receiver scheduled to execute on/in intended_time.
836
+ # In time is interpreted from the moment the receiver is resolved, therefore it inserts
837
+ # delay into the chain.
838
+ #
839
+ # @!macro promises.param.intended_time
840
+ # @return [Event]
841
+ def schedule(intended_time)
842
+ chain do
843
+ event = ScheduledPromise.new(@DefaultExecutor, intended_time).event
844
+ ZipEventEventPromise.new_blocked_by2(self, event, @DefaultExecutor).event
845
+ end.flat_event
846
+ end
847
+
848
+ # Converts event to a future. The future is fulfilled when the event is resolved, the future may never fail.
849
+ #
850
+ # @return [Future]
851
+ def to_future
852
+ future = Promises.resolvable_future
853
+ ensure
854
+ chain_resolvable(future)
855
+ end
856
+
857
+ # Returns self, since this is event
858
+ # @return [Event]
859
+ def to_event
860
+ self
861
+ end
862
+
863
+ # @!macro promises.method.with_default_executor
864
+ # @return [Event]
865
+ def with_default_executor(executor)
866
+ EventWrapperPromise.new_blocked_by1(self, executor).event
867
+ end
868
+
869
+ private
870
+
871
+ def rejected_resolution(raise_on_reassign, state)
872
+ Concurrent::MultipleAssignmentError.new('Event can be resolved only once') if raise_on_reassign
873
+ return false
874
+ end
875
+
876
+ def callback_on_resolution(state, args, callback)
877
+ callback.call(*args)
878
+ end
879
+ end
880
+
881
+ # Represents a value which will become available in future. May reject with a reason instead,
882
+ # e.g. when the tasks raises an exception.
883
+ class Future < AbstractEventFuture
884
+
885
+ # Is it in fulfilled state?
886
+ # @return [Boolean]
887
+ def fulfilled?
888
+ state = internal_state
889
+ state.resolved? && state.fulfilled?
890
+ end
891
+
892
+ # Is it in rejected state?
893
+ # @return [Boolean]
894
+ def rejected?
895
+ state = internal_state
896
+ state.resolved? && !state.fulfilled?
897
+ end
898
+
899
+ # @!macro promises.warn.nil
900
+ # @note Make sure returned `nil` is not confused with timeout, no value when rejected,
901
+ # no reason when fulfilled, etc.
902
+ # Use more exact methods if needed, like {#wait}, {#value!}, {#result}, etc.
903
+
904
+ # @!macro promises.method.value
905
+ # Return value of the future.
906
+ # @!macro promises.touches
907
+ #
908
+ # @!macro promises.warn.blocks
909
+ # @!macro promises.warn.nil
910
+ # @!macro promises.param.timeout
911
+ # @return [Object, nil] the value of the Future when fulfilled, nil on timeout or rejection.
912
+ def value(timeout = nil)
913
+ internal_state.value if wait_until_resolved timeout
914
+ end
915
+
916
+ # Returns reason of future's rejection.
917
+ # @!macro promises.touches
918
+ #
919
+ # @!macro promises.warn.blocks
920
+ # @!macro promises.warn.nil
921
+ # @!macro promises.param.timeout
922
+ # @return [Exception, nil] nil on timeout or fulfillment.
923
+ def reason(timeout = nil)
924
+ internal_state.reason if wait_until_resolved timeout
925
+ end
926
+
927
+ # Returns triplet fulfilled?, value, reason.
928
+ # @!macro promises.touches
929
+ #
930
+ # @!macro promises.warn.blocks
931
+ # @!macro promises.param.timeout
932
+ # @return [Array(Boolean, Object, Exception), nil] triplet of fulfilled?, value, reason, or nil
933
+ # on timeout.
934
+ def result(timeout = nil)
935
+ internal_state.result if wait_until_resolved timeout
936
+ end
937
+
938
+ # @!macro promises.method.wait
939
+ # @raise [Exception] {#reason} on rejection
940
+ def wait!(timeout = nil)
941
+ result = wait_until_resolved!(timeout)
942
+ timeout ? result : self
943
+ end
944
+
945
+ # @!macro promises.method.value
946
+ # @return [Object, nil] the value of the Future when fulfilled, nil on timeout.
947
+ # @raise [Exception] {#reason} on rejection
948
+ def value!(timeout = nil)
949
+ internal_state.value if wait_until_resolved! timeout
950
+ end
951
+
952
+ # Allows rejected Future to be risen with `raise` method.
953
+ # @example
954
+ # raise Promises.rejected_future(StandardError.new("boom"))
955
+ # @raise [StandardError] when raising not rejected future
956
+ # @return [Exception]
957
+ def exception(*args)
958
+ raise Concurrent::Error, 'it is not rejected' unless rejected?
959
+ reason = Array(internal_state.reason).flatten.compact
960
+ if reason.size > 1
961
+ ex = Concurrent::MultipleErrors.new reason
962
+ ex.set_backtrace(caller)
963
+ ex
964
+ else
965
+ ex = reason[0].clone.exception(*args)
966
+ ex.set_backtrace Array(ex.backtrace) + caller
967
+ ex
968
+ end
969
+ end
970
+
971
+ # @!macro promises.shortcut.on
972
+ # @return [Future]
973
+ def then(*args, &task)
974
+ then_on @DefaultExecutor, *args, &task
975
+ end
976
+
977
+ # Chains the task to be executed asynchronously on executor after it fulfills. Does not run
978
+ # the task if it rejects. It will resolve though, triggering any dependent futures.
979
+ #
980
+ # @!macro promises.param.executor
981
+ # @!macro promises.param.args
982
+ # @!macro promise.param.task-future
983
+ # @return [Future]
984
+ # @yield [value, *args] to the task.
985
+ def then_on(executor, *args, &task)
986
+ ThenPromise.new_blocked_by1(self, @DefaultExecutor, executor, args, &task).future
987
+ end
988
+
989
+ # @!macro promises.shortcut.on
990
+ # @return [Future]
991
+ def rescue(*args, &task)
992
+ rescue_on @DefaultExecutor, *args, &task
993
+ end
994
+
995
+ # Chains the task to be executed asynchronously on executor after it rejects. Does not run
996
+ # the task if it fulfills. It will resolve though, triggering any dependent futures.
997
+ #
998
+ # @!macro promises.param.executor
999
+ # @!macro promises.param.args
1000
+ # @!macro promise.param.task-future
1001
+ # @return [Future]
1002
+ # @yield [reason, *args] to the task.
1003
+ def rescue_on(executor, *args, &task)
1004
+ RescuePromise.new_blocked_by1(self, @DefaultExecutor, executor, args, &task).future
1005
+ end
1006
+
1007
+ # @!macro promises.method.zip
1008
+ # @return [Future]
1009
+ def zip(other)
1010
+ if other.is_a?(Future)
1011
+ ZipFuturesPromise.new_blocked_by2(self, other, @DefaultExecutor).future
1012
+ else
1013
+ ZipFutureEventPromise.new_blocked_by2(self, other, @DefaultExecutor).future
1014
+ end
1015
+ end
1016
+
1017
+ alias_method :&, :zip
1018
+
1019
+ # Creates a new event which will be resolved when the first of receiver, `event_or_future`
1020
+ # resolves. Returning future will have value nil if event_or_future is event and resolves
1021
+ # first.
1022
+ #
1023
+ # @return [Future]
1024
+ def any(event_or_future)
1025
+ AnyResolvedFuturePromise.new_blocked_by2(self, event_or_future, @DefaultExecutor).future
1026
+ end
1027
+
1028
+ alias_method :|, :any
1029
+
1030
+ # Creates new future dependent on receiver which will not evaluate until touched, see {#touch}.
1031
+ # In other words, it inserts delay into the chain of Futures making rest of it lazy evaluated.
1032
+ #
1033
+ # @return [Future]
1034
+ def delay
1035
+ event = DelayPromise.new(@DefaultExecutor).event
1036
+ ZipFutureEventPromise.new_blocked_by2(self, event, @DefaultExecutor).future
1037
+ end
1038
+
1039
+ # @!macro promise.method.schedule
1040
+ # @return [Future]
1041
+ def schedule(intended_time)
1042
+ chain do
1043
+ event = ScheduledPromise.new(@DefaultExecutor, intended_time).event
1044
+ ZipFutureEventPromise.new_blocked_by2(self, event, @DefaultExecutor).future
1045
+ end.flat
1046
+ end
1047
+
1048
+ # @!macro promises.method.with_default_executor
1049
+ # @return [Future]
1050
+ def with_default_executor(executor)
1051
+ FutureWrapperPromise.new_blocked_by1(self, executor).future
1052
+ end
1053
+
1054
+ # Creates new future which will have result of the future returned by receiver. If receiver
1055
+ # rejects it will have its rejection.
1056
+ #
1057
+ # @param [Integer] level how many levels of futures should flatten
1058
+ # @return [Future]
1059
+ def flat_future(level = 1)
1060
+ FlatFuturePromise.new_blocked_by1(self, level, @DefaultExecutor).future
1061
+ end
1062
+
1063
+ alias_method :flat, :flat_future
1064
+
1065
+ # Creates new event which will be resolved when the returned event by receiver is.
1066
+ # Be careful if the receiver rejects it will just resolve since Event does not hold reason.
1067
+ #
1068
+ # @return [Event]
1069
+ def flat_event
1070
+ FlatEventPromise.new_blocked_by1(self, @DefaultExecutor).event
1071
+ end
1072
+
1073
+ # @!macro promises.shortcut.using
1074
+ # @return [self]
1075
+ def on_fulfillment(*args, &callback)
1076
+ on_fulfillment_using @DefaultExecutor, *args, &callback
1077
+ end
1078
+
1079
+ # Stores the callback to be executed synchronously on resolving thread after it is
1080
+ # fulfilled. Does nothing on rejection.
1081
+ #
1082
+ # @!macro promises.param.args
1083
+ # @!macro promise.param.callback
1084
+ # @return [self]
1085
+ # @yield [value, *args] to the callback.
1086
+ def on_fulfillment!(*args, &callback)
1087
+ add_callback :callback_on_fulfillment, args, callback
1088
+ end
1089
+
1090
+ # Stores the callback to be executed asynchronously on executor after it is
1091
+ # fulfilled. Does nothing on rejection.
1092
+ #
1093
+ # @!macro promises.param.executor
1094
+ # @!macro promises.param.args
1095
+ # @!macro promise.param.callback
1096
+ # @return [self]
1097
+ # @yield [value, *args] to the callback.
1098
+ def on_fulfillment_using(executor, *args, &callback)
1099
+ add_callback :async_callback_on_fulfillment, executor, args, callback
1100
+ end
1101
+
1102
+ # @!macro promises.shortcut.using
1103
+ # @return [self]
1104
+ def on_rejection(*args, &callback)
1105
+ on_rejection_using @DefaultExecutor, *args, &callback
1106
+ end
1107
+
1108
+ # Stores the callback to be executed synchronously on resolving thread after it is
1109
+ # rejected. Does nothing on fulfillment.
1110
+ #
1111
+ # @!macro promises.param.args
1112
+ # @!macro promise.param.callback
1113
+ # @return [self]
1114
+ # @yield [reason, *args] to the callback.
1115
+ def on_rejection!(*args, &callback)
1116
+ add_callback :callback_on_rejection, args, callback
1117
+ end
1118
+
1119
+ # Stores the callback to be executed asynchronously on executor after it is
1120
+ # rejected. Does nothing on fulfillment.
1121
+ #
1122
+ # @!macro promises.param.executor
1123
+ # @!macro promises.param.args
1124
+ # @!macro promise.param.callback
1125
+ # @return [self]
1126
+ # @yield [reason, *args] to the callback.
1127
+ def on_rejection_using(executor, *args, &callback)
1128
+ add_callback :async_callback_on_rejection, executor, args, callback
1129
+ end
1130
+
1131
+ # Allows to use futures as green threads. The receiver has to evaluate to a future which
1132
+ # represents what should be done next. It basically flattens indefinitely until non Future
1133
+ # values is returned which becomes result of the returned future. Any encountered exception
1134
+ # will become reason of the returned future.
1135
+ #
1136
+ # @return [Future]
1137
+ # @example
1138
+ # body = lambda do |v|
1139
+ # v += 1
1140
+ # v < 5 ? Promises.future(v, &body) : v
1141
+ # end
1142
+ # Promises.future(0, &body).run.value! # => 5
1143
+ def run
1144
+ RunFuturePromise.new_blocked_by1(self, @DefaultExecutor).future
1145
+ end
1146
+
1147
+ # @!visibility private
1148
+ def apply(args, block)
1149
+ internal_state.apply args, block
1150
+ end
1151
+
1152
+ # Converts future to event which is resolved when future is resolved by fulfillment or rejection.
1153
+ #
1154
+ # @return [Event]
1155
+ def to_event
1156
+ event = Promises.resolvable_event
1157
+ ensure
1158
+ chain_resolvable(event)
1159
+ end
1160
+
1161
+ # Returns self, since this is a future
1162
+ # @return [Future]
1163
+ def to_future
1164
+ self
1165
+ end
1166
+
1167
+ private
1168
+
1169
+ def rejected_resolution(raise_on_reassign, state)
1170
+ if raise_on_reassign
1171
+ raise Concurrent::MultipleAssignmentError.new(
1172
+ "Future can be resolved only once. It's #{result}, trying to set #{state.result}.",
1173
+ current_result: result, new_result: state.result)
1174
+ end
1175
+ return false
1176
+ end
1177
+
1178
+ def wait_until_resolved!(timeout = nil)
1179
+ result = wait_until_resolved(timeout)
1180
+ raise self if rejected?
1181
+ result
1182
+ end
1183
+
1184
+ def async_callback_on_fulfillment(state, executor, args, callback)
1185
+ with_async(executor, state, args, callback) do |st, ar, cb|
1186
+ callback_on_fulfillment st, ar, cb
1187
+ end
1188
+ end
1189
+
1190
+ def async_callback_on_rejection(state, executor, args, callback)
1191
+ with_async(executor, state, args, callback) do |st, ar, cb|
1192
+ callback_on_rejection st, ar, cb
1193
+ end
1194
+ end
1195
+
1196
+ def callback_on_fulfillment(state, args, callback)
1197
+ state.apply args, callback if state.fulfilled?
1198
+ end
1199
+
1200
+ def callback_on_rejection(state, args, callback)
1201
+ state.apply args, callback unless state.fulfilled?
1202
+ end
1203
+
1204
+ def callback_on_resolution(state, args, callback)
1205
+ callback.call(*state.result, *args)
1206
+ end
1207
+
1208
+ end
1209
+
1210
+ # Marker module of Future, Event resolved manually by user.
1211
+ module Resolvable
1212
+ end
1213
+
1214
+ # A Event which can be resolved by user.
1215
+ class ResolvableEvent < Event
1216
+ include Resolvable
1217
+
1218
+
1219
+ # @!macro raise_on_reassign
1220
+ # @raise [MultipleAssignmentError] when already resolved and raise_on_reassign is true.
1221
+
1222
+ # @!macro promise.param.raise_on_reassign
1223
+ # @param [Boolean] raise_on_reassign should method raise exception if already resolved
1224
+ # @return [self, false] false is returner when raise_on_reassign is false and the receiver
1225
+ # is already resolved.
1226
+ #
1227
+
1228
+ # Makes the event resolved, which triggers all dependent futures.
1229
+ #
1230
+ # @!macro promise.param.raise_on_reassign
1231
+ def resolve(raise_on_reassign = true)
1232
+ resolve_with RESOLVED, raise_on_reassign
1233
+ end
1234
+
1235
+ # Creates new event wrapping receiver, effectively hiding the resolve method.
1236
+ #
1237
+ # @return [Event]
1238
+ def with_hidden_resolvable
1239
+ @with_hidden_resolvable ||= EventWrapperPromise.new_blocked_by1(self, @DefaultExecutor).event
1240
+ end
1241
+ end
1242
+
1243
+ # A Future which can be resolved by user.
1244
+ class ResolvableFuture < Future
1245
+ include Resolvable
1246
+
1247
+ # Makes the future resolved with result of triplet `fulfilled?`, `value`, `reason`,
1248
+ # which triggers all dependent futures.
1249
+ #
1250
+ # @!macro promise.param.raise_on_reassign
1251
+ def resolve(fulfilled = true, value = nil, reason = nil, raise_on_reassign = true)
1252
+ resolve_with(fulfilled ? Fulfilled.new(value) : Rejected.new(reason), raise_on_reassign)
1253
+ end
1254
+
1255
+ # Makes the future fulfilled with `value`,
1256
+ # which triggers all dependent futures.
1257
+ #
1258
+ # @!macro promise.param.raise_on_reassign
1259
+ def fulfill(value, raise_on_reassign = true)
1260
+ promise.fulfill(value, raise_on_reassign)
1261
+ end
1262
+
1263
+ # Makes the future rejected with `reason`,
1264
+ # which triggers all dependent futures.
1265
+ #
1266
+ # @!macro promise.param.raise_on_reassign
1267
+ def reject(reason, raise_on_reassign = true)
1268
+ promise.reject(reason, raise_on_reassign)
1269
+ end
1270
+
1271
+ # Evaluates the block and sets its result as future's value fulfilling, if the block raises
1272
+ # an exception the future rejects with it.
1273
+ # @yield [*args] to the block.
1274
+ # @yieldreturn [Object] value
1275
+ # @return [self]
1276
+ def evaluate_to(*args, &block)
1277
+ promise.evaluate_to(*args, block)
1278
+ end
1279
+
1280
+ # Evaluates the block and sets its result as future's value fulfilling, if the block raises
1281
+ # an exception the future rejects with it.
1282
+ # @yield [*args] to the block.
1283
+ # @yieldreturn [Object] value
1284
+ # @return [self]
1285
+ # @raise [Exception] also raise reason on rejection.
1286
+ def evaluate_to!(*args, &block)
1287
+ promise.evaluate_to(*args, block).wait!
1288
+ end
1289
+
1290
+ # Creates new future wrapping receiver, effectively hiding the resolve method and similar.
1291
+ #
1292
+ # @return [Future]
1293
+ def with_hidden_resolvable
1294
+ @with_hidden_resolvable ||= FutureWrapperPromise.new_blocked_by1(self, @DefaultExecutor).future
1295
+ end
1296
+ end
1297
+
1298
+ # @abstract
1299
+ # @private
1300
+ class AbstractPromise < Synchronization::Object
1301
+ safe_initialization!
1302
+ include InternalStates
1303
+
1304
+ def initialize(future)
1305
+ super()
1306
+ @Future = future
1307
+ end
1308
+
1309
+ def future
1310
+ @Future
1311
+ end
1312
+
1313
+ alias_method :event, :future
1314
+
1315
+ def default_executor
1316
+ future.default_executor
1317
+ end
1318
+
1319
+ def state
1320
+ future.state
1321
+ end
1322
+
1323
+ def touch
1324
+ end
1325
+
1326
+ def to_s
1327
+ format '%s %s>', super[0..-2], @Future
1328
+ end
1329
+
1330
+ alias_method :inspect, :to_s
1331
+
1332
+ def delayed_because
1333
+ nil
1334
+ end
1335
+
1336
+ private
1337
+
1338
+ def resolve_with(new_state, raise_on_reassign = true)
1339
+ @Future.resolve_with(new_state, raise_on_reassign)
1340
+ end
1341
+
1342
+ # @return [Future]
1343
+ def evaluate_to(*args, block)
1344
+ resolve_with Fulfilled.new(block.call(*args))
1345
+ rescue Exception => error
1346
+ resolve_with Rejected.new(error)
1347
+ raise error unless error.is_a?(StandardError)
1348
+ end
1349
+ end
1350
+
1351
+ class ResolvableEventPromise < AbstractPromise
1352
+ def initialize(default_executor)
1353
+ super ResolvableEvent.new(self, default_executor)
1354
+ end
1355
+ end
1356
+
1357
+ class ResolvableFuturePromise < AbstractPromise
1358
+ def initialize(default_executor)
1359
+ super ResolvableFuture.new(self, default_executor)
1360
+ end
1361
+
1362
+ def fulfill(value, raise_on_reassign)
1363
+ resolve_with Fulfilled.new(value), raise_on_reassign
1364
+ end
1365
+
1366
+ def reject(reason, raise_on_reassign)
1367
+ resolve_with Rejected.new(reason), raise_on_reassign
1368
+ end
1369
+
1370
+ public :evaluate_to
1371
+ end
1372
+
1373
+ # @abstract
1374
+ class InnerPromise < AbstractPromise
1375
+ end
1376
+
1377
+ # @abstract
1378
+ class BlockedPromise < InnerPromise
1379
+
1380
+ private_class_method :new
1381
+
1382
+ def self.new_blocked_by1(blocker, *args, &block)
1383
+ blocker_delayed = blocker.promise.delayed_because
1384
+ promise = new(blocker_delayed, 1, *args, &block)
1385
+ blocker.add_callback_notify_blocked promise, 0
1386
+ promise
1387
+ end
1388
+
1389
+ def self.new_blocked_by2(blocker1, blocker2, *args, &block)
1390
+ blocker_delayed1 = blocker1.promise.delayed_because
1391
+ blocker_delayed2 = blocker2.promise.delayed_because
1392
+ delayed = if blocker_delayed1 && blocker_delayed2
1393
+ # TODO (pitr-ch 23-Dec-2016): use arrays when we know it will not grow (only flat adds delay)
1394
+ LockFreeStack.of2(blocker_delayed1, blocker_delayed2)
1395
+ else
1396
+ blocker_delayed1 || blocker_delayed2
1397
+ end
1398
+ promise = new(delayed, 2, *args, &block)
1399
+ blocker1.add_callback_notify_blocked promise, 0
1400
+ blocker2.add_callback_notify_blocked promise, 1
1401
+ promise
1402
+ end
1403
+
1404
+ def self.new_blocked_by(blockers, *args, &block)
1405
+ delayed = blockers.reduce(nil) { |d, f| add_delayed d, f.promise.delayed_because }
1406
+ promise = new(delayed, blockers.size, *args, &block)
1407
+ blockers.each_with_index { |f, i| f.add_callback_notify_blocked promise, i }
1408
+ promise
1409
+ end
1410
+
1411
+ def self.add_delayed(delayed1, delayed2)
1412
+ if delayed1 && delayed2
1413
+ delayed1.push delayed2
1414
+ delayed1
1415
+ else
1416
+ delayed1 || delayed2
1417
+ end
1418
+ end
1419
+
1420
+ def initialize(delayed, blockers_count, future)
1421
+ super(future)
1422
+ @Delayed = delayed
1423
+ # noinspection RubyArgCount
1424
+ @Countdown = AtomicFixnum.new blockers_count
1425
+ end
1426
+
1427
+ def on_blocker_resolution(future, index)
1428
+ countdown = process_on_blocker_resolution(future, index)
1429
+ resolvable = resolvable?(countdown, future, index)
1430
+
1431
+ on_resolvable(future, index) if resolvable
1432
+ end
1433
+
1434
+ def delayed_because
1435
+ @Delayed
1436
+ end
1437
+
1438
+ def touch
1439
+ clear_and_propagate_touch
1440
+ end
1441
+
1442
+ # for inspection only
1443
+ def blocked_by
1444
+ blocked_by = []
1445
+ ObjectSpace.each_object(AbstractEventFuture) { |o| blocked_by.push o if o.blocks.include? self }
1446
+ blocked_by
1447
+ end
1448
+
1449
+ private
1450
+
1451
+ def clear_and_propagate_touch(stack_or_element = @Delayed)
1452
+ return if stack_or_element.nil?
1453
+
1454
+ if stack_or_element.is_a? LockFreeStack
1455
+ stack_or_element.clear_each { |element| clear_and_propagate_touch element }
1456
+ else
1457
+ stack_or_element.touch unless stack_or_element.nil? # if still present
1458
+ end
1459
+ end
1460
+
1461
+ # @return [true,false] if resolvable
1462
+ def resolvable?(countdown, future, index)
1463
+ countdown.zero?
1464
+ end
1465
+
1466
+ def process_on_blocker_resolution(future, index)
1467
+ @Countdown.decrement
1468
+ end
1469
+
1470
+ def on_resolvable(resolved_future, index)
1471
+ raise NotImplementedError
1472
+ end
1473
+ end
1474
+
1475
+ # @abstract
1476
+ class BlockedTaskPromise < BlockedPromise
1477
+ def initialize(delayed, blockers_count, default_executor, executor, args, &task)
1478
+ raise ArgumentError, 'no block given' unless block_given?
1479
+ super delayed, 1, Future.new(self, default_executor)
1480
+ @Executor = executor
1481
+ @Task = task
1482
+ @Args = args
1483
+ end
1484
+
1485
+ def executor
1486
+ @Executor
1487
+ end
1488
+ end
1489
+
1490
+ class ThenPromise < BlockedTaskPromise
1491
+ private
1492
+
1493
+ def initialize(delayed, blockers_count, default_executor, executor, args, &task)
1494
+ super delayed, blockers_count, default_executor, executor, args, &task
1495
+ end
1496
+
1497
+ def on_resolvable(resolved_future, index)
1498
+ if resolved_future.fulfilled?
1499
+ Concurrent.executor(@Executor).post(resolved_future, @Args, @Task) do |future, args, task|
1500
+ evaluate_to lambda { future.apply args, task }
1501
+ end
1502
+ else
1503
+ resolve_with resolved_future.internal_state
1504
+ end
1505
+ end
1506
+ end
1507
+
1508
+ class RescuePromise < BlockedTaskPromise
1509
+ private
1510
+
1511
+ def initialize(delayed, blockers_count, default_executor, executor, args, &task)
1512
+ super delayed, blockers_count, default_executor, executor, args, &task
1513
+ end
1514
+
1515
+ def on_resolvable(resolved_future, index)
1516
+ if resolved_future.rejected?
1517
+ Concurrent.executor(@Executor).post(resolved_future, @Args, @Task) do |future, args, task|
1518
+ evaluate_to lambda { future.apply args, task }
1519
+ end
1520
+ else
1521
+ resolve_with resolved_future.internal_state
1522
+ end
1523
+ end
1524
+ end
1525
+
1526
+ class ChainPromise < BlockedTaskPromise
1527
+ private
1528
+
1529
+ def on_resolvable(resolved_future, index)
1530
+ if Future === resolved_future
1531
+ Concurrent.executor(@Executor).post(resolved_future, @Args, @Task) do |future, args, task|
1532
+ evaluate_to(*future.result, *args, task)
1533
+ end
1534
+ else
1535
+ Concurrent.executor(@Executor).post(@Args, @Task) do |args, task|
1536
+ evaluate_to(*args, task)
1537
+ end
1538
+ end
1539
+ end
1540
+ end
1541
+
1542
+ # will be immediately resolved
1543
+ class ImmediateEventPromise < InnerPromise
1544
+ def initialize(default_executor)
1545
+ super Event.new(self, default_executor).resolve_with(RESOLVED)
1546
+ end
1547
+ end
1548
+
1549
+ class ImmediateFuturePromise < InnerPromise
1550
+ def initialize(default_executor, fulfilled, value, reason)
1551
+ super Future.new(self, default_executor).
1552
+ resolve_with(fulfilled ? Fulfilled.new(value) : Rejected.new(reason))
1553
+ end
1554
+ end
1555
+
1556
+ class AbstractFlatPromise < BlockedPromise
1557
+
1558
+ def initialize(delayed_because, blockers_count, event_or_future)
1559
+ delayed = LockFreeStack.of1(self)
1560
+ super(delayed, blockers_count, event_or_future)
1561
+ # noinspection RubyArgCount
1562
+ @Touched = AtomicBoolean.new false
1563
+ @DelayedBecause = delayed_because || LockFreeStack.new
1564
+
1565
+ event_or_future.add_callback_clear_delayed_node delayed.peek
1566
+ end
1567
+
1568
+ def touch
1569
+ if @Touched.make_true
1570
+ clear_and_propagate_touch @DelayedBecause
1571
+ end
1572
+ end
1573
+
1574
+ private
1575
+
1576
+ def touched?
1577
+ @Touched.value
1578
+ end
1579
+
1580
+ def on_resolvable(resolved_future, index)
1581
+ resolve_with resolved_future.internal_state
1582
+ end
1583
+
1584
+ def resolvable?(countdown, future, index)
1585
+ !@Future.internal_state.resolved? && super(countdown, future, index)
1586
+ end
1587
+
1588
+ def add_delayed_of(future)
1589
+ delayed = future.promise.delayed_because
1590
+ if touched?
1591
+ clear_and_propagate_touch delayed
1592
+ else
1593
+ BlockedPromise.add_delayed @DelayedBecause, delayed
1594
+ clear_and_propagate_touch @DelayedBecause if touched?
1595
+ end
1596
+ end
1597
+
1598
+ end
1599
+
1600
+ class FlatEventPromise < AbstractFlatPromise
1601
+
1602
+ private
1603
+
1604
+ def initialize(delayed, blockers_count, default_executor)
1605
+ super delayed, 2, Event.new(self, default_executor)
1606
+ end
1607
+
1608
+ def process_on_blocker_resolution(future, index)
1609
+ countdown = super(future, index)
1610
+ if countdown.nonzero?
1611
+ internal_state = future.internal_state
1612
+
1613
+ unless internal_state.fulfilled?
1614
+ resolve_with RESOLVED
1615
+ return countdown
1616
+ end
1617
+
1618
+ value = internal_state.value
1619
+ case value
1620
+ when Future, Event
1621
+ add_delayed_of value
1622
+ value.add_callback_notify_blocked self, nil
1623
+ countdown
1624
+ else
1625
+ resolve_with RESOLVED
1626
+ end
1627
+ end
1628
+ countdown
1629
+ end
1630
+
1631
+ end
1632
+
1633
+ class FlatFuturePromise < AbstractFlatPromise
1634
+
1635
+ private
1636
+
1637
+ def initialize(delayed, blockers_count, levels, default_executor)
1638
+ raise ArgumentError, 'levels has to be higher than 0' if levels < 1
1639
+ # flat promise may result to a future having delayed futures, therefore we have to have empty stack
1640
+ # to be able to add new delayed futures
1641
+ super delayed || LockFreeStack.new, 1 + levels, Future.new(self, default_executor)
1642
+ end
1643
+
1644
+ def process_on_blocker_resolution(future, index)
1645
+ countdown = super(future, index)
1646
+ if countdown.nonzero?
1647
+ internal_state = future.internal_state
1648
+
1649
+ unless internal_state.fulfilled?
1650
+ resolve_with internal_state
1651
+ return countdown
1652
+ end
1653
+
1654
+ value = internal_state.value
1655
+ case value
1656
+ when Future
1657
+ add_delayed_of value
1658
+ value.add_callback_notify_blocked self, nil
1659
+ countdown
1660
+ when Event
1661
+ evaluate_to(lambda { raise TypeError, 'cannot flatten to Event' })
1662
+ else
1663
+ evaluate_to(lambda { raise TypeError, "returned value #{value.inspect} is not a Future" })
1664
+ end
1665
+ end
1666
+ countdown
1667
+ end
1668
+
1669
+ end
1670
+
1671
+ class RunFuturePromise < AbstractFlatPromise
1672
+
1673
+ private
1674
+
1675
+ def initialize(delayed, blockers_count, default_executor)
1676
+ super delayed, 1, Future.new(self, default_executor)
1677
+ end
1678
+
1679
+ def process_on_blocker_resolution(future, index)
1680
+ internal_state = future.internal_state
1681
+
1682
+ unless internal_state.fulfilled?
1683
+ resolve_with internal_state
1684
+ return 0
1685
+ end
1686
+
1687
+ value = internal_state.value
1688
+ case value
1689
+ when Future
1690
+ add_delayed_of value
1691
+ value.add_callback_notify_blocked self, nil
1692
+ else
1693
+ resolve_with internal_state
1694
+ end
1695
+
1696
+ 1
1697
+ end
1698
+ end
1699
+
1700
+ class ZipEventEventPromise < BlockedPromise
1701
+ def initialize(delayed, blockers_count, default_executor)
1702
+ super delayed, 2, Event.new(self, default_executor)
1703
+ end
1704
+
1705
+ private
1706
+
1707
+ def on_resolvable(resolved_future, index)
1708
+ resolve_with RESOLVED
1709
+ end
1710
+ end
1711
+
1712
+ class ZipFutureEventPromise < BlockedPromise
1713
+ def initialize(delayed, blockers_count, default_executor)
1714
+ super delayed, 2, Future.new(self, default_executor)
1715
+ @result = nil
1716
+ end
1717
+
1718
+ private
1719
+
1720
+ def process_on_blocker_resolution(future, index)
1721
+ # first blocking is future, take its result
1722
+ @result = future.internal_state if index == 0
1723
+ # super has to be called after above to piggyback on volatile @Countdown
1724
+ super future, index
1725
+ end
1726
+
1727
+ def on_resolvable(resolved_future, index)
1728
+ resolve_with @result
1729
+ end
1730
+ end
1731
+
1732
+ class EventWrapperPromise < BlockedPromise
1733
+ def initialize(delayed, blockers_count, default_executor)
1734
+ super delayed, 1, Event.new(self, default_executor)
1735
+ end
1736
+
1737
+ private
1738
+
1739
+ def on_resolvable(resolved_future, index)
1740
+ resolve_with RESOLVED
1741
+ end
1742
+ end
1743
+
1744
+ class FutureWrapperPromise < BlockedPromise
1745
+ def initialize(delayed, blockers_count, default_executor)
1746
+ super delayed, 1, Future.new(self, default_executor)
1747
+ end
1748
+
1749
+ private
1750
+
1751
+ def on_resolvable(resolved_future, index)
1752
+ resolve_with resolved_future.internal_state
1753
+ end
1754
+ end
1755
+
1756
+ class ZipFuturesPromise < BlockedPromise
1757
+
1758
+ private
1759
+
1760
+ def initialize(delayed, blockers_count, default_executor)
1761
+ super(delayed, blockers_count, Future.new(self, default_executor))
1762
+ @Resolutions = ::Array.new(blockers_count, nil)
1763
+
1764
+ on_resolvable nil, nil if blockers_count == 0
1765
+ end
1766
+
1767
+ def process_on_blocker_resolution(future, index)
1768
+ # TODO (pitr-ch 18-Dec-2016): Can we assume that array will never break under parallel access when never re-sized?
1769
+ @Resolutions[index] = future.internal_state # has to be set before countdown in super
1770
+ super future, index
1771
+ end
1772
+
1773
+ def on_resolvable(resolved_future, index)
1774
+ all_fulfilled = true
1775
+ values = ::Array.new(@Resolutions.size)
1776
+ reasons = ::Array.new(@Resolutions.size)
1777
+
1778
+ @Resolutions.each_with_index do |internal_state, i|
1779
+ fulfilled, values[i], reasons[i] = internal_state.result
1780
+ all_fulfilled &&= fulfilled
1781
+ end
1782
+
1783
+ if all_fulfilled
1784
+ resolve_with FulfilledArray.new(values)
1785
+ else
1786
+ resolve_with PartiallyRejected.new(values, reasons)
1787
+ end
1788
+ end
1789
+ end
1790
+
1791
+ class ZipEventsPromise < BlockedPromise
1792
+
1793
+ private
1794
+
1795
+ def initialize(delayed, blockers_count, default_executor)
1796
+ super delayed, blockers_count, Event.new(self, default_executor)
1797
+
1798
+ on_resolvable nil, nil if blockers_count == 0
1799
+ end
1800
+
1801
+ def on_resolvable(resolved_future, index)
1802
+ resolve_with RESOLVED
1803
+ end
1804
+ end
1805
+
1806
+ # @abstract
1807
+ class AbstractAnyPromise < BlockedPromise
1808
+ end
1809
+
1810
+ class AnyResolvedFuturePromise < AbstractAnyPromise
1811
+
1812
+ private
1813
+
1814
+ def initialize(delayed, blockers_count, default_executor)
1815
+ super delayed, blockers_count, Future.new(self, default_executor)
1816
+ end
1817
+
1818
+ def resolvable?(countdown, future, index)
1819
+ true
1820
+ end
1821
+
1822
+ def on_resolvable(resolved_future, index)
1823
+ resolve_with resolved_future.internal_state, false
1824
+ end
1825
+ end
1826
+
1827
+ class AnyResolvedEventPromise < AbstractAnyPromise
1828
+
1829
+ private
1830
+
1831
+ def initialize(delayed, blockers_count, default_executor)
1832
+ super delayed, blockers_count, Event.new(self, default_executor)
1833
+ end
1834
+
1835
+ def resolvable?(countdown, future, index)
1836
+ true
1837
+ end
1838
+
1839
+ def on_resolvable(resolved_future, index)
1840
+ resolve_with RESOLVED, false
1841
+ end
1842
+ end
1843
+
1844
+ class AnyFulfilledFuturePromise < AnyResolvedFuturePromise
1845
+
1846
+ private
1847
+
1848
+ def resolvable?(countdown, future, index)
1849
+ future.fulfilled? ||
1850
+ # inlined super from BlockedPromise
1851
+ countdown.zero?
1852
+ end
1853
+ end
1854
+
1855
+ class DelayPromise < InnerPromise
1856
+
1857
+ def initialize(default_executor)
1858
+ event = Event.new(self, default_executor)
1859
+ @Delayed = LockFreeStack.of1(self)
1860
+ super event
1861
+ event.add_callback_clear_delayed_node @Delayed.peek
1862
+ end
1863
+
1864
+ def touch
1865
+ @Future.resolve_with RESOLVED
1866
+ end
1867
+
1868
+ def delayed_because
1869
+ @Delayed
1870
+ end
1871
+
1872
+ end
1873
+
1874
+ class ScheduledPromise < InnerPromise
1875
+ def intended_time
1876
+ @IntendedTime
1877
+ end
1878
+
1879
+ def inspect
1880
+ "#{to_s[0..-2]} intended_time: #{@IntendedTime}>"
1881
+ end
1882
+
1883
+ private
1884
+
1885
+ def initialize(default_executor, intended_time)
1886
+ super Event.new(self, default_executor)
1887
+
1888
+ @IntendedTime = intended_time
1889
+
1890
+ in_seconds = begin
1891
+ now = Time.now
1892
+ schedule_time = if @IntendedTime.is_a? Time
1893
+ @IntendedTime
1894
+ else
1895
+ now + @IntendedTime
1896
+ end
1897
+ [0, schedule_time.to_f - now.to_f].max
1898
+ end
1899
+
1900
+ Concurrent.global_timer_set.post(in_seconds) do
1901
+ @Future.resolve_with RESOLVED
1902
+ end
1903
+ end
1904
+ end
1905
+
1906
+ extend FactoryMethods
1907
+
1908
+ private_constant :AbstractPromise,
1909
+ :ResolvableEventPromise,
1910
+ :ResolvableFuturePromise,
1911
+ :InnerPromise,
1912
+ :BlockedPromise,
1913
+ :BlockedTaskPromise,
1914
+ :ThenPromise,
1915
+ :RescuePromise,
1916
+ :ChainPromise,
1917
+ :ImmediateEventPromise,
1918
+ :ImmediateFuturePromise,
1919
+ :AbstractFlatPromise,
1920
+ :FlatFuturePromise,
1921
+ :FlatEventPromise,
1922
+ :RunFuturePromise,
1923
+ :ZipEventEventPromise,
1924
+ :ZipFutureEventPromise,
1925
+ :EventWrapperPromise,
1926
+ :FutureWrapperPromise,
1927
+ :ZipFuturesPromise,
1928
+ :ZipEventsPromise,
1929
+ :AbstractAnyPromise,
1930
+ :AnyResolvedFuturePromise,
1931
+ :AnyFulfilledFuturePromise,
1932
+ :AnyResolvedEventPromise,
1933
+ :DelayPromise,
1934
+ :ScheduledPromise
1935
+
1936
+
1937
+ end
1938
+ end