concurrent-ruby-edge 0.1.0.pre2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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