Skip Menu |

This queue is for tickets about the Future-AsyncAwait CPAN distribution.

Report information
The Basics
Id: 129303
Status: resolved
Priority: 0/
Queue: Future-AsyncAwait

People
Owner: Nobody in particular
Requestors: leonerd-cpan [...] leonerd.org.uk
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: 0.24
Fixed in: 0.25



Subject: panic: ARGG we lost state->returning_future
I have a reproduction case, but tracking it down seems tricky: $ perl -MDevel::MAT::Dumper=-dump_at_SIGABRT -Mblib examples/dac.pl --adapter BusPirate --chip DAC7571 128 Future::AsyncAwait panic: ARGH we lost state->returning_future for curcv=0x55ef19cac640 Future::AsyncAwait panic: ARGH future_check() on undefined value Future::AsyncAwait panic: ARGH future_check() on non-reference Can't call method "is_cancelled" on an undefined value at /home/leo/lib/perl5/Future.pm line 462. Dumping to /home/leo/src/perl/Device-Chip-AnalogConverters/dac.pl.pmat because of SIGABRT TODO: savestack type=25 Aborted $ pmat dac.pl.pmat Perl memory dumpfile from perl 5.28.1 threaded Heap contains 30216 objects Show quoted text
pmat> show 0x55ef19cac640
No such SV at address 55ef19cac640 -- Paul Evans
On Wed Apr 24 08:55:00 2019, PEVANS wrote: Show quoted text
> I have a reproduction case, but tracking it down seems tricky:
Ah; it needs SIGABRT to be using an unsafe signal handler in Devel::MAT::Dumper. Having added that I now get: $ perl -MDevel::MAT::Dumper=-dump_at_SIGABRT -Mblib examples/dac.pl --adapter BusPirate --chip DAC7571 128 Future::AsyncAwait panic: ARGH we lost state->returning_future for curcv=0x5620db383c38 Dumping to /home/leo/src/perl/Device-Chip-AnalogConverters/dac.pl.pmat because of SIGABRT Aborted $ pmat ../Device-Chip-AnalogConverters/dac.pl.pmat Perl memory dumpfile from perl 5.28.1 threaded Heap contains 31084 objects Show quoted text
pmat> show 0x5620db383c38
CODE(PP) at 0x5620db383c38 with refcount 3 size 128 bytes named as &Device::BusPirate::write_expect_acked_data has ~ magic no hekname stash=STASH(29) at 0x5620db906db0 glob=GLOB(&*) at 0x5620dbb6ccb0 location=/home/leo/lib/perl5/Device/BusPirate.pm scope=CODE() at 0x5620db906cf0 no padlist no padnames_av pad[0]=PAD(25) at 0x5620dbc8ad68 Referenced globs: GLOB($@I) at 0x5620db34c1e8=defgv, Show quoted text
pmat> identify 0x5620db383c38
CODE(PP) at 0x5620db383c38 is: ├─(via RV) element [1] of ARRAY(2) at 0x5620dbc859a0, which is: │ └─the referrant of REF() at 0x5620dbc85fa0, which is: │ ├─element [0] of ARRAY(1) at 0x5620dbc87958, which is: │ │ └─(via RV) the lexical $callbacks at depth 3 of CODE(PP) at 0x5620db71f870, which is: │ │ └─the symbol '&Future::_mark_ready' │ └─the lexical $cb at depth 3 of CODE(PP) at 0x5620db71f870, which is: │ └─the symbol '&Future::_mark_ready' └─(via RV) the lexical $code at depth 3 of CODE(PP) at 0x5620db71f870, which is: └─the symbol '&Future::_mark_ready' Show quoted text
pmat> outrefs 0x5620db383c38
... s the awaiting Future REF(W) at 0x5620dbc857a8 s the returning Future UNDEF() at 0x5620db90e508 -- Paul Evans
This error is caused by trying to resume a suspended sub by completing its await future, while its return future has been abandoned. It's quite similar to what happens if you do $f2 = $f1->then(sub { ... }); undef $f1; Attached patch fixes this by turning the error into a type of "cancel" case, but printing a warning (similar to the "lost sequence future" warning of Future.pm), as doing so should always be considered a bug, though its something we can just warn about and carry on. -- Paul Evans
Subject: rt129303.patch
=== modified file 'lib/Future/AsyncAwait.xs' --- lib/Future/AsyncAwait.xs 2019-04-18 22:34:47 +0000 +++ lib/Future/AsyncAwait.xs 2019-04-24 14:28:45 +0000 @@ -142,14 +142,11 @@ typedef struct { SV *awaiting_future; /* the Future that 'await' is currently waiting for */ SV *returning_future; /* the Future that its contining CV will eventually return */ + COP *curcop; /* value of PL_curcop at suspend time */ SuspendedFrame *frames; U32 padlen; SV **padslots; - -#ifdef DEBUG - COP *curcop; -#endif } SuspendedState; #ifdef DEBUG @@ -1576,16 +1573,10 @@ CV *curcv = find_runcv(0); CV *origcv = curcv; - COP *curcop = PL_curcop; /* just for debug printing purposes */ bool defer_mortal_curcv = FALSE; SuspendedState *state = suspendedstate_get(curcv); -#ifdef DEBUG - if(state && state->curcop) - curcop = state->curcop; -#endif - if(state && state->awaiting_future && CATCH_GET) { /* If we don't do this we get all the mess that is * https://rt.cpan.org/Ticket/Display.html?id=126037 @@ -1593,13 +1584,21 @@ return docatch(pp_await); } - TRACEPRINT("ENTER await curcv=%p [%s:%d]\n", curcv, CopFILE(curcop), CopLINE(curcop)); + if(state && state->curcop) + PL_curcop = state->curcop; + + TRACEPRINT("ENTER await curcv=%p [%s:%d]\n", curcv, CopFILE(PL_curcop), CopLINE(PL_curcop)); if(state && state->awaiting_future) { - if(!SvROK(state->returning_future)) - panic("ARGG we lost state->returning_future\n"); + if(!SvROK(state->returning_future) || future_is_cancelled(state->returning_future)) { + if(!SvROK(state->returning_future)) { + GV *gv = CvGV(curcv); + if(!CvANON(curcv)) + warn("Suspended async sub %s::%s lost its returning future", HvNAME(GvSTASH(gv)), GvNAME(gv)); + else + warn("Suspended async sub CODE(0x%p) in package %s lost its returning future", curcv, HvNAME(GvSTASH(gv))); + } - if(future_is_cancelled(state->returning_future)) { TRACEPRINT(" CANCELLED\n"); PUSHMARK(SP); @@ -1648,13 +1647,11 @@ if(future_is_ready(f)) { assert(CvDEPTH(curcv) > 0); TRACEPRINT(" READY\n"); -#ifdef DEBUG if(state) state->curcop = NULL; -#endif /* This might throw */ future_get_to_stack(f, GIMME_V); - TRACEPRINT("LEAVE await curcv=%p [%s:%d]\n", curcv, CopFILE(curcop), CopLINE(curcop)); + TRACEPRINT("LEAVE await curcv=%p [%s:%d]\n", curcv, CopFILE(PL_curcop), CopLINE(PL_curcop)); return PL_op->op_next; } @@ -1674,9 +1671,7 @@ TRACEPRINT(" SUSPEND reuse CV\n"); } -#ifdef DEBUG - state->curcop = curcop; -#endif + state->curcop = PL_curcop; suspendedstate_suspend(state, origcv); @@ -1699,7 +1694,7 @@ if(!SvWEAKREF(state->returning_future)) sv_rvweaken(state->returning_future); if(!SvROK(state->returning_future)) - panic("ARGH we lost the ->returning_future\n"); + panic("ARGH we lost state->returning_future for curcv=%p\n", curcv); /* For unknown reasons, doing this on perls 5.20 or 5.22 massively breaks * everything. @@ -1710,11 +1705,11 @@ #endif if(!SvROK(state->returning_future)) - panic("ARGH we lost the ->returning_future\n"); + panic("ARGH we lost state->returning_future for curcv=%p\n", curcv); if(!SvROK(state->awaiting_future)) - panic("ARGH we lost the ->awaiting_future\n"); + panic("ARGH we lost state->awaiting_future for curcv=%p\n", curcv); - TRACEPRINT("LEAVE await curcv=%p [%s:%d]\n", curcv, CopFILE(curcop), CopLINE(curcop)); + TRACEPRINT("LEAVE await curcv=%p [%s:%d]\n", curcv, CopFILE(PL_curcop), CopLINE(PL_curcop)); return PL_ppaddr[OP_RETURN](aTHX); } === modified file 't/42unresolved.t' --- t/42unresolved.t 2019-03-21 22:22:45 +0000 +++ t/42unresolved.t 2019-04-24 14:31:04 +0000 @@ -10,6 +10,7 @@ use Future::AsyncAwait; my $orig_cxstack_ix = Future::AsyncAwait::__cxstack_ix; +my $file = quotemeta __FILE__; async sub identity { @@ -25,12 +26,47 @@ $x + 1 + [ "a", await identity $f ]; } -# unresolved -foreach ( 1 .. 1023 ) { +# abandoned chain +{ my $f1 = Future->new; my $fret = func( $f1, 1, 2 ); undef $fret; + pass( 'abandoned chain does not crash' ); +} + +# abandoned subsequent (RT129303) +{ + my $f1 = Future->new; + my $fret = func( $f1, 3, 4 ); + + undef $fret; + + my $warnings = ""; + { + local $SIG{__WARN__} = sub { $warnings .= join "", @_ }; + $f1->done; + } + pass( 'abandoned subsequent does not crash' ); + like( $warnings, qr/^Suspended async sub main::func lost its returning future at $file line \d+/, + 'warning from attempted resume' ); +} + +# abandoned subsequent on anon sub +{ + my $f1 = Future->new; + my $fret = (async sub { await $f1 })->(); + + undef $fret; + + my $warnings = ""; + { + local $SIG{__WARN__} = sub { $warnings .= join "", @_ }; + $f1->done; + } + pass( 'abandoned subsequent does not crash' ); + like( $warnings, qr/^Suspended async sub CODE\(0x[0-9a-f]+\) in package main lost its returning future at $file line \d+/, + 'warning from attempted resume' ); } is( Future::AsyncAwait::__cxstack_ix, $orig_cxstack_ix,