concurrent-ruby 1.0.5 → 1.1.0.pre1

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