concurrent-ruby-edge 0.1.0.pre2 → 0.1.0.pre3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 21429b1dcafe35b79201be08fd02442171fc68fb
4
- data.tar.gz: 2de92c445f74fbf670cb605e760b9a4c413597c4
3
+ metadata.gz: b3b0a6f42dd53529fb35370f6d9d6b1957aa576c
4
+ data.tar.gz: b960e25b2834254aaa00685cf38e41fc3a481e9f
5
5
  SHA512:
6
- metadata.gz: cb45d38d9f966ff005121fec47adc053349675ee7d018662e7b49c67a67fdd3c3bb603cca559d3b39c4243f62ff099905c72b320b2ada6b29d8d5fc00541df49
7
- data.tar.gz: 2e1c9c51ec89bb08e32ced3883cf5fa8cf472bdf0a98616af1966e7929c2c018ee832b747262fff13831295aa381c67cf883f207f60c9dfa29355c504803563d
6
+ metadata.gz: aec6112c612dbfa9f709181cef273ba892ae5db92915875e7750d18e159c821e613f5c02520530eb27805e60531e55e68021ac003279a94b8d78b63ef184948e
7
+ data.tar.gz: bb59836fc4e5da39fc2c3f7885d1a123f0a9100850c9c486871830d9421c5b2e0756f45e9f7c7dee7f538adfd030f765b3a54fa33fd94726c13e05b8d08b4977
@@ -12,14 +12,14 @@ module Concurrent
12
12
  # @yieldparam [Integer] index of the worker, usually used in its name
13
13
  # @yieldreturn [Reference] the reference of newly created worker
14
14
  # @example
15
- # class Worker < Concurrent::Actor::Utils::AbstractWorker
16
- # def work(message)
15
+ # class Worker < Concurrent::Actor::RestartingContext
16
+ # def on_message(message)
17
17
  # p message * 5
18
18
  # end
19
19
  # end
20
20
  #
21
- # pool = Concurrent::Actor::Utils::Pool.spawn! 'pool', 5 do |balancer, index|
22
- # Worker.spawn name: "worker-#{index}", supervise: true, args: [balancer]
21
+ # pool = Concurrent::Actor::Utils::Pool.spawn! 'pool', 5 do |index|
22
+ # Worker.spawn name: "worker-#{index}", supervise: true, args: []
23
23
  # end
24
24
  #
25
25
  # pool << 'asd' << 2
@@ -93,15 +93,7 @@ module Concurrent
93
93
  #
94
94
  # @param [Object] initial the initial value
95
95
  #
96
- # @!macro [attach] executor_and_deref_options
97
- #
98
- # @param [Hash] opts the options used to define the behavior at update and deref
99
- # and to specify the executor on which to perform actions
100
- # @option opts [Executor] :executor when set use the given `Executor` instance.
101
- # Three special values are also supported: `:task` returns the global task pool,
102
- # `:operation` returns the global operation pool, and `:immediate` returns a new
103
- # `ImmediateExecutor` object.
104
- # @!macro deref_options
96
+ # @!macro executor_and_deref_options
105
97
  def initialize(initial, opts = {})
106
98
  @value = initial
107
99
  @rescuers = []
@@ -31,42 +31,38 @@ module Concurrent
31
31
  # @overload future(default_executor = :io)
32
32
  # User is responsible for completing the future once by {Edge::CompletableFuture#success} or {Edge::CompletableFuture#fail}
33
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)
34
+ def future(default_executor = :io, &task)
39
35
  if task
40
- ImmediatePromise.new(default_executor, *args).future.then(&task)
36
+ ImmediateEventPromise.new(default_executor).future.then(&task)
41
37
  else
42
38
  CompletableFuturePromise.new(default_executor).future
43
39
  end
44
40
  end
45
41
 
42
+ # @return [Future] which is already completed with value
43
+ def completed_future(value, default_executor = :io)
44
+ ImmediateFuturePromise.new(default_executor, value).future
45
+ end
46
+
47
+ # @return [Event] which is already completed
48
+ def completed_event(default_executor = :io)
49
+ ImmediateEventPromise.new(default_executor).event
50
+ end
51
+
46
52
  alias_method :async, :future
47
53
 
48
54
  # Constructs new Future which will evaluate to the block after
49
55
  # requested by calling `#wait`, `#value`, `#value!`, etc. on it or on any of the chained futures.
50
56
  # @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)
57
+ def delay(default_executor = :io, &task)
58
+ Delay.new(default_executor).future.then(&task)
58
59
  end
59
60
 
