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