Last active
December 28, 2015 16:09
-
-
Save timo/7526734 to your computer and use it in GitHub Desktop.
implementation of WINNER sub for perl6
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
my constant $WINNER_KIND_DONE = 0; | |
my constant $WINNER_KIND_MORE = 1; | |
sub WINNER(@winner_args, *@pieces, :$wild_done, :$wild_more, :$later) { | |
my Int $num_pieces = +@pieces div 3; | |
sub invoke_right(&block, $key, $value?) { | |
my @names = map *.name, &block.signature.params; | |
say @names; | |
return do if @names eqv ['$k', '$v'] || @names eqv ['$v', '$k'] { | |
&block(:k($key), :v($value)); | |
} elsif @names eqv ['$_'] { | |
&block($value); | |
} elsif @names eqv ['$k'] { | |
&block(:k($key)); | |
} elsif @names eqv ['$v'] { | |
&block(:v($value)); | |
} else { | |
say "couldn't figure out how to invoke {&block.signature().perl}"; | |
} | |
} | |
# if we don't have a last block, we need to retry until we | |
# have a winner. | |
loop { | |
my @promises_only; | |
my Bool $has_channels = False; | |
if $num_pieces > 0 { | |
for (^$num_pieces).pick(*) -> $index { | |
my ($kind, $arg, &block) = @pieces[$index * 3, $index * 3 + 1, $index * 3 + 2]; | |
if $kind == $WINNER_KIND_DONE { | |
if $arg ~~ Promise { | |
if $arg { | |
return invoke_right(&block, $arg, $arg.result); | |
} | |
@promises_only.push: $arg; | |
} elsif $arg ~~ Channel { | |
if $arg.closed { | |
return invoke_right(&block, $arg); | |
} | |
$has_channels = True; | |
} else { | |
die "Got a {$arg.WHAT.perl}, but expected a Channel or Promise."; | |
} | |
} elsif $kind == $WINNER_KIND_MORE { | |
if $arg ~~ Channel { | |
if (my $val := $arg.poll) !~~ Nil { | |
return invoke_right(&block, $arg, $val); | |
} | |
$has_channels = True; | |
} elsif $arg ~~ Promise { | |
die "cannot use 'more' on a Promise."; | |
} else { | |
die "Got a {$arg.WHAT.perl}, but expected a Channel or Promise."; | |
} | |
} | |
} | |
if $later { | |
return $later(); | |
} | |
} else { | |
for @winner_args.pick(*) { | |
when Channel { | |
if (my $val := $_.poll()) !~~ Nil { | |
return invoke_right($wild_more, $_, $val); | |
} elsif $_.closed.has_value { | |
return $wild_done(:k($_)); | |
} | |
$has_channels = True; | |
} | |
when Promise { | |
if $_ { | |
return invoke_right($wild_done, $_, $_.result); | |
} | |
@promises_only.push: $_; | |
} | |
default { | |
die "Got a {$_.WHAT.perl}, but expected a Channel or Promise."; | |
} | |
} | |
# when we hit this, none of the promises or channels | |
# have given us a result. if we have a later closure, | |
# we immediately return, otherwise we block on any | |
# of the promises of our args. | |
if $later { | |
return $later(); | |
} | |
# if we only had promises, we can block on "anyof". | |
} | |
if $has_channels { | |
Thread.yield(); | |
} else { | |
Promise.anyof(@promises_only).result; | |
} | |
} | |
} | |
sub MAIN('test') { | |
{ | |
my $p1 = Promise.new(); | |
my $p2 = Promise.new(); | |
my $p3 = Promise.new(); | |
my $p4 = Promise.in(3); | |
say WINNER([], | |
$WINNER_KIND_DONE, $p1, { say "promise one: $_" }, | |
$WINNER_KIND_DONE, $p2, { say "promise two: $_" }, | |
$WINNER_KIND_DONE, $p3, { say "promise three: $_" }, | |
$WINNER_KIND_DONE, $p4, { say "timeout promise finally matched!" } | |
); | |
for $p1, $p2, $p3 { | |
$_.keep: (^20).pick; | |
} | |
say WINNER([], | |
$WINNER_KIND_DONE, $p1, { say "promise one: $_" }, | |
$WINNER_KIND_DONE, $p2, { say "promise two: $_" }, | |
$WINNER_KIND_DONE, $p3, { say "promise three: $_" }, | |
:later({ say "nothing matched" })); | |
say WINNER([$p1, $p2, $p3], | |
:wild_done({ say "wildcard done: $_" }), | |
:later({ say "nothing matched" })); | |
} | |
{ | |
my $chan_a = Channel.new(); | |
my $chan_b = Channel.new(); | |
start { | |
for ^5 { | |
$chan_a.send("a: {(^20).pick}"); | |
$chan_b.send("b: {(^20).pick}"); | |
sleep 0.1; | |
} | |
$chan_b.close; | |
sleep 0.1; | |
$chan_a.close; | |
} | |
for ^12 { | |
say WINNER([], | |
$WINNER_KIND_MORE, $chan_a, { say "channel $:k, value $:v" }, | |
$WINNER_KIND_MORE, $chan_b, { say "channel $:k, value $:v" }, | |
$WINNER_KIND_DONE, $chan_a, { say "done $:k" }, | |
$WINNER_KIND_DONE, $chan_b, { say "done $:k" }); | |
} | |
} | |
{ | |
my $chan_a = Channel.new(); | |
my $chan_b = Channel.new(); | |
my $promise = Promise.new(); | |
start { | |
for ^5 { | |
$chan_a.send("a: {(^20).pick}"); | |
$chan_b.send("b: {(^20).pick}"); | |
sleep 0.1; | |
if (True, False, False).pick { | |
$promise.keep((^100).pick); | |
} | |
} | |
$chan_b.close; | |
sleep 0.1; | |
$chan_a.close; | |
unless $promise { | |
$promise.keep((^100).pick); | |
} | |
} | |
for ^15 { | |
say WINNER([], | |
$WINNER_KIND_MORE, $chan_a, { say "channel $:k, value $:v" }, | |
$WINNER_KIND_MORE, $chan_b, { say "channel $:k, value $:v" }, | |
$WINNER_KIND_DONE, $chan_a, { say "done $:k" }, | |
$WINNER_KIND_DONE, $chan_b, { say "done $:k" }, | |
$WINNER_KIND_DONE, $promise, { say "the promise has been kept! $:v" }); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment