concurrent-ruby-edge 0.1.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +284 -0
  4. data/lib/concurrent-edge.rb +11 -0
  5. data/lib/concurrent/actor.rb +98 -0
  6. data/lib/concurrent/actor/behaviour.rb +143 -0
  7. data/lib/concurrent/actor/behaviour/abstract.rb +51 -0
  8. data/lib/concurrent/actor/behaviour/awaits.rb +21 -0
  9. data/lib/concurrent/actor/behaviour/buffer.rb +56 -0
  10. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +12 -0
  11. data/lib/concurrent/actor/behaviour/executes_context.rb +17 -0
  12. data/lib/concurrent/actor/behaviour/linking.rb +83 -0
  13. data/lib/concurrent/actor/behaviour/pausing.rb +123 -0
  14. data/lib/concurrent/actor/behaviour/removes_child.rb +16 -0
  15. data/lib/concurrent/actor/behaviour/sets_results.rb +37 -0
  16. data/lib/concurrent/actor/behaviour/supervising.rb +39 -0
  17. data/lib/concurrent/actor/behaviour/terminates_children.rb +14 -0
  18. data/lib/concurrent/actor/behaviour/termination.rb +74 -0
  19. data/lib/concurrent/actor/context.rb +167 -0
  20. data/lib/concurrent/actor/core.rb +220 -0
  21. data/lib/concurrent/actor/default_dead_letter_handler.rb +9 -0
  22. data/lib/concurrent/actor/envelope.rb +41 -0
  23. data/lib/concurrent/actor/errors.rb +27 -0
  24. data/lib/concurrent/actor/internal_delegations.rb +59 -0
  25. data/lib/concurrent/actor/public_delegations.rb +40 -0
  26. data/lib/concurrent/actor/reference.rb +106 -0
  27. data/lib/concurrent/actor/root.rb +37 -0
  28. data/lib/concurrent/actor/type_check.rb +48 -0
  29. data/lib/concurrent/actor/utils.rb +10 -0
  30. data/lib/concurrent/actor/utils/ad_hoc.rb +27 -0
  31. data/lib/concurrent/actor/utils/balancer.rb +43 -0
  32. data/lib/concurrent/actor/utils/broadcast.rb +52 -0
  33. data/lib/concurrent/actor/utils/pool.rb +54 -0
  34. data/lib/concurrent/agent.rb +289 -0
  35. data/lib/concurrent/channel.rb +6 -0
  36. data/lib/concurrent/channel/blocking_ring_buffer.rb +82 -0
  37. data/lib/concurrent/channel/buffered_channel.rb +87 -0
  38. data/lib/concurrent/channel/channel.rb +19 -0
  39. data/lib/concurrent/channel/ring_buffer.rb +65 -0
  40. data/lib/concurrent/channel/unbuffered_channel.rb +39 -0
  41. data/lib/concurrent/channel/waitable_list.rb +48 -0
  42. data/lib/concurrent/edge/atomic_markable_reference.rb +184 -0
  43. data/lib/concurrent/edge/future.rb +1226 -0
  44. data/lib/concurrent/edge/lock_free_stack.rb +85 -0
  45. metadata +110 -0
