An implementation here, which simply stops execution.
It doesn't provide the `async sub` any way to implement ->on_cancel behaviours yet. Further thought needs to be had on a good interface for that, but for now this patch is enough for cases that don't need such.
--
Paul Evans
=== modified file 'lib/Future/AsyncAwait.xs'
--- lib/Future/AsyncAwait.xs 2019-04-17 22:59:36 +0000
+++ lib/Future/AsyncAwait.xs 2019-04-18 10:55:35 +0000
@@ -1350,29 +1350,30 @@
return f;
}
-#define future_is_ready(f) MY_future_is_ready(aTHX_ f)
-static int MY_future_is_ready(pTHX_ SV *f)
+#define future_is_ready(f) MY_future_check(aTHX_ f, "is_ready")
+#define future_is_cancelled(f) MY_future_check(aTHX_ f, "is_cancelled")
+static bool MY_future_check(pTHX_ SV *f, const char *method)
{
dSP;
- ENTER_with_name("future_is_ready");
+ ENTER_with_name("future_check");
SAVETMPS;
PUSHMARK(SP);
XPUSHs(f);
PUTBACK;
- call_method("is_ready", G_SCALAR);
+ call_method(method, G_SCALAR);
SPAGAIN;
- int is_ready = POPi;
+ bool ret = SvTRUEx(POPs);
PUTBACK;
FREETMPS;
- LEAVE_with_name("future_is_ready");
+ LEAVE_with_name("future_check");
- return is_ready;
+ return ret;
}
#define future_get_to_stack(f, gimme) MY_future_get_to_stack(aTHX_ f, gimme)
@@ -1408,6 +1409,23 @@
LEAVE_with_name("future_on_ready");
}
+#define future_chain_on_cancel(f1, f2) MY_future_chain_on_cancel(aTHX_ f1, f2)
+static void MY_future_chain_on_cancel(pTHX_ SV *f1, SV *f2)
+{
+ dSP;
+
+ ENTER_with_name("future_chain_on_cancel");
+
+ PUSHMARK(SP);
+ XPUSHs(f1);
+ XPUSHs(f2);
+ PUTBACK;
+
+ call_method("on_cancel", G_VOID);
+
+ LEAVE_with_name("future_chain_on_cancel");
+}
+
/*
* Custom ops
*/
@@ -1491,6 +1509,11 @@
TRACEPRINT("ENTER await curcv=%p [%s:%d]\n", curcv, CopFILE(curcop), CopLINE(curcop));
if(state && state->awaiting_future) {
+ if(future_is_cancelled(state->returning_future)) {
+ TRACEPRINT(" CANCELLED\n");
+ return PL_ppaddr[OP_RETURN](aTHX);
+ }
+
I32 orig_height;
TRACEPRINT(" RESUME\n");
@@ -1582,6 +1605,8 @@
if(!SvWEAKREF(state->returning_future))
sv_rvweaken(state->returning_future);
+ future_chain_on_cancel(state->returning_future, state->awaiting_future);
+
TRACEPRINT("LEAVE await curcv=%p [%s:%d]\n", curcv, CopFILE(curcop), CopLINE(curcop));
return PL_ppaddr[OP_RETURN](aTHX);
=== added file 't/08await-cancel.t'
--- t/08await-cancel.t 1970-01-01 00:00:00 +0000
+++ t/08await-cancel.t 2019-04-18 10:44:16 +0000
@@ -0,0 +1,43 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+
+use Future;
+
+use Future::AsyncAwait;
+
+my $orig_cxstack_ix = Future::AsyncAwait::__cxstack_ix;
+
+# ->cancel stops execution
+{
+ my $called;
+
+ my $f1 = Future->new;
+ my $f2 = (async sub {
+ await $f1;
+ $called++;
+ })->();
+
+ $f2->cancel;
+ $f1->done;
+
+ ok( !$called, 'async sub stops execution after ->cancel' );
+}
+
+# ->cancel propagates
+{
+ my $f1 = Future->new;
+ my $f2 = (async sub { await $f1 })->();
+
+ $f2->cancel;
+
+ ok( $f1->is_cancelled, 'async sub propagates cancel' );
+}
+
+is( Future::AsyncAwait::__cxstack_ix, $orig_cxstack_ix,
+ 'cxstack_ix did not grow during the test' );
+
+done_testing;