Skip to content

Commit

Permalink
Merge pull request #3139 from dumarchie/master
Browse files Browse the repository at this point in the history
 Throw if keep/break is invoked on a vow more than once. Exception of type `X::Promise::IllegalTransition` would be thrown if `keep`/`break` are invoked on a vow taken on a promise that is not `Planned`.

This should resolve /issues/3137.

Note for Blin runs: this merge is likely to reveal a few vow misuses (i.e. repeated status change of a same `Promise`).
  • Loading branch information
vrurg committed Sep 7, 2019
2 parents 9dd67cb + b8f92d2 commit 6b2739a
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 18 deletions.
36 changes: 19 additions & 17 deletions src/core.c/Channel.pm6
Expand Up @@ -56,15 +56,13 @@ my class Channel does Awaitable {
nqp::istype(msg,CHANNEL_CLOSE),
nqp::stmts(
nqp::push($!queue, msg), # make sure other readers see it
$!closed_promise_vow.keep(Nil),
X::Channel::ReceiveOnClosed.new(channel => self).throw
),
nqp::if(
nqp::istype(msg,CHANNEL_FAIL),
nqp::stmts(
nqp::push($!queue,msg), # make sure other readers see it
$!closed_promise_vow.break(my $error := msg.error),
$error.rethrow
msg.error.rethrow
),
nqp::stmts(
self!peek(), # trigger promise if closed
Expand All @@ -81,14 +79,12 @@ my class Channel does Awaitable {
nqp::if(
nqp::istype(msg, CHANNEL_CLOSE),
nqp::stmts(
$!closed_promise_vow.keep(Nil),
nqp::push($!queue, msg),
Nil
),
nqp::if(
nqp::istype(msg, CHANNEL_FAIL),
nqp::stmts(
$!closed_promise_vow.break(msg.error),
nqp::push($!queue, msg),
Nil
),
Expand All @@ -107,11 +103,11 @@ my class Channel does Awaitable {
Nil
} else {
if nqp::istype(msg, CHANNEL_CLOSE) {
$!closed_promise_vow.keep(Nil);
try $!closed_promise_vow.keep(Nil);
Nil
}
elsif nqp::istype(msg, CHANNEL_FAIL) {
$!closed_promise_vow.break(msg.error);
try $!closed_promise_vow.break(msg.error);
Nil
}
else {
Expand Down Expand Up @@ -166,36 +162,42 @@ my class Channel does Awaitable {
}
}

my class Iterate { ... }
trusts Iterate;
my class Iterate does Iterator {
has $!queue;
has $!vow;
method !SET-SELF(\queue,\vow) {
has $!channel;
method !SET-SELF(\queue,\channel) {
$!queue := queue;
$!vow := vow;
$!channel := channel;
self
}
method new(\queue,\vow) { nqp::create(self)!SET-SELF(queue,vow) }
method new(\queue,\channel) {
nqp::create(self)!SET-SELF(queue,channel);
}
method pull-one() {
my \msg := nqp::shift($!queue);
nqp::if(
nqp::istype((my \msg := nqp::shift($!queue)),CHANNEL_CLOSE),
nqp::istype(msg,CHANNEL_CLOSE),
nqp::stmts(
nqp::push($!queue,msg), # make sure other readers see it
$!vow.keep(Nil),
IterationEnd
),
nqp::if(
nqp::istype(msg,CHANNEL_FAIL),
nqp::stmts(
nqp::push($!queue,msg), # make sure other readers see it
$!vow.break(my $error := msg.error),
$error.rethrow
msg.error.rethrow
),
msg
nqp::stmts(
$!channel!Channel::peek(), # trigger promise if closed
msg
)
)
)
}
}
method iterator(Channel:D:) { Iterate.new($!queue,$!closed_promise_vow) }
method iterator(Channel:D:) { Iterate.new($!queue,self) }

method list(Channel:D:) { self.Seq.list }

Expand Down
2 changes: 1 addition & 1 deletion src/core.c/IO/Socket/Async.pm6
Expand Up @@ -166,7 +166,7 @@ my class IO::Socket::Async {

method close(IO::Socket::Async:D: --> True) {
nqp::closefh($!VMIO);
$!close-vow.keep(True);
try $!close-vow.keep(True);
}

method connect(IO::Socket::Async:U: Str() $host, Int() $port where Port-Number,
Expand Down
12 changes: 12 additions & 0 deletions src/core.c/Promise.pm6
Expand Up @@ -15,6 +15,12 @@ my class X::Promise::Vowed is Exception {
has $.promise;
method message() { "Access denied to keep/break this Promise; already vowed" }
}
my class X::Promise::Resolved is Exception {
has $.promise;
method message() {
"Cannot keep/break a Promise more than once (status: $!promise.status())";
}
}
my role X::Promise::Broken {
has $.result-backtrace;
multi method gist(::?CLASS:D:) {
Expand Down Expand Up @@ -96,6 +102,9 @@ my class Promise does Awaitable {

method !keep(Mu \result --> Nil) {
$!lock.protect({
X::Promise::Resolved.new(promise => self).throw
if $!status != Planned;

$!result := result;
$!status := Kept;
self!schedule_thens();
Expand Down Expand Up @@ -125,6 +134,9 @@ my class Promise does Awaitable {

method !break(\result --> Nil) {
$!lock.protect({
X::Promise::Resolved.new(promise => self).throw
if $!status != Planned;

$!result := nqp::istype(result, Exception)
?? result
!! X::AdHoc.new(payload => result);
Expand Down

0 comments on commit 6b2739a

Please sign in to comment.