@@ -0,0 +1,1226 @@
1
+ require 'concurrent' # TODO do not require whole concurrent gem
2
+ require 'concurrent/edge/lock_free_stack'
3
+
4
+
5
+ # @note different name just not to collide for now
6
+ module Concurrent
7
+ module Edge
8
+
9
+ # Provides edge features, which will be added to or replace features in main gem.
10
+ #
11
+ # Contains new unified implementation of Futures and Promises which combines Features of previous `Future`,
12
+ # `Promise`, `IVar`, `Event`, `Probe`, `dataflow`, `Delay`, `TimerTask` into single framework. It uses extensively
13
+ # new synchronization layer to make all the paths lock-free with exception of blocking threads on `#wait`.
14
+ # It offers better performance and does not block threads (exception being #wait and similar methods where it's
15
+ # intended).
16
+ #
17
+ # ## Examples
18
+ # {include:file:examples/edge_futures.out.rb}
19
+ #
20
+ # @!macro edge_warning
21
+ module FutureShortcuts
22
+ # User is responsible for completing the event once by {Edge::CompletableEvent#complete}
23
+ # @return [CompletableEvent]
24
+ def event(default_executor = :io)
25
+ CompletableEventPromise.new(default_executor).future
26
+ end
27
+
28
+ # @overload future(default_executor = :io, &task)
29
+ # Constructs new Future which will be completed after block is evaluated on executor. Evaluation begins immediately.
30
+ # @return [Future]
31
+ # @overload future(default_executor = :io)
32
+ # User is responsible for completing the future once by {Edge::CompletableFuture#success} or {Edge::CompletableFuture#fail}
33
+ # @return [CompletableFuture]
34
+ def future(*args, &task)
35
+ future_on :io, *args, &task
36
+ end
37
+
38
+ def future_on(default_executor, *args, &task)
39
+ if task
40
+ ImmediatePromise.new(default_executor, *args).future.then(&task)
41
+ else
42
+ CompletableFuturePromise.new(default_executor).future
43
+ end
44
+ end
45
+
46
+ alias_method :async, :future
47
+
48
+ # Constructs new Future which will evaluate to the block after
49
+ # requested by calling `#wait`, `#value`, `#value!`, etc. on it or on any of the chained futures.
50
+ # @return [Future]
51
+ def delay(*args, &task)
52
+ delay_on :io, *args, &task
53
+ end
54
+
55
+ # @return [Future]
56
+ def delay_on(default_executor, *args, &task)
57
+ Delay.new(default_executor, *args).future.then(&task)
58
+ end
59
+
60
+ # Schedules the block to be executed on executor in given intended_time.
61
+ # @param [Numeric, Time] intended_time Numeric => run in `intended_time` seconds. Time => eun on time.
62
+ # @return [Future]
63
+ def schedule(intended_time, *args, &task)
64
+ schedule_on :io, intended_time, *args, &task
65
+ end
66
+
67
+ # @return [Future]
68
+ def schedule_on(default_executor, intended_time, *args, &task)
69
+ ScheduledPromise.new(default_executor, intended_time, *args).future.then(&task)
70
+ end
71
+
72
+ # Constructs new {Future} which is completed after all futures are complete. Its value is array
73
+ # of dependent future values. If there is an error it fails with the first one.
74
+ # @param [Event] futures
75
+ # @return [Future]
76
+ def zip(*futures)
77
+ AllPromise.new(futures, :io).future
78
+ end
79
+
80
+ # Constructs new {Future} which is completed after first of the futures is complete.
81
+ # @param [Event] futures
82
+ # @return [Future]
83
+ def any(*futures)
84
+ AnyPromise.new(futures, :io).future
85
+ end
86
+
87
+ # only proof of concept
88
+ # @return [Future]
89
+ def select(*channels)
90
+ probe = future
91
+ channels.each { |ch| ch.select probe }
92
+ probe
93
+ end
94
+
95
+ # post job on :fast executor
96
+ # @return [true, false]
97
+ def post!(*args, &job)
98
+ post_on(:fast, *args, &job)
99
+ end
100
+
101
+ # post job on :io executor
102
+ # @return [true, false]
103
+ def post(*args, &job)
104
+ post_on(:io, *args, &job)
105
+ end
106
+
107
+ # post job on executor
108
+ # @return [true, false]
109
+ def post_on(executor, *args, &job)
110
+ Concurrent.executor(executor).post *args, &job
111
+ end
112
+
113
+ # TODO add first(futures, count=count)
114
+ # TODO allow to to have a zip point for many futures and process them in batches by 10
115
+ end
116
+
117
+ extend FutureShortcuts
118
+ include FutureShortcuts
119
+
120
+ # Represents an event which will happen in future (will be completed). It has to always happen.
121
+ class Event < Synchronization::Object
122
+ include Concern::Deprecation
123
+
124
+ class State
125
+ def completed?
126
+ raise NotImplementedError
127
+ end
128
+
129
+ def to_sym
130
+ raise NotImplementedError
131
+ end
132
+ end
133
+
134
+ class Pending < State
135
+ def completed?
136
+ false
137
+ end
138
+
139
+ def to_sym
140
+ :pending
141
+ end
142
+ end
143
+
144
+ class Completed < State
145
+ def completed?
146
+ true
147
+ end
148
+
149
+ def to_sym
150
+ :completed
151
+ end
152
+ end
153
+
154
+ PENDING = Pending.new
155
+ COMPLETED = Completed.new
156
+
157
+ def initialize(promise, default_executor)
158
+ @Promise = promise
159
+ @DefaultExecutor = default_executor
160
+ @Touched = AtomicBoolean.new(false)
161
+ @Callbacks = LockFreeStack.new
162
+ @Waiters = LockFreeStack.new
163
+ @State = AtomicReference.new PENDING
164
+ super()
165
+ ensure_ivar_visibility!
166
+ end
167
+
168
+ # @return [:pending, :completed]
169
+ def state
170
+ @State.get.to_sym
171
+ end
172
+
173
+ # Is Event/Future pending?
174
+ # @return [Boolean]
175
+ def pending?(state = @State.get)
176
+ !state.completed?
177
+ end
178
+
179
+ def unscheduled?
180
+ raise 'unsupported'
181
+ end
182
+
183
+ alias_method :incomplete?, :pending?
184
+
185
+ # Has the Event been completed?
186
+ # @return [Boolean]
187
+ def completed?(state = @State.get)
188
+ state.completed?
189
+ end
190
+
191
+ alias_method :complete?, :completed?
192
+
193
+ # Wait until Event is #complete?
194
+ # @param [Numeric] timeout the maximum time in second to wait.
195
+ # @return [Event] self
196
+ def wait(timeout = nil)
197
+ touch
198
+ wait_until_complete timeout
199
+ self
200
+ end
201
+
202
+ # @!visibility private
203
+ def touch
204
+ # distribute touch to promise only once
205
+ @Promise.touch if @Touched.make_true
206
+ self
207
+ end
208
+
209
+ # @return [Executor] current default executor
210
+ # @see #with_default_executor
211
+ def default_executor
212
+ @DefaultExecutor
213
+ end
214
+
215
+ # @yield [success, value, reason] of the parent
216
+ def chain(executor = nil, &callback)
217
+ ChainPromise.new(self, @DefaultExecutor, executor || @DefaultExecutor, &callback).future
218
+ end
219
+
220
+ alias_method :then, :chain
221
+
222
+ # TODO take block optionally
223
+ # Zip with futures producing new Future
224
+ # @return [Future]
225
+ def zip(*futures)
226
+ AllPromise.new([self, *futures], @DefaultExecutor).future
227
+ end
228
+
229
+ alias_method :&, :zip
230
+
231
+ # Inserts delay into the chain of Futures making rest of it lazy evaluated.
232
+ # @return [Future]
233
+ def delay
234
+ zip(Delay.new(@DefaultExecutor).future)
235
+ end
236
+
237
+ # Schedules rest of the chain for execution with specified time or on specified time
238
+ # @return [Future]
239
+ def schedule(intended_time)
240
+ chain { ScheduledPromise.new(@DefaultExecutor, intended_time).event.zip(self) }.flat
241
+ end
242
+
243
+ # Zips with selected value form the suplied channels
244
+ # @return [Future]
245
+ def then_select(*channels)
246
+ self.zip(Concurrent.select(*channels))
247
+ end
248
+
249
+ # @yield [success, value, reason] executed async on `executor` when completed
250
+ # @return self
251
+ def on_completion(executor = nil, &callback)
252
+ add_callback :pr_async_callback_on_completion, executor || @DefaultExecutor, callback
253
+ end
254
+
255
+ # @yield [success, value, reason] executed sync when completed
256
+ # @return self
257
+ def on_completion!(&callback)
258
+ add_callback :pr_callback_on_completion, callback
259
+ end
260
+
261
+ # Changes default executor for rest of the chain
262
+ # @return [Future]
263
+ def with_default_executor(executor)
264
+ AllPromise.new([self], executor).future
265
+ end
266
+
267
+ def to_s
268
+ "<##{self.class}:0x#{'%x' % (object_id << 1)} #{state.to_sym}>"
269
+ end
270
+
271
+ def inspect
272
+ "#{to_s[0..-2]} blocks:[#{blocks.map(&:to_s).join(', ')}]>"
273
+ end
274
+
275
+ def set(*args, &block)
276
+ raise 'Use CompletableEvent#complete or CompletableFuture#complete instead, ' +
277
+ 'constructed by Concurrent.event or Concurrent.future respectively.'
278
+ end
279
+
280
+ # @!visibility private
281
+ def complete(raise_on_reassign = true)
282
+ if complete_state
283
+ # go to synchronized block only if there were waiting threads
284
+ synchronize { ns_broadcast } if @Waiters.clear
285
+ call_callbacks
286
+ else
287
+ Concurrent::MultipleAssignmentError.new('multiple assignment') if raise_on_reassign
288
+ return false
289
+ end
290
+ self
291
+ end
292
+
293
+ # @!visibility private
294
+ # just for inspection
295
+ # @return [Array<AbstractPromise>]
296
+ def blocks
297
+ @Callbacks.each_with_object([]) do |callback, promises|
298
+ promises.push *callback.select { |v| v.is_a? AbstractPromise }
299
+ end
300
+ end
301
+
302
+ # @!visibility private
303
+ # just for inspection
304
+ def callbacks
305
+ @Callbacks.each.to_a
306
+ end
307
+
308
+ # @!visibility private
309
+ def add_callback(method, *args)
310
+ if completed?
311
+ call_callback method, *args
312
+ else
313
+ @Callbacks.push [method, *args]
314
+ call_callbacks if completed?
315
+ end
316
+ self
317
+ end
318
+
319
+ # @!visibility private
320
+ # only for inspection
321
+ def promise
322
+ @Promise
323
+ end
324
+
325
+ # @!visibility private
326
+ # only for inspection
327
+ def touched
328
+ @Touched.value
329
+ end
330
+
331
+ # only for debugging inspection
332
+ def waiting_threads
333
+ @Waiters.each.to_a
334
+ end
335
+
336
+ private
337
+
338
+ def wait_until_complete(timeout)
339
+ while true
340
+ last_waiter = @Waiters.peek # waiters' state before completion
341
+ break if completed?
342
+
343
+ # synchronize so it cannot be signaled before it waits
344
+ synchronize do
345
+ # ok only if completing thread did not start signaling
346
+ next unless @Waiters.compare_and_push last_waiter, Thread.current
347
+ ns_wait_until(timeout) { completed? }
348
+ break
349
+ end
350
+ end
351
+ self
352
+ end
353
+
354
+ def complete_state
355
+ COMPLETED if @State.compare_and_set(PENDING, COMPLETED)
356
+ end
357
+
358
+ def pr_with_async(executor, *args, &block)
359
+ Concurrent.post_on(executor, *args, &block)
360
+ end
361
+
362
+ def pr_async_callback_on_completion(executor, callback)
363
+ pr_with_async(executor) { pr_callback_on_completion callback }
364
+ end
365
+
366
+ def pr_callback_on_completion(callback)
367
+ callback.call
368
+ end
369
+
370
+ def pr_callback_notify_blocked(promise)
371
+ promise.on_done self
372
+ end
373
+
374
+ def call_callback(method, *args)
375
+ self.send method, *args
376
+ end
377
+
378
+ def call_callbacks
379
+ method, *args = @Callbacks.pop
380
+ while method
381
+ call_callback method, *args
382
+ method, *args = @Callbacks.pop
383
+ end
384
+ end
385
+ end
386
+
387
+ # Represents a value which will become available in future. May fail with a reason instead.
388
+ class Future < Event
389
+ class CompletedWithResult < Completed
390
+ def result
391
+ [success?, value, reason]
392
+ end
393
+
394
+ def success?
395
+ raise NotImplementedError
396
+ end
397
+
398
+ def value
399
+ raise NotImplementedError
400
+ end
401
+
402
+ def reason
403
+ raise NotImplementedError
404
+ end
405
+ end
406
+
407
+ class Success < CompletedWithResult
408
+ def initialize(value)
409
+ @Value = value
410
+ end
411
+
412
+ def success?
413
+ true
414
+ end
415
+
416
+ def apply(block)
417
+ block.call value
418
+ end
419
+
420
+ def value
421
+ @Value
422
+ end
423
+
424
+ def reason
425
+ nil
426
+ end
427
+
428
+ def to_sym
429
+ :success
430
+ end
431
+ end
432
+
433
+ class SuccessArray < Success
434
+ def apply(block)
435
+ block.call *value
436
+ end
437
+ end
438
+
439
+ class Failed < CompletedWithResult
440
+ def initialize(reason)
441
+ @Reason = reason
442
+ end
443
+
444
+ def success?
445
+ false
446
+ end
447
+
448
+ def value
449
+ nil
450
+ end
451
+
452
+ def reason
453
+ @Reason
454
+ end
455
+
456
+ def to_sym
457
+ :failed
458
+ end
459
+ end
460
+
461
+ # @!method state
462
+ # @return [:pending, :success, :failed]
463
+
464
+ # Has Future been success?
465
+ # @return [Boolean]
466
+ def success?(state = @State.get)
467
+ state.success?
468
+ end
469
+
470
+ def fulfilled?
471
+ deprecated_method 'fulfilled?', 'success?'
472
+ success?
473
+ end
474
+
475
+ # Has Future been failed?
476
+ # @return [Boolean]
477
+ def failed?(state = @State.get)
478
+ !success?(state)
479
+ end
480
+
481
+ def rejected?
482
+ deprecated_method 'rejected?', 'failed?'
483
+ failed?
484
+ end
485
+
486
+ # @return [Object] the value of the Future when success
487
+ def value(timeout = nil)
488
+ touch
489
+ wait_until_complete timeout
490
+ @State.get.value
491
+ end
492
+
493
+ # @return [Exception] the reason of the Future's failure
494
+ def reason(timeout = nil)
495
+ touch
496
+ wait_until_complete timeout
497
+ @State.get.reason
498
+ end
499
+
500
+ # @return [Array(Boolean, Object, Exception)] triplet of success, value, reason
501
+ def result(timeout = nil)
502
+ touch
503
+ wait_until_complete timeout
504
+ @State.get.result
505
+ end
506
+
507
+ # Wait until Future is #complete?
508
+ # @param [Numeric] timeout the maximum time in second to wait.
509
+ # @raise reason on failure
510
+ # @return [Event] self
511
+ def wait!(timeout = nil)
512
+ touch
513
+ wait_until_complete! timeout
514
+ end
515
+
516
+ # Wait until Future is #complete?
517
+ # @param [Numeric] timeout the maximum time in second to wait.
518
+ # @raise reason on failure
519
+ # @return [Object]
520
+ def value!(timeout = nil)
521
+ touch
522
+ wait_until_complete!(timeout)
523
+ @State.get.value
524
+ end
525
+
526
+ # @example allows failed Future to be risen
527
+ # raise Concurrent.future.fail
528
+ def exception(*args)
529
+ touch
530
+ raise 'obligation is not failed' unless failed?
531
+ @State.get.reason.exception(*args)
532
+ end
533
+
534
+ # @yield [value] executed only on parent success
535
+ # @return [Future]
536
+ def then(executor = nil, &callback)
537
+ ThenPromise.new(self, @DefaultExecutor, executor || @DefaultExecutor, &callback).future
538
+ end
539
+
540
+ # Asks the actor with its value.
541
+ # @return [Future] new future with the response form the actor
542
+ def then_ask(actor)
543
+ self.then { |v| actor.ask(v) }.flat
544
+ end
545
+
546
+ # @yield [reason] executed only on parent failure
547
+ # @return [Future]
548
+ def rescue(executor = nil, &callback)
549
+ RescuePromise.new(self, @DefaultExecutor, executor || @DefaultExecutor, &callback).future
550
+ end
551
+
552
+ # zips with the Future in the value
553
+ # @example
554
+ # Concurrent.future { Concurrent.future { 1 } }.flat.vale # => 1
555
+ def flat(level = 1)
556
+ FlattingPromise.new(self, level, @DefaultExecutor).future
557
+ end
558
+
559
+ # @return [Future] which has first completed value from futures
560
+ def any(*futures)
561
+ AnyPromise.new([self, *futures], @DefaultExecutor).future
562
+ end
563
+
564
+ alias_method :|, :any
565
+
566
+ # only proof of concept
567
+ def then_push(channel)
568
+ on_success { |value| channel.push value } # FIXME it's blocking for now
569
+ end
570
+
571
+ # @yield [value] executed async on `executor` when success
572
+ # @return self
573
+ def on_success(executor = nil, &callback)
574
+ add_callback :pr_async_callback_on_success, executor || @DefaultExecutor, callback
575
+ end
576
+
577
+ # @yield [reason] executed async on `executor` when failed?
578
+ # @return self
579
+ def on_failure(executor = nil, &callback)
580
+ add_callback :pr_async_callback_on_failure, executor || @DefaultExecutor, callback
581
+ end
582
+
583
+ # @yield [value] executed sync when success
584
+ # @return self
585
+ def on_success!(&callback)
586
+ add_callback :pr_callback_on_success, callback
587
+ end
588
+
589
+ # @yield [reason] executed sync when failed?
590
+ # @return self
591
+ def on_failure!(&callback)
592
+ add_callback :pr_callback_on_failure, callback
593
+ end
594
+
595
+ # @!visibility private
596
+ def complete(success, value, reason, raise_on_reassign = true)
597
+ if (new_state = complete_state success, value, reason)
598
+ @Waiters.clear
599
+ synchronize { ns_broadcast }
600
+ call_callbacks new_state
601
+ else
602
+ raise reason || Concurrent::MultipleAssignmentError.new('multiple assignment') if raise_on_reassign
603
+ return false
604
+ end
605
+ self
606
+ end
607
+
608
+ # @!visibility private
609
+ def add_callback(method, *args)
610
+ state = @State.get
611
+ if completed?(state)
612
+ call_callback method, state, *args
613
+ else
614
+ @Callbacks.push [method, *args]
615
+ state = @State.get
616
+ # take back if it was completed in the meanwhile
617
+ call_callbacks state if completed?(state)
618
+ end
619
+ self
620
+ end
621
+
622
+ # @!visibility private
623
+ def apply(block)
624
+ @State.get.apply block
625
+ end
626
+
627
+ private
628
+
629
+ def wait_until_complete!(timeout = nil)
630
+ wait_until_complete(timeout)
631
+ raise self if failed?
632
+ self
633
+ end
634
+
635
+ def complete_state(success, value, reason)
636
+ new_state = if success
637
+ if value.is_a?(Array)
638
+ SuccessArray.new(value)
639
+ else
640
+ Success.new(value)
641
+ end
642
+ else
643
+ Failed.new(reason)
644
+ end
645
+ new_state if @State.compare_and_set(PENDING, new_state)
646
+ end
647
+
648
+ def call_callbacks(state)
649
+ method, *args = @Callbacks.pop
650
+ while method
651
+ call_callback method, state, *args
652
+ method, *args = @Callbacks.pop
653
+ end
654
+ end
655
+
656
+ def call_callback(method, state, *args)
657
+ self.send method, state, *args
658
+ end
659
+
660
+ def pr_async_callback_on_success(state, executor, callback)
661
+ pr_with_async(executor, state, callback) do |state, callback|
662
+ pr_callback_on_success state, callback
663
+ end
664
+ end
665
+
666
+ def pr_async_callback_on_failure(state, executor, callback)
667
+ pr_with_async(executor, state, callback) do |state, callback|
668
+ pr_callback_on_failure state, callback
669
+ end
670
+ end
671
+
672
+ def pr_callback_on_success(state, callback)
673
+ state.apply callback if state.success?
674
+ end
675
+
676
+ def pr_callback_on_failure(state, callback)
677
+ state.apply callback unless state.success?
678
+ end
679
+
680
+ def pr_callback_on_completion(state, callback)
681
+ callback.call state.result
682
+ end
683
+
684
+ def pr_callback_notify_blocked(state, promise)
685
+ super(promise)
686
+ end
687
+
688
+ def pr_async_callback_on_completion(state, executor, callback)
689
+ pr_with_async(executor, state, callback) do |state, callback|
690
+ pr_callback_on_completion state, callback
691
+ end
692
+ end
693
+
694
+ end
695
+
696
+ # A Event which can be completed by user.
697
+ class CompletableEvent < Event
698
+ # Complete the Event, `raise` if already completed
699
+ def complete(raise_on_reassign = true)
700
+ super raise_on_reassign
701
+ end
702
+
703
+ def hide_completable
704
+ Concurrent.zip(self)
705
+ end
706
+ end
707
+
708
+ # A Future which can be completed by user.
709
+ class CompletableFuture < Future
710
+ # Complete the future with triplet od `success`, `value`, `reason`
711
+ # `raise` if already completed
712
+ # return [self]
713
+ def complete(success, value, reason, raise_on_reassign = true)
714
+ super success, value, reason, raise_on_reassign
715
+ end
716
+
717
+ # Complete the future with value
718
+ # return [self]
719
+ def success(value)
720
+ promise.success(value)
721
+ end
722
+
723
+ # Try to complete the future with value
724
+ # return [self]
725
+ def try_success(value)
726
+ promise.try_success(value)
727
+ end
728
+
729
+ # Fail the future with reason
730
+ # return [self]
731
+ def fail(reason = StandardError.new)
732
+ promise.fail(reason)
733
+ end
734
+
735
+ # Try to fail the future with reason
736
+ # return [self]
737
+ def try_fail(reason = StandardError.new)
738
+ promise.try_fail(reason)
739
+ end
740
+
741
+ # Evaluate the future to value if there is an exception the future fails with it
742
+ # return [self]
743
+ def evaluate_to(*args, &block)
744
+ promise.evaluate_to(*args, block)
745
+ end
746
+
747
+ # Evaluate the future to value if there is an exception the future fails with it
748
+ # @raise the exception
749
+ # return [self]
750
+ def evaluate_to!(*args, &block)
751
+ promise.evaluate_to!(*args, block)
752
+ end
753
+
754
+ def hide_completable
755
+ Concurrent.zip(self)
756
+ end
757
+ end
758
+
759
+ # TODO modularize blocked_by and notify blocked
760
+
761
+ # @abstract
762
+ # @!visibility private
763
+ class AbstractPromise < Synchronization::Object
764
+ def initialize(future)
765
+ @Future = future
766
+ ensure_ivar_visibility!
767
+ end
768
+
769
+ def future
770
+ @Future
771
+ end
772
+
773
+ alias_method :event, :future
774
+
775
+ def default_executor
776
+ future.default_executor
777
+ end
778
+
779
+ def state
780
+ future.state
781
+ end
782
+
783
+ def touch
784
+ end
785
+
786
+ def to_s
787
+ "<##{self.class}:0x#{'%x' % (object_id << 1)} #{state}>"
788
+ end
789
+
790
+ def inspect
791
+ to_s
792
+ end
793
+
794
+ private
795
+
796
+ def complete(*args)
797
+ @Future.complete(*args)
798
+ end
799
+
800
+ # @return [Future]
801
+ def evaluate_to(*args, block)
802
+ complete true, block.call(*args), nil
803
+ rescue => error
804
+ complete false, nil, error
805
+ end
806
+ end
807
+
808
+ # @!visibility private
809
+ class CompletableEventPromise < AbstractPromise
810
+ public :complete
811
+
812
+ def initialize(default_executor)
813
+ super CompletableEvent.new(self, default_executor)
814
+ end
815
+ end
816
+
817
+ # @!visibility private
818
+ class CompletableFuturePromise < AbstractPromise
819
+ # TODO consider to allow being blocked_by
820
+
821
+ def initialize(default_executor)
822
+ super CompletableFuture.new(self, default_executor)
823
+ end
824
+
825
+ # Set the `Future` to a value and wake or notify all threads waiting on it.
826
+ #
827
+ # @param [Object] value the value to store in the `Future`
828
+ # @raise [Concurrent::MultipleAssignmentError] if the `Future` has already been set or otherwise completed
829
+ # @return [Future]
830
+ def success(value)
831
+ complete(true, value, nil)
832
+ end
833
+
834
+ def try_success(value)
835
+ complete(true, value, nil, false)
836
+ end
837
+
838
+ # Set the `Future` to failed due to some error and wake or notify all threads waiting on it.
839
+ #
840
+ # @param [Object] reason for the failure
841
+ # @raise [Concurrent::MultipleAssignmentError] if the `Future` has already been set or otherwise completed
842
+ # @return [Future]
843
+ def fail(reason = StandardError.new)
844
+ complete(false, nil, reason)
845
+ end
846
+
847
+ def try_fail(reason = StandardError.new)
848
+ !!complete(false, nil, reason, false)
849
+ end
850
+
851
+ public :complete
852
+ public :evaluate_to
853
+
854
+ # @return [Future]
855
+ def evaluate_to!(*args, block)
856
+ evaluate_to(*args, block).wait!
857
+ end
858
+ end
859
+
860
+ # @abstract
861
+ # @!visibility private
862
+ class InnerPromise < AbstractPromise
863
+ end
864
+
865
+ # @abstract
866
+ # @!visibility private
867
+ class BlockedPromise < InnerPromise
868
+ def initialize(future, blocked_by_futures, countdown, &block)
869
+ initialize_blocked_by(blocked_by_futures)
870
+ @Countdown = AtomicFixnum.new countdown
871
+
872
+ super(future)
873
+ @BlockedBy.each { |future| future.add_callback :pr_callback_notify_blocked, self }
874
+ end
875
+
876
+ # @api private
877
+ def on_done(future)
878
+ countdown = process_on_done(future)
879
+ completable = completable?(countdown)
880
+
881
+ if completable
882
+ on_completable(future)
883
+ # futures could be deleted from blocked_by one by one here, but that would be too expensive,
884
+ # it's done once when all are done to free the reference
885
+ clear_blocked_by!
886
+ end
887
+ end
888
+
889
+ def touch
890
+ blocked_by.each(&:touch)
891
+ end
892
+
893
+ # !visibility private
894
+ # for inspection only
895
+ def blocked_by
896
+ @BlockedBy
897
+ end
898
+
899
+ def inspect
900
+ "#{to_s[0..-2]} blocked_by:[#{ blocked_by.map(&:to_s).join(', ')}]>"
901
+ end
902
+
903
+ private
904
+
905
+ def initialize_blocked_by(blocked_by_futures)
906
+ @BlockedBy = Array(blocked_by_futures)
907
+ end
908
+
909
+ def clear_blocked_by!
910
+ # not synchronized because we do not care when this change propagates
911
+ @BlockedBy = []
912
+ nil
913
+ end
914
+
915
+ # @return [true,false] if completable
916
+ def completable?(countdown)
917
+ countdown.zero?
918
+ end
919
+
920
+ def process_on_done(future)
921
+ @Countdown.decrement
922
+ end
923
+
924
+ def on_completable(done_future)
925
+ raise NotImplementedError
926
+ end
927
+ end
928
+
929
+ # @abstract
930
+ # @!visibility private
931
+ class BlockedTaskPromise < BlockedPromise
932
+ def initialize(blocked_by_future, default_executor, executor, &task)
933
+ raise ArgumentError, 'no block given' unless block_given?
934
+ @Executor = executor
935
+ @Task = task
936
+ super Future.new(self, default_executor), blocked_by_future, 1
937
+ end
938
+
939
+ def executor
940
+ @Executor
941
+ end
942
+ end
943
+
944
+ # @!visibility private
945
+ class ThenPromise < BlockedTaskPromise
946
+ private
947
+
948
+ def initialize(blocked_by_future, default_executor, executor, &task)
949
+ raise ArgumentError, 'only Future can be appended with then' unless blocked_by_future.is_a? Future
950
+ super blocked_by_future, default_executor, executor, &task
951
+ end
952
+
953
+ def on_completable(done_future)
954
+ if done_future.success?
955
+ Concurrent.post_on(@Executor, done_future, @Task) do |done_future, task|
956
+ evaluate_to lambda { done_future.apply task }
957
+ end
958
+ else
959
+ complete false, nil, done_future.reason
960
+ end
961
+ end
962
+ end
963
+
964
+ # @!visibility private
965
+ class RescuePromise < BlockedTaskPromise
966
+ private
967
+
968
+ def initialize(blocked_by_future, default_executor, executor, &task)
969
+ raise ArgumentError, 'only Future can be rescued' unless blocked_by_future.is_a? Future
970
+ super blocked_by_future, default_executor, executor, &task
971
+ end
972
+
973
+ def on_completable(done_future)
974
+ if done_future.failed?
975
+ Concurrent.post_on(@Executor, done_future.reason, @Task) { |reason, task| evaluate_to reason, task }
976
+ else
977
+ complete true, done_future.value, nil
978
+ end
979
+ end
980
+ end
981
+
982
+ # @!visibility private
983
+ class ChainPromise < BlockedTaskPromise
984
+ private
985
+
986
+ def on_completable(done_future)
987
+ if Future === done_future
988
+ Concurrent.post_on(@Executor, done_future, @Task) { |future, task| evaluate_to *future.result, task }
989
+ else
990
+ Concurrent.post_on(@Executor, @Task) { |task| evaluate_to task }
991
+ end
992
+ end
993
+ end
994
+
995
+ # will be immediately completed
996
+ # @!visibility private
997
+ class ImmediatePromise < InnerPromise
998
+ def initialize(default_executor, *args)
999
+ super(if args.empty?
1000
+ Event.new(self, default_executor).complete
1001
+ else
1002
+ Future.new(self, default_executor).complete(true, args, nil)
1003
+ end)
1004
+ end
1005
+ end
1006
+
1007
+ # @!visibility private
1008
+ class FlattingPromise < BlockedPromise
1009
+
1010
+ # !visibility private
1011
+ def blocked_by
1012
+ @BlockedBy.each.to_a
1013
+ end
1014
+
1015
+ private
1016
+
1017
+ def process_on_done(future)
1018
+ countdown = super(future)
1019
+ value = future.value!
1020
+ if countdown.nonzero?
1021
+ case value
1022
+ when Future
1023
+ @BlockedBy.push value
1024
+ value.add_callback :pr_callback_notify_blocked, self
1025
+ @Countdown.value
1026
+ when Event
1027
+ raise TypeError, 'cannot flatten to Event'
1028
+ else
1029
+ raise TypeError, "returned value #{value.inspect} is not a Future"
1030
+ end
1031
+ end
1032
+ countdown
1033
+ end
1034
+
1035
+ def initialize(blocked_by_future, levels, default_executor)
1036
+ raise ArgumentError, 'levels has to be higher than 0' if levels < 1
1037
+ blocked_by_future.is_a? Future or
1038
+ raise ArgumentError, 'only Future can be flatten'
1039
+ super Future.new(self, default_executor), blocked_by_future, 1 + levels
1040
+ end
1041
+
1042
+ def initialize_blocked_by(blocked_by_future)
1043
+ @BlockedBy = LockFreeStack.new.push(blocked_by_future)
1044
+ end
1045
+
1046
+ def on_completable(done_future)
1047
+ complete *done_future.result
1048
+ end
1049
+
1050
+ def clear_blocked_by!
1051
+ @BlockedBy.clear
1052
+ nil
1053
+ end
1054
+ end
1055
+
1056
+ # @!visibility private
1057
+ class AllPromise < BlockedPromise
1058
+
1059
+ private
1060
+
1061
+ def initialize(blocked_by_futures, default_executor)
1062
+ klass = Event
1063
+ blocked_by_futures.each do |f|
1064
+ if f.is_a?(Future)
1065
+ if klass == Event
1066
+ klass = Future
1067
+ break
1068
+ end
1069
+ end
1070
+ end
1071
+
1072
+ # noinspection RubyArgCount
1073
+ super(klass.new(self, default_executor), blocked_by_futures, blocked_by_futures.size)
1074
+ end
1075
+
1076
+ def on_completable(done_future)
1077
+ all_success = true
1078
+ values = []
1079
+ reasons = []
1080
+
1081
+ blocked_by.each do |future|
1082
+ next unless future.is_a?(Future)
1083
+ success, value, reason = future.result
1084
+
1085
+ unless success
1086
+ all_success = false
1087
+ end
1088
+
1089
+ values << value
1090
+ reasons << reason
1091
+ end
1092
+
1093
+ if all_success
1094
+ if values.empty?
1095
+ complete
1096
+ else
1097
+ complete(true, values.size == 1 ? values.first : values, nil)
1098
+ end
1099
+ else
1100
+ # TODO what about other reasons?
1101
+ complete(false, nil, reasons.compact.first)
1102
+ end
1103
+ end
1104
+ end
1105
+
1106
+ # @!visibility private
1107
+ class AnyPromise < BlockedPromise
1108
+
1109
+ private
1110
+
1111
+ def initialize(blocked_by_futures, default_executor)
1112
+ blocked_by_futures.all? { |f| f.is_a? Future } or
1113
+ raise ArgumentError, 'accepts only Futures not Events'
1114
+ super(Future.new(self, default_executor), blocked_by_futures, blocked_by_futures.size)
1115
+ end
1116
+
1117
+ def completable?(countdown)
1118
+ true
1119
+ end
1120
+
1121
+ def on_completable(done_future)
1122
+ complete *done_future.result, false
1123
+ end
1124
+ end
1125
+
1126
+ # @!visibility private
1127
+ class Delay < InnerPromise
1128
+ def touch
1129
+ if @Args.empty?
1130
+ @Future.complete
1131
+ else
1132
+ @Future.complete(true, @Args, nil)
1133
+ end
1134
+ end
1135
+
1136
+ private
1137
+
1138
+ def initialize(default_executor, *args)
1139
+ @Args = args
1140
+ super(if args.empty?
1141
+ Event.new(self, default_executor)
1142
+ else
1143
+ Future.new(self, default_executor)
1144
+ end)
1145
+ end
1146
+ end
1147
+
1148
+ # will be evaluated to task in intended_time
1149
+ # @!visibility private
1150
+ class ScheduledPromise < InnerPromise
1151
+ def intended_time
1152
+ @IntendedTime
1153
+ end
1154
+
1155
+ def inspect
1156
+ "#{to_s[0..-2]} intended_time:[#{@IntendedTime}}>"
1157
+ end
1158
+
1159
+ private
1160
+
1161
+ def initialize(default_executor, intended_time, *args)
1162
+ @IntendedTime = intended_time
1163
+
1164
+ in_seconds = begin
1165
+ now = Time.now
1166
+ schedule_time = if @IntendedTime.is_a? Time
1167
+ @IntendedTime
1168
+ else
1169
+ now + @IntendedTime
1170
+ end
1171
+ [0, schedule_time.to_f - now.to_f].max
1172
+ end
1173
+
1174
+ use_event = args.empty?
1175
+ super(if use_event
1176
+ Event.new(self, default_executor)
1177
+ else
1178
+ Future.new(self, default_executor)
1179
+ end)
1180
+
1181
+ Concurrent.global_timer_set.post(in_seconds, *args) do |*args|
1182
+ if use_event
1183
+ @Future.complete
1184
+ else
1185
+ @Future.complete(true, args, nil)
1186
+ end
1187
+ end
1188
+ end
1189
+ end
1190
+
1191
+ # proof of concept
1192
+ class Channel < Synchronization::Object
1193
+ # TODO make lock free
1194
+ def initialize
1195
+ super
1196
+ @ProbeSet = Concurrent::Channel::WaitableList.new
1197
+ ensure_ivar_visibility!
1198
+ end
1199
+
1200
+ def probe_set_size
1201
+ @ProbeSet.size
1202
+ end
1203
+
1204
+ def push(value)
1205
+ until @ProbeSet.take.try_success([value, self])
1206
+ end
1207
+ end
1208
+
1209
+ def pop
1210
+ select(Concurrent.future)
1211
+ end
1212
+
1213
+ def select(probe)
1214
+ @ProbeSet.put(probe)
1215
+ probe
1216
+ end
1217
+
1218
+ def inspect
1219
+ to_s
1220
+ end
1221
+ end
1222
+ end
1223
+
1224
+ extend Edge::FutureShortcuts
1225
+ include Edge::FutureShortcuts
1226
+ end