60
61
  # Schedules the block to be executed on executor in given intended_time.
61
62
  # @param [Numeric, Time] intended_time Numeric => run in `intended_time` seconds. Time => eun on time.
62
63
  # @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)
64
+ def schedule(intended_time, default_executor = :io, &task)
65
+ ScheduledPromise.new(default_executor, intended_time).future.then(&task)
70
66
  end
71
67
 
72
68
  # Constructs new {Future} which is completed after all futures are complete. Its value is array
@@ -74,7 +70,7 @@ module Concurrent
74
70
  # @param [Event] futures
75
71
  # @return [Future]
76
72
  def zip(*futures)
77
- AllPromise.new(futures, :io).future
73
+ ZipPromise.new(futures, :io).future
78
74
  end
79
75
 
80
76
  # Constructs new {Future} which is completed after first of the futures is complete.
@@ -121,6 +117,7 @@ module Concurrent
121
117
  class Event < Synchronization::Object
122
118
  include Concern::Deprecation
123
119
 
120
+ # @!visibility private
124
121
  class State
125
122
  def completed?
126
123
  raise NotImplementedError
@@ -131,6 +128,7 @@ module Concurrent
131
128
  end
132
129
  end
133
130
 
131
+ # @!visibility private
134
132
  class Pending < State
135
133
  def completed?
136
134
  false
@@ -141,6 +139,7 @@ module Concurrent
141
139
  end
142
140
  end
143
141
 
142
+ # @!visibility private
144
143
  class Completed < State
145
144
  def completed?
146
145
  true
@@ -151,7 +150,9 @@ module Concurrent
151
150
  end
152
151
  end
153
152
 
153
+ # @!visibility private
154
154
  PENDING = Pending.new
155
+ # @!visibility private
155
156
  COMPLETED = Completed.new
156
157
 
157
158
  def initialize(promise, default_executor)
@@ -159,7 +160,7 @@ module Concurrent
159
160
  @DefaultExecutor = default_executor
160
161
  @Touched = AtomicBoolean.new(false)
161
162
  @Callbacks = LockFreeStack.new
162
- @Waiters = LockFreeStack.new
163
+ @Waiters = LockFreeStack.new # TODO replace with AtomicFixnum, avoid aba problem
163
164
  @State = AtomicReference.new PENDING
164
165
  super()
165
166
  ensure_ivar_visibility!
@@ -192,11 +193,14 @@ module Concurrent
192
193
 
193
194
  # Wait until Event is #complete?
194
195
  # @param [Numeric] timeout the maximum time in second to wait.
195
- # @return [Event] self
196
+ # @return [Event, true, false] self or true/false if timeout is used
197
+ # @!macro [attach] edge.periodical_wait
198
+ # @note a thread should wait only once! For repeated checking use faster `completed?` check.
199
+ # If thread waits periodically it will dangerously grow the waiters stack.
196
200
  def wait(timeout = nil)
197
201
  touch
198
- wait_until_complete timeout
199
- self
202
+ result = wait_until_complete(timeout)
203
+ timeout ? result : self
200
204
  end
201
205
 
202
206
  # @!visibility private
@@ -219,31 +223,44 @@ module Concurrent
219
223
 
220
224
  alias_method :then, :chain
221
225
 
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
226
+ def chain_completable(completable_event)
227
+ on_completion! { completable_event.complete_with COMPLETED }
228
+ end
229
+
230
+ alias_method :tangle, :chain_completable
231
+
232
+ # Zip with future producing new Future
233
+ # @return [Event]
234
+ def zip(other)
235
+ if other.is?(Future)
236
+ ZipFutureEventPromise.new(other, self, @DefaultExecutor).future
237
+ else
238
+ ZipEventEventPromise.new(self, other, @DefaultExecutor).future
239
+ end
227
240
  end
228
241
 
229
242
  alias_method :&, :zip
230
243
 
231
244
  # Inserts delay into the chain of Futures making rest of it lazy evaluated.
232
- # @return [Future]
245
+ # @return [Event]
233
246
  def delay
234
- zip(Delay.new(@DefaultExecutor).future)
247
+ ZipEventEventPromise.new(self, Delay.new(@DefaultExecutor).event, @DefaultExecutor).event
235
248
  end
236
249
 
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
250
+ # # Schedules rest of the chain for execution with specified time or on specified time
251
+ # # @return [Event]
252
+ # def schedule(intended_time)
253
+ # chain do
254
+ # ZipEventEventPromise.new(self,
255
+ # ScheduledPromise.new(@DefaultExecutor, intended_time).event,
256
+ # @DefaultExecutor).event
257
+ # end.flat
258
+ # end
242
259
 
243
260
  # Zips with selected value form the suplied channels
244
261
  # @return [Future]
245
262
  def then_select(*channels)
246
- self.zip(Concurrent.select(*channels))
263
+ ZipFutureEventPromise(Concurrent.select(*channels), self, @DefaultExecutor).future
247
264
  end
248
265
 
249
266
  # @yield [success, value, reason] executed async on `executor` when completed
@@ -259,9 +276,9 @@ module Concurrent
259
276
  end
260
277
 
261
278
  # Changes default executor for rest of the chain
262
- # @return [Future]
279
+ # @return [Event]
263
280
  def with_default_executor(executor)
264
- AllPromise.new([self], executor).future
281
+ EventWrapperPromise.new(self, executor).future
265
282
  end
266
283
 
267
284
  def to_s
@@ -278,13 +295,14 @@ module Concurrent
278
295
  end
279
296
 
280
297
  # @!visibility private
281
- def complete(raise_on_reassign = true)
282
- if complete_state
298
+ def complete_with(state, raise_on_reassign = true)
299
+ if @State.compare_and_set(PENDING, state)
300
+ (state)
283
301
  # go to synchronized block only if there were waiting threads
284
302
  synchronize { ns_broadcast } if @Waiters.clear
285
303
  call_callbacks
286
304
  else
287
- Concurrent::MultipleAssignmentError.new('multiple assignment') if raise_on_reassign
305
+ Concurrent::MultipleAssignmentError.new('Event can be completed only once') if raise_on_reassign
288
306
  return false
289
307
  end
290
308
  self
@@ -328,31 +346,32 @@ module Concurrent
328
346
  @Touched.value
329
347
  end
330
348
 
349
+ # @!visibility private
331
350
  # only for debugging inspection
332
351
  def waiting_threads
333
352
  @Waiters.each.to_a
334
353
  end
335
354
 
355
+ # @!visibility private
356
+ def internal_state
357
+ @State.get
358
+ end
359
+
336
360
  private
337
361
 
362
+ # @return [true, false]
338
363
  def wait_until_complete(timeout)
339
364
  while true
340
365
  last_waiter = @Waiters.peek # waiters' state before completion
341
- break if completed?
366
+ return true if completed?
342
367
 
343
368
  # synchronize so it cannot be signaled before it waits
344
369
  synchronize do
345
370
  # ok only if completing thread did not start signaling
346
371
  next unless @Waiters.compare_and_push last_waiter, Thread.current
347
- ns_wait_until(timeout) { completed? }
348
- break
372
+ return ns_wait_until(timeout) { completed? }
349
373
  end
350
374
  end
351
- self
352
- end
353
-
354
- def complete_state
355
- COMPLETED if @State.compare_and_set(PENDING, COMPLETED)
356
375
  end
357
376
 
358
377
  def pr_with_async(executor, *args, &block)
@@ -386,6 +405,7 @@ module Concurrent
386
405
 
387
406
  # Represents a value which will become available in future. May fail with a reason instead.
388
407
  class Future < Event
408
+ # @!visibility private
389
409
  class CompletedWithResult < Completed
390
410
  def result
391
411
  [success?, value, reason]
@@ -404,6 +424,7 @@ module Concurrent
404
424
  end
405
425
  end
406
426
 
427
+ # @!visibility private
407
428
  class Success < CompletedWithResult
408
429
  def initialize(value)
409
430
  @Value = value
@@ -430,12 +451,14 @@ module Concurrent
430
451
  end
431
452
  end
432
453
 
454
+ # @!visibility private
433
455
  class SuccessArray < Success
434
456
  def apply(block)
435
457
  block.call *value
436
458
  end
437
459
  end
438
460
 
461
+ # @!visibility private
439
462
  class Failed < CompletedWithResult
440
463
  def initialize(reason)
441
464
  @Reason = reason
@@ -456,6 +479,39 @@ module Concurrent
456
479
  def to_sym
457
480
  :failed
458
481
  end
482
+
483
+ def apply(block)
484
+ block.call reason
485
+ end
486
+ end
487
+
488
+ # @!visibility private
489
+ class PartiallyFailed < CompletedWithResult
490
+ def initialize(value, reason)
491
+ @Value = value
492
+ @Reason = reason
493
+ super()
494
+ end
495
+
496
+ def success?
497
+ false
498
+ end
499
+
500
+ def to_sym
501
+ :failed
502
+ end
503
+
504
+ def value
505
+ @Value
506
+ end
507
+
508
+ def reason
509
+ @Reason
510
+ end
511
+
512
+ def apply(block)
513
+ block.call *reason
514
+ end
459
515
  end
