Skip to content

Commit

Permalink
Report unhandled exceptions of sunk start blocks
Browse files Browse the repository at this point in the history
In 6.d only. They are reported using the `handle_uncaught` method of
the scheduler the code was run under, providing a way to handle such
application-level errors (and also to test this functionality).

Since this is done with the `sink` method, then something like:

    sub foo() { start deadly-stuff() }
    foo();

Will, provided the call `foo()` is itself in sink context, result in
the exception being reported. This is a slight divergence from the
original idea of making it entirely syntactic, but will catch more
cases. Some things can be multiply sunk; there's a check to make sure
we only attach one handler, and this also means we are robust if we
just ever end up sinking a Promise we already did attach a handler to
somehow.

The new adverb to `start`, and indeed Promise construction, is for now
considered an implementation detail, however we might want to consider
it for inclusion in 6.e - perhaps with a different name, but I couldn't
think of anything shorter than communicated what it does decently. So,
for now it's unspecified.
  • Loading branch information
jnthn committed Nov 2, 2018
1 parent 8e858c8 commit 15c16e3
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 11 deletions.
9 changes: 8 additions & 1 deletion src/Perl6/Actions.nqp
Expand Up @@ -2480,13 +2480,20 @@ class Perl6::Actions is HLL::Actions does STDActions {
unless $block.symbol('$!') {
$*W.install_lexical_magical($block, '$!');
}
make QAST::Op.new(
my $qast := QAST::Op.new(
:op('callmethod'),
:name('start'),
:returns($*W.find_symbol(['Promise'])),
QAST::WVal.new( :value($*W.find_symbol(['Promise'])) ),
$<blorst>.ast
);
unless $*W.lang-ver-before('d') {
$qast.push(QAST::WVal.new(
:value($*W.find_symbol(['Bool', 'True'])),
:named('report-broken-if-sunk')
));
}
make $qast;
}

method statement_prefix:sym<lazy>($/) {
Expand Down
28 changes: 18 additions & 10 deletions src/core/Promise.pm6
Expand Up @@ -33,19 +33,20 @@ my class Promise does Awaitable {
has $!cond;
has $!thens;
has Mu $!dynamic_context;
has Bool $!report-broken-if-sunk;

submethod new(:$scheduler = $*SCHEDULER) {
submethod new(:$scheduler = $*SCHEDULER, :$report-broken-if-sunk) {
my \p = nqp::create(self);
p.BUILD(:$scheduler);
p.BUILD(:$scheduler, :$report-broken-if-sunk);
p
}

submethod BUILD(:$scheduler = $*SCHEDULER --> Nil) {
$!scheduler := $scheduler;
$!lock := nqp::create(Lock);
$!cond := $!lock.condition();
$!status := Planned;
$!thens := nqp::list();
submethod BUILD(:$!scheduler = $*SCHEDULER, :$report-broken-if-sunk --> Nil) {
$!report-broken-if-sunk := so $report-broken-if-sunk;
$!lock := nqp::create(Lock);
$!cond := $!lock.condition();
$!status := Planned;
$!thens := nqp::list();
}

# A Vow is used to enable the right to keep/break a promise
Expand Down Expand Up @@ -239,8 +240,15 @@ my class Promise does Awaitable {
}
}

method start(Promise:U: &code, :&catch, :$scheduler = $*SCHEDULER, |c) {
my $p := self.new(:$scheduler);
method sink(--> Nil) {
if $!report-broken-if-sunk && $!lock.protect({ not nqp::elems($!thens) }) {
self.then({ .status == Broken && $!scheduler.handle_uncaught(.cause) });
}
}

method start(Promise:U: &code, :&catch, :$scheduler = $*SCHEDULER,
:$report-broken-if-sunk, |c) {
my $p := self.new(:$scheduler, :$report-broken-if-sunk);
nqp::bindattr($p, Promise, '$!dynamic_context', nqp::ctx());
my $vow := $p.vow;
$scheduler.cue(
Expand Down

0 comments on commit 15c16e3

Please sign in to comment.