@@ -83,6 +83,7 @@ def initialize(promise, default_executor = :io)
83
83
@DefaultExecutor = default_executor
84
84
@Touched = AtomicBoolean . new ( false )
85
85
@Callbacks = LockFreeStack . new
86
+ @Waiters = LockFreeStack . new
86
87
@State = AtomicReference . new :pending
87
88
super ( )
88
89
ensure_ivar_visibility!
@@ -174,7 +175,8 @@ def inspect
174
175
# @api private
175
176
def complete ( raise = true )
176
177
if complete_state
177
- synchronize { ns_broadcast }
178
+ # go to synchronized block only if there were waiting threads
179
+ synchronize { ns_broadcast } if @Waiters . clear
178
180
call_callbacks
179
181
else
180
182
Concurrent ::MultipleAssignmentError . new ( 'multiple assignment' ) if raise
@@ -222,8 +224,19 @@ def touched
222
224
private
223
225
224
226
def wait_until_complete ( timeout )
225
- unless completed?
226
- synchronize { ns_wait_until ( timeout ) { completed? } }
227
+ lock = Synchronization ::Lock . new
228
+
229
+ while true
230
+ last_waiter = @Waiters . peek # waiters' state before completion
231
+ break if completed?
232
+
233
+ # synchronize so it cannot be signaled before it waits
234
+ synchronize do
235
+ # ok only if completing thread did not start signaling
236
+ next unless @Waiters . compare_and_push last_waiter , lock
237
+ ns_wait_until ( timeout ) { completed? }
238
+ break
239
+ end
227
240
end
228
241
self
229
242
end
@@ -244,12 +257,11 @@ def pr_callback_on_completion(callback)
244
257
callback . call
245
258
end
246
259
247
- def pr_notify_blocked ( promise )
260
+ def pr_callback_notify_blocked ( promise )
248
261
promise . on_done self
249
262
end
250
263
251
264
def call_callback ( method , *args )
252
- # all methods has to be pure
253
265
self . send method , *args
254
266
end
255
267
@@ -273,7 +285,7 @@ def to_s
273
285
end
274
286
end
275
287
276
- Failed = ImmutableStruct . new :reason do
288
+ Failed = ImmutableStruct . new :reason do
277
289
def value
278
290
nil
279
291
end
@@ -395,9 +407,15 @@ def on_failure!(&callback)
395
407
add_callback :pr_callback_on_failure , callback
396
408
end
397
409
410
+ # @api private
411
+ def apply_value ( value , block )
412
+ block . call value
413
+ end
414
+
398
415
# @api private
399
416
def complete ( success , value , reason , raise = true )
400
417
if complete_state success , value , reason
418
+ @Waiters . clear
401
419
synchronize { ns_broadcast }
402
420
call_callbacks success , value , reason
403
421
else
@@ -414,6 +432,7 @@ def add_callback(method, *args)
414
432
else
415
433
@Callbacks . push [ method , *args ]
416
434
state = self . state
435
+ # take back if it was completed in the meanwhile
417
436
call_callbacks success? ( state ) , state . value , state . reason if completed? ( state )
418
437
end
419
438
self
@@ -439,6 +458,10 @@ def call_callbacks(success, value, reason)
439
458
end
440
459
end
441
460
461
+ def call_callback ( method , success , value , reason , *args )
462
+ self . send method , success , value , reason , *args
463
+ end
464
+
442
465
def pr_async_callback_on_success ( success , value , reason , executor , callback )
443
466
pr_with_async ( executor , success , value , reason , callback ) do |success , value , reason , callback |
444
467
pr_callback_on_success success , value , reason , callback
@@ -452,7 +475,7 @@ def pr_async_callback_on_failure(success, value, reason, executor, callback)
452
475
end
453
476
454
477
def pr_callback_on_success ( success , value , reason , callback )
455
- callback . call value if success
478
+ apply_value value , callback if success
456
479
end
457
480
458
481
def pr_callback_on_failure ( success , value , reason , callback )
@@ -463,7 +486,7 @@ def pr_callback_on_completion(success, value, reason, callback)
463
486
callback . call success , value , reason
464
487
end
465
488
466
- def pr_notify_blocked ( success , value , reason , promise )
489
+ def pr_callback_notify_blocked ( success , value , reason , promise )
467
490
super ( promise )
468
491
end
469
492
@@ -506,11 +529,11 @@ def try_fail(reason = StandardError.new)
506
529
end
507
530
508
531
def evaluate_to ( *args , &block )
509
- promise . evaluate_to ( *args , & block )
532
+ promise . evaluate_to ( *args , block )
510
533
end
511
534
512
535
def evaluate_to! ( *args , &block )
513
- promise . evaluate_to! ( *args , & block )
536
+ promise . evaluate_to! ( *args , block )
514
537
end
515
538
end
516
539
@@ -609,8 +632,8 @@ def try_fail(reason = StandardError.new)
609
632
public :evaluate_to
610
633
611
634
# @return [Future]
612
- def evaluate_to! ( *args , & block )
613
- evaluate_to ( *args , & block ) . wait!
635
+ def evaluate_to! ( *args , block )
636
+ evaluate_to ( *args , block ) . wait!
614
637
end
615
638
end
616
639
@@ -625,7 +648,7 @@ def initialize(future, blocked_by_futures, countdown, &block)
625
648
@Countdown = AtomicFixnum . new countdown
626
649
627
650
super ( future )
628
- blocked_by . each { |f | f . add_callback :pr_notify_blocked , self }
651
+ blocked_by . each { |future | future . add_callback :pr_callback_notify_blocked , self }
629
652
end
630
653
631
654
# @api private
@@ -705,7 +728,9 @@ def initialize(blocked_by_future, default_executor = :io, executor = default_exe
705
728
706
729
def on_completable ( done_future )
707
730
if done_future . success?
708
- Concurrent . post_on ( @Executor , done_future , @Task ) { |done_future , task | evaluate_to done_future . value , &task }
731
+ Concurrent . post_on ( @Executor , done_future , @Task ) do |done_future , task |
732
+ evaluate_to { done_future . apply_value done_future . value , task }
733
+ end
709
734
else
710
735
complete false , nil , done_future . reason
711
736
end
@@ -722,7 +747,7 @@ def initialize(blocked_by_future, default_executor = :io, executor = default_exe
722
747
723
748
def on_completable ( done_future )
724
749
if done_future . failed?
725
- Concurrent . post_on ( @Executor , done_future , @Task ) { |done_future , task | evaluate_to done_future . reason , &task }
750
+ Concurrent . post_on ( @Executor , done_future . reason , @Task ) { |reason , task | evaluate_to reason , &task }
726
751
else
727
752
complete true , done_future . value , nil
728
753
end
@@ -757,12 +782,12 @@ def blocked_by
757
782
758
783
def process_on_done ( future )
759
784
countdown = super ( future )
760
- value = future . value
785
+ value = future . value
761
786
if countdown . nonzero?
762
787
case value
763
788
when Future
764
789
@BlockedBy . push value
765
- value . add_callback :pr_notify_blocked , self
790
+ value . add_callback :pr_callback_notify_blocked , self
766
791
@Countdown . value
767
792
when Event
768
793
raise TypeError , 'cannot flatten to Event'
@@ -797,26 +822,57 @@ def clear_blocked_by!
797
822
798
823
# used internally to support #with_default_executor
799
824
class AllPromise < BlockedPromise
825
+
826
+ class ArrayFuture < Future
827
+ def apply_value ( value , block )
828
+ block . call ( *value )
829
+ end
830
+ end
831
+
800
832
private
801
833
802
834
def initialize ( blocked_by_futures , default_executor = :io )
803
- klass = blocked_by_futures . any? { |f | f . is_a? ( Future ) } ? Future : Event
835
+ klass = Event
836
+ blocked_by_futures . each do |f |
837
+ if f . is_a? ( Future )
838
+ if klass == Event
839
+ klass = Future
840
+ elsif klass == Future
841
+ klass = ArrayFuture
842
+ break
843
+ end
844
+ end
845
+ end
846
+
804
847
# noinspection RubyArgCount
805
848
super ( klass . new ( self , default_executor ) , blocked_by_futures , blocked_by_futures . size )
806
849
end
807
850
808
851
def on_completable ( done_future )
809
- results = blocked_by . select { |f | f . is_a? ( Future ) } . map ( &:result )
810
- if results . empty?
811
- complete
812
- else
813
- if results . all? { |success , _ , _ | success }
814
- params = results . map { |_ , value , _ | value }
815
- complete ( true , params . size == 1 ? params . first : params , nil )
852
+ all_success = true
853
+ reason = nil
854
+
855
+ values = blocked_by . each_with_object ( [ ] ) do |future , values |
856
+ next unless future . is_a? ( Future )
857
+ success , value , reason = future . result
858
+
859
+ unless success
860
+ all_success = false
861
+ reason = reason
862
+ break
863
+ end
864
+ values << value
865
+ end
866
+
867
+ if all_success
868
+ if values . empty?
869
+ complete
816
870
else
817
- # TODO what about other reasons?
818
- complete false , nil , results . find { |success , _ , _ | !success } . last
871
+ complete ( true , values . size == 1 ? values . first : values , nil )
819
872
end
873
+ else
874
+ # TODO what about other reasons?
875
+ complete false , nil , reason
820
876
end
821
877
end
822
878
end
0 commit comments