460
516
 
461
517
  # @!method state
@@ -464,7 +520,7 @@ module Concurrent
464
520
  # Has Future been success?
465
521
  # @return [Boolean]
466
522
  def success?(state = @State.get)
467
- state.success?
523
+ state.completed? && state.success?
468
524
  end
469
525
 
470
526
  def fulfilled?
@@ -475,7 +531,7 @@ module Concurrent
475
531
  # Has Future been failed?
476
532
  # @return [Boolean]
477
533
  def failed?(state = @State.get)
478
- !success?(state)
534
+ state.completed? && !state.success?
479
535
  end
480
536
 
481
537
  def rejected?
@@ -483,52 +539,65 @@ module Concurrent
483
539
  failed?
484
540
  end
485
541
 
486
- # @return [Object] the value of the Future when success
542
+ # @return [Object, nil] the value of the Future when success, nil on timeout
543
+ # @!macro [attach] edge.timeout_nil
544
+ # @note If the Future can have value `nil` then it cannot be distinquished from `nil` returned on timeout.
545
+ # In this case is better to use first `wait` then `value` (or similar).
546
+ # @!macro edge.periodical_wait
487
547
  def value(timeout = nil)
488
548
  touch
489
- wait_until_complete timeout
490
- @State.get.value
549
+ @State.get.value if wait_until_complete timeout
491
550
  end
492
551
 
493
- # @return [Exception] the reason of the Future's failure
552
+ # @return [Exception, nil] the reason of the Future's failure
553
+ # @!macro edge.timeout_nil
554
+ # @!macro edge.periodical_wait
494
555
  def reason(timeout = nil)
495
556
  touch
496
- wait_until_complete timeout
497
- @State.get.reason
557
+ @State.get.reason if wait_until_complete timeout
498
558
  end
499
559
 
500
- # @return [Array(Boolean, Object, Exception)] triplet of success, value, reason
560
+ # @return [Array(Boolean, Object, Exception), nil] triplet of success, value, reason
561
+ # @!macro edge.timeout_nil
562
+ # @!macro edge.periodical_wait
501
563
  def result(timeout = nil)
502
564
  touch
503
- wait_until_complete timeout
504
- @State.get.result
565
+ @State.get.result if wait_until_complete timeout
505
566
  end
506
567
 
507
568
  # Wait until Future is #complete?
508
569
  # @param [Numeric] timeout the maximum time in second to wait.
509
570
  # @raise reason on failure
510
- # @return [Event] self
571
+ # @return [Event, true, false] self or true/false if timeout is used
572
+ # @!macro edge.periodical_wait
511
573
  def wait!(timeout = nil)
512
574
  touch
513
- wait_until_complete! timeout
575
+ result = wait_until_complete!(timeout)
576
+ timeout ? result : self
514
577
  end
515
578
 
516
579
  # Wait until Future is #complete?
517
580
  # @param [Numeric] timeout the maximum time in second to wait.
518
581
  # @raise reason on failure
519
- # @return [Object]
582
+ # @return [Object, nil]
583
+ # @!macro edge.timeout_nil
584
+ # @!macro edge.periodical_wait
520
585
  def value!(timeout = nil)
521
586
  touch
522
- wait_until_complete!(timeout)
523
- @State.get.value
587
+ @State.get.value if wait_until_complete! timeout
524
588
  end
525
589
 
526
590
  # @example allows failed Future to be risen
527
591
  # raise Concurrent.future.fail
528
592
  def exception(*args)
529
- touch
530
593
  raise 'obligation is not failed' unless failed?
531
- @State.get.reason.exception(*args)
594
+ reason = @State.get.reason
595
+ if reason.is_a?(Array)
596
+ reason.each { |e| log Error, 'Edge::Future', e }
597
+ Concurrent::Error.new 'multiple exceptions, inspect log'
598
+ else
599
+ reason.exception(*args)
600
+ end
532
601
  end
533
602
 
534
603
  # @yield [value] executed only on parent success
@@ -543,6 +612,12 @@ module Concurrent
543
612
  self.then { |v| actor.ask(v) }.flat
544
613
  end
545
614
 
615
+ def chain_completable(completable_future)
616
+ on_completion! { completable_future.complete_with internal_state }
617
+ end
618
+
619
+ alias_method :tangle, :chain_completable
620
+
546
621
  # @yield [reason] executed only on parent failure
547
622
  # @return [Future]
548
623
  def rescue(executor = nil, &callback)
