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<-----------------------------------------