On Sun Sep 11 07:01:11 2016, pierre.vig...@gmail.com wrote:
> Hi,
> 
> i stumbled a across a strange behaviour when playing with Supplier, i
> tried to gulf it down, starting from the documentation of supply. The
> following code, taken from the documentation and slightly modified is
> working as expected:
> 
> my $s = Supplier.new;
> my $l = $s.Supply();
> start {
>   sleep 1;
>   $s.emit(1);
>   $s.emit(2);
>   $s.done;
> }
> $l.wait;
> say "done";
> 
> If i tap the $l variable to display the value that are supplied by the
> supplier, it then hangs indefinitely:
> 
> my $s = Supplier.new;
> my $l = $s.Supply();
> $l.tap( -> $v { say "got : $v" } );
> start {
>   sleep 1;
>   $s.emit(1);
>   $s.emit(2);
>   $s.done;
> }
> $l.wait;
> say "done";
> 
> Pierre


I tried to debug this, but ultimately failed, as I'm getting spectest failures.

There are actually three issues:
1) Supply.sanitize uses a per-instance $!finished attribute and when the first 
tap gets closed, no more taps get closed. This is what causes the OP bug.
2) Supply.wait calls Supply.Promise that opens a new tap that keeps/breaks the 
Promise on done/quit. However, if this is done on-already finished Supply, that 
Promise will never complete, causing in a hang in .wait. This is what causes 
the bug if we golf OP's code to get rid of the start {}.
3) To me, it looks like there's a race condition with regards to $!finished, 
since one tap can be setting it to `1`, while another is mid-checking it. Not 
sure if it matters.

Below is my attempt at fixing it that fixes issues #1 and #2 (where .wait on a 
finished supply returns a Nil), however, this has failures in 
t/spec/S17-supply/basic.t because I'm not returning a Tap, I think.

I've also no idea why the created supplies go through the .serialize -> 
.sanitize chain where both methods look nearly identical. I'm leaving this for 
someone more knowledgeable to finish.


----------------------8<-----------------------------------------
diff --git a/src/core/Supply.pm b/src/core/Supply.pm
index d91c3b8..f0fa251 100644
--- a/src/core/Supply.pm
+++ b/src/core/Supply.pm
@@ -197,6 +197,7 @@ my class Supply {
 
             method tap(&emit, &done, &quit) {
                 my int $cleaned-up = 0;
+                return if $!finished;
                 my $source-tap = $!source.tap(
                     -> \value{
                         emit(value) unless $!finished;
@@ -204,16 +205,16 @@ my class Supply {
                     done => -> {
                         unless $!finished {
                             $!finished = 1;
-                            done();
                             self!cleanup($cleaned-up, $source-tap);
                         }
+                        done();
                     },
                     quit => -> $ex {
                         unless $!finished {
                             $!finished = 1;
-                            quit($ex);
                             self!cleanup($cleaned-up, $source-tap);
                         }
+                        quit($ex);
                     });
                 Tap.new({ self!cleanup($cleaned-up, $source-tap) })
             }
@@ -610,10 +611,11 @@ my class Supply {
         my $p = Promise.new;
         my $v = $p.vow;
         my $final := Nil;
-        my $t = self.tap:
+        my $t = self.tap(
             -> \val { $final := val },
             done => { $v.keep($final) },
-            quit => -> \ex { $v.break(ex) };
+            quit => -> \ex { $v.break(ex) },
+        ) or $v.keep(Nil);
         $p
     }
----------------------8<-----------------------------------------



Reply via email to