@@ -553,7 +628,7 @@ module Concurrent
553
628
  # @example
554
629
  # Concurrent.future { Concurrent.future { 1 } }.flat.vale # => 1
555
630
  def flat(level = 1)
556
- FlattingPromise.new(self, level, @DefaultExecutor).future
631
+ FlatPromise.new(self, level, @DefaultExecutor).future
557
632
  end
558
633
 
559
634
  # @return [Future] which has first completed value from futures
@@ -561,11 +636,52 @@ module Concurrent
561
636
  AnyPromise.new([self, *futures], @DefaultExecutor).future
562
637
  end
563
638
 
639
+ # Inserts delay into the chain of Futures making rest of it lazy evaluated.
640
+ # @return [Future]
641
+ def delay
642
+ ZipFutureEventPromise.new(self, Delay.new(@DefaultExecutor).future, @DefaultExecutor).future
643
+ end
644
+
645
+ # Schedules rest of the chain for execution with specified time or on specified time
646
+ # @return [Future]
647
+ def schedule(intended_time)
648
+ chain do
649
+ ZipFutureEventPromise.new(self,
650
+ ScheduledPromise.new(@DefaultExecutor, intended_time).event,
651
+ @DefaultExecutor).future
652
+ end.flat
653
+ end
654
+
655
+ # Zips with selected value form the suplied channels
656
+ # @return [Future]
657
+ def then_select(*channels)
658
+ ZipPromise.new([self, Concurrent.select(*channels)], @DefaultExecutor).future
659
+ end
660
+
661
+ # Changes default executor for rest of the chain
662
+ # @return [Future]
663
+ def with_default_executor(executor)
664
+ FutureWrapperPromise.new(self, executor).future
665
+ end
666
+
667
+ # Zip with future producing new Future
668
+ # @return [Future]
669
+ def zip(other)
670
+ if other.is_a?(Future)
671
+ ZipFutureFuturePromise.new(self, other, @DefaultExecutor).future
672
+ else
673
+ ZipFutureEventPromise.new(self, other, @DefaultExecutor).future
674
+ end
675
+ end
676
+
677
+ alias_method :&, :zip
678
+
564
679
  alias_method :|, :any
565
680
 
566
681
  # only proof of concept
682
+ # @note may block
567
683
  def then_push(channel)
568
- on_success { |value| channel.push value } # FIXME it's blocking for now
684
+ on_success { |value| channel.push value }
569
685
  end
570
686
 
571
687
  # @yield [value] executed async on `executor` when success
@@ -593,13 +709,18 @@ module Concurrent
593
709
  end
594
710
 
595
711
  # @!visibility private
596
- def complete(success, value, reason, raise_on_reassign = true)
597
- if (new_state = complete_state success, value, reason)
712
+ def complete_with(state, raise_on_reassign = true)
713
+ if @State.compare_and_set(PENDING, state)
598
714
  @Waiters.clear
599
715
  synchronize { ns_broadcast }
600
- call_callbacks new_state
716
+ call_callbacks state
601
717
  else
602
- raise reason || Concurrent::MultipleAssignmentError.new('multiple assignment') if raise_on_reassign
718
+ if raise_on_reassign
719
+ log ERROR, 'Edge::Future', reason if reason # print otherwise hidden error
720
+ raise(Concurrent::MultipleAssignmentError.new(
721
+ "Future can be completed only once. Current result is #{result}, " +
722
+ "trying to set #{state.result}"))
723
+ end
603
724
  return false
604
725
  end
605
726
  self
@@ -627,22 +748,9 @@ module Concurrent
627
748
  private
628
749
 
629
750
  def wait_until_complete!(timeout = nil)
630
- wait_until_complete(timeout)
751
+ result = wait_until_complete(timeout)
631
752
  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)
753
+ result
646
754
  end
647
755
 
648
756
  def call_callbacks(state)
@@ -697,11 +805,11 @@ module Concurrent
697
805
  class CompletableEvent < Event
698
806
  # Complete the Event, `raise` if already completed
699
807
  def complete(raise_on_reassign = true)
700
- super raise_on_reassign
808
+ complete_with COMPLETED, raise_on_reassign
701
809
  end
702
810
 
703
811
  def hide_completable
704
- Concurrent.zip(self)
812
+ EventWrapperPromise.new(self, @DefaultExecutor).event
705
813
  end
706
814
  end
707
815
 
@@ -711,7 +819,7 @@ module Concurrent
711
819
  # `raise` if already completed
712
820
  # return [self]
713
821
  def complete(success, value, reason, raise_on_reassign = true)
