concurrent-ruby-edge 0.3.1 → 0.4.0.pre1

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