Skip to content

Instantly share code, notes, and snippets.

@timo
Last active December 28, 2015 16:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timo/7526734 to your computer and use it in GitHub Desktop.
Save timo/7526734 to your computer and use it in GitHub Desktop.
implementation of WINNER sub for perl6
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