714
- super success, value, reason, raise_on_reassign
822
+ complete_with(success ? Success.new(value) : Failed.new(reason), raise_on_reassign)
715
823
  end
716
824
 
717
825
  # Complete the future with value
@@ -752,12 +860,10 @@ module Concurrent
752
860
  end
753
861
 
754
862
  def hide_completable
755
- Concurrent.zip(self)
863
+ FutureWrapperPromise.new(self, @DefaultExecutor).future
756
864
  end
757
865
  end
758
866
 
759
- # TODO modularize blocked_by and notify blocked
760
-
761
867
  # @abstract
762
868
  # @!visibility private
763
869
  class AbstractPromise < Synchronization::Object
@@ -793,22 +899,20 @@ module Concurrent
793
899
 
794
900
  private
795
901
 
796
- def complete(*args)
797
- @Future.complete(*args)
902
+ def complete_with(new_state, raise_on_reassign = true)
903
+ @Future.complete_with(new_state, raise_on_reassign)
798
904
  end
799
905
 
800
906
  # @return [Future]
801
907
  def evaluate_to(*args, block)
802
- complete true, block.call(*args), nil
908
+ complete_with Future::Success.new(block.call(*args))
803
909
  rescue => error
804
- complete false, nil, error
910
+ complete_with Future::Failed.new(error)
805
911
  end
806
912
  end
807
913
 
808
914
  # @!visibility private
809
915
  class CompletableEventPromise < AbstractPromise
810
- public :complete
811
-
812
916
  def initialize(default_executor)
813
917
  super CompletableEvent.new(self, default_executor)
814
918
  end
@@ -816,8 +920,6 @@ module Concurrent
816
920
 
817
921
  # @!visibility private
818
922
  class CompletableFuturePromise < AbstractPromise
819
- # TODO consider to allow being blocked_by
820
-
821
923
  def initialize(default_executor)
822
924
  super CompletableFuture.new(self, default_executor)
823
925
  end
@@ -828,11 +930,11 @@ module Concurrent
828
930
  # @raise [Concurrent::MultipleAssignmentError] if the `Future` has already been set or otherwise completed
829
931
  # @return [Future]
830
932
  def success(value)
831
- complete(true, value, nil)
933
+ complete_with Future::Success.new(value)
832
934
  end
833
935
 
834
936
  def try_success(value)
835
- complete(true, value, nil, false)
937
+ !!complete_with(Future::Success.new(value), false)
836
938
  end
837
939
 
838
940
  # Set the `Future` to failed due to some error and wake or notify all threads waiting on it.
@@ -841,14 +943,13 @@ module Concurrent
841
943
  # @raise [Concurrent::MultipleAssignmentError] if the `Future` has already been set or otherwise completed
842
944
  # @return [Future]
843
945
  def fail(reason = StandardError.new)
844
- complete(false, nil, reason)
946
+ complete_with Future::Failed.new(reason)
845
947
  end
846
948
 
847
949
  def try_fail(reason = StandardError.new)
848
- !!complete(false, nil, reason, false)
950
+ !!complete_with(Future::Failed.new(reason), false)
849
951
  end
850
952
 
851
- public :complete
852
953
  public :evaluate_to
853
954
 
854
955
  # @return [Future]
@@ -865,7 +966,7 @@ module Concurrent
865
966
  # @abstract
866
967
  # @!visibility private
867
968
  class BlockedPromise < InnerPromise
868
- def initialize(future, blocked_by_futures, countdown, &block)
969
+ def initialize(future, blocked_by_futures, countdown)
869
970
  initialize_blocked_by(blocked_by_futures)
870
971
  @Countdown = AtomicFixnum.new countdown
871
972
 
@@ -956,7 +1057,7 @@ module Concurrent
956
1057
  evaluate_to lambda { done_future.apply task }
957
1058
  end
958
1059
  else
959
- complete false, nil, done_future.reason
1060
+ complete_with Future::Failed.new(done_future.reason)
960
1061
  end
961
1062
  end
962
1063
  end
@@ -966,15 +1067,16 @@ module Concurrent
966
1067
  private
967
1068
 
968
1069
  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
1070
  super blocked_by_future, default_executor, executor, &task
971
1071
  end
972
1072
 
973
1073
  def on_completable(done_future)
974
1074
  if done_future.failed?
975
- Concurrent.post_on(@Executor, done_future.reason, @Task) { |reason, task| evaluate_to reason, task }
1075
+ Concurrent.post_on(@Executor, done_future, @Task) do |done_future, task|
1076
+ evaluate_to lambda { done_future.apply task }
1077
+ end
976
1078
  else
977
- complete true, done_future.value, nil
1079
+ complete_with done_future.internal_state
978
1080
  end
979
1081
  end
980
1082
  end
@@ -994,18 +1096,21 @@ module Concurrent
994
1096
 
995
1097
  # will be immediately completed
996
1098
  # @!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)
1099
+ class ImmediateEventPromise < InnerPromise
1100
+ def initialize(default_executor)
1101
+ super Event.new(self, default_executor).complete_with(Event::COMPLETED)
1004
1102
  end
1005
1103
  end
1006
1104
 
1007
1105
  # @!visibility private
1008
- class FlattingPromise < BlockedPromise
1106
+ class ImmediateFuturePromise < InnerPromise
1107
+ def initialize(default_executor, value)
1108
+ super Future.new(self, default_executor).complete_with(Future::Success.new(value))
1109
+ end
1110
+ end
1111
+
1112
+ # @!visibility private
1113
+ class FlatPromise < BlockedPromise
1009
1114
 
1010
1115
  # !visibility private
1011
1116
  def blocked_by
@@ -1034,8 +1139,6 @@ module Concurrent
1034
1139
 
1035
1140
  def initialize(blocked_by_future, levels, default_executor)
1036
1141
  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
1142
  super Future.new(self, default_executor), blocked_by_future, 1 + levels
1040
1143
  end
1041
1144
 
@@ -1044,7 +1147,7 @@ module Concurrent
1044
1147
  end
1045
1148
 
1046
1149
  def on_completable(done_future)
1047
- complete *done_future.result
1150
+ complete_with done_future.internal_state
1048
1151
  end
1049
1152
 
1050
1153
  def clear_blocked_by!
@@ -1054,7 +1157,73 @@ module Concurrent
1054
1157
  end
1055
1158
 
1056
1159
  # @!visibility private
1057
- class AllPromise < BlockedPromise
1160
+ class ZipEventEventPromise < BlockedPromise
1161
+ def initialize(event1, event2, default_executor)
1162
+ super Event.new(self, default_executor), [event1, event2], 2
1163
+ end
1164
+
1165
+ def on_completable(done_future)
1166
+ complete_with Event::COMPLETED
1167
+ end
1168
+ end
1169
+
1170
+ # @!visibility private
1171
+ class ZipFutureEventPromise < BlockedPromise
1172
+ def initialize(future, event, default_executor)
1173
+ @FutureResult = future
1174
+ super Future.new(self, default_executor), [future, event], 2
1175
+ end
1176
+
1177
+ def on_completable(done_future)
1178
+ complete_with @FutureResult.internal_state
1179
+ end
1180
+ end
1181
+
1182
+ # @!visibility private
1183
+ class ZipFutureFuturePromise < BlockedPromise
1184
+ def initialize(future1, future2, default_executor)
1185
+ @Future1Result = future1
1186
+ @Future2Result = future2
1187
+ super Future.new(self, default_executor), [future1, future2], 2
1188
+ end
1189
+
1190
+ def on_completable(done_future)
1191
+ success1, value1, reason1 = @Future1Result.result
1192
+ success2, value2, reason2 = @Future2Result.result
1193
+ success = success1 && success2
1194
+ new_state = if success
1195
+ Future::SuccessArray.new([value1, value2])
1196
+ else
1197
+ Future::PartiallyFailed.new([value1, value2], [reason1, reason2])
1198
+ end
1199
+ complete_with new_state
1200
+ end
1201
+ end
1202
+
1203
+ # @!visibility private
1204
+ class EventWrapperPromise < BlockedPromise
1205
+ def initialize(event, default_executor)
1206
+ super Event.new(self, default_executor), event, 1
1207
+ end
1208
+
1209
+ def on_completable(done_future)
1210
+ complete_with Event::COMPLETED
1211
+ end
1212
+ end
1213
+
1214
+ # @!visibility private
1215
+ class FutureWrapperPromise < BlockedPromise
1216
+ def initialize(future, default_executor)
1217
+ super Future.new(self, default_executor), future, 1
1218
+ end
1219
+
1220
+ def on_completable(done_future)
1221
+ complete_with done_future.internal_state
1222
+ end
1223
+ end
1224
+
1225
+ # @!visibility private
1226
+ class ZipPromise < BlockedPromise
1058
1227
 
1059
1228
  private
1060
1229
 
@@ -1071,6 +1240,10 @@ module Concurrent
1071
1240
 
1072
1241
  # noinspection RubyArgCount
1073
1242
  super(klass.new(self, default_executor), blocked_by_futures, blocked_by_futures.size)
1243
+
1244
+ if blocked_by_futures.empty?
1245
+ on_completable nil
1246
+ end
1074
1247
  end
1075
1248
 
1076
1249
  def on_completable(done_future)
@@ -1092,13 +1265,16 @@ module Concurrent
1092
1265
 
1093
1266
  if all_success
1094
1267
  if values.empty?
1095
- complete
1268
+ complete_with Event::COMPLETED
1096
1269
  else
1097
- complete(true, values.size == 1 ? values.first : values, nil)
1270
+ if values.size == 1
1271
+ complete_with Future::Success.new(values.first)
1272
+ else
1273
+ complete_with Future::SuccessArray.new(values)
1274
+ end
1098
1275
  end
1099
1276
  else
1100
- # TODO what about other reasons?
1101
- complete(false, nil, reasons.compact.first)
1277
+ complete_with Future::PartiallyFailed.new(values, reasons)
1102
1278
  end
1103
1279
  end
1104
1280
  end
@@ -1119,29 +1295,34 @@ module Concurrent
1119
1295
  end
1120
1296
 
1121
1297
  def on_completable(done_future)
1122
- complete *done_future.result, false
1298
+ complete_with done_future.internal_state, false
1123
1299
  end
1124
1300
  end
1125
1301
 
1126
1302
  # @!visibility private
1127
1303
  class Delay < InnerPromise
1128
1304
  def touch
1129
- if @Args.empty?
1130
- @Future.complete
1131
- else
1132
- @Future.complete(true, @Args, nil)
1133
- end
1305
+ @Future.complete_with Event::COMPLETED
1134
1306
  end
1135
1307
 
1136
1308
  private
1137
1309
 
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)
1310
+ def initialize(default_executor)
1311
+ super Event.new(self, default_executor)
1312
+ end
1313
+ end
1314
+
1315
+ # @!visibility private
1316
+ class DelayValue < InnerPromise
1317
+ def touch
1318
+ @Future.complete_with Future::Success.new(@Value)
1319
+ end
1320
+
1321
+ private
1322
+
1323
+ def initialize(default_executor, value)
1324
+ @Value = value
1325
+ super Future.new(self, default_executor)
1145
1326
  end
1146
1327
  end
1147
1328
 
@@ -1158,7 +1339,7 @@ module Concurrent
1158
1339
 
1159
1340
  private
1160
1341
 
1161
- def initialize(default_executor, intended_time, *args)
1342
+ def initialize(default_executor, intended_time)
1162
1343
  @IntendedTime = intended_time
1163
1344
 
1164
1345
  in_seconds = begin
@@ -1171,19 +1352,10 @@ module Concurrent
1171
1352
  [0, schedule_time.to_f - now.to_f].max
1172
1353
  end
1173
1354
 
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)
1355
+ super Event.new(self, default_executor)
1180
1356
 
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
1357
+ Concurrent.global_timer_set.post(in_seconds) do
1358
+ @Future.complete_with Event::COMPLETED
1187
1359
  end
1188
1360
  end
1189
1361
  end
metadata CHANGED
@@ -1,15 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: concurrent-ruby-edge
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.pre2
4
+ version: 0.1.0.pre3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jerry D'Antonio
8
+ - Petr Chalupa
8
9
  - The Ruby Concurrency Team
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2015-06-08 00:00:00.000000000 Z
13
+ date: 2015-06-22 00:00:00.000000000 Z
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: concurrent-ruby
@@ -17,14 +18,14 @@ dependencies:
17
18
  requirements:
18
19
  - - "~>"
19
20
  - !ruby/object:Gem::Version
20
- version: 0.9.0.pre2
21
+ version: 0.9.0.pre3
21
22
  type: :runtime
22
23
  prerelease: false
23
24
  version_requirements: !ruby/object:Gem::Requirement
24
25
  requirements:
25
26
  - - "~>"
26
27
  - !ruby/object:Gem::Version
27
- version: 0.9.0.pre2
28
+ version: 0.9.0.pre3
28
29
  description: |
29
30
  These features are under active development and may change frequently. They are expected not to
30
31
  keep backward compatibility (there may also lack tests and documentation). Semantic versions will
@@ -102,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
103
  version: 1.3.1
103
104
  requirements: []
104
105
  rubyforge_project:
105
- rubygems_version: 2.4.7
106
+ rubygems_version: 2.4.8
106
107
  signing_key:
107
108
  specification_version: 4
108
109
  summary: Edge features and additions to the concurrent-ruby gem.