aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-01-18 11:40:45 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-01-18 11:40:45 -0300
commitd0f34d91373fa265d4445e456e4a10ce206c1559 (patch)
tree0389b97b7844634144158df06040ea89c63fda46
parent825ac8eca8e384d6ad2538b5670088c31e08a9d7 (diff)
downloadlua-d0f34d91373fa265d4445e456e4a10ce206c1559.tar.gz
lua-d0f34d91373fa265d4445e456e4a10ce206c1559.tar.bz2
lua-d0f34d91373fa265d4445e456e4a10ce206c1559.zip
Allow yields in '__close' metamethods ater errors
Completes commit b07fc10e91a. '__close' metamethods can yield even when they are being called due to an error. '__close' metamethods from C functions are still not allowed to yield.
-rw-r--r--ldo.c127
-rw-r--r--lstate.h22
-rw-r--r--testes/locals.lua48
3 files changed, 126 insertions, 71 deletions
diff --git a/ldo.c b/ldo.c
index aa159cf0..45cfd592 100644
--- a/ldo.c
+++ b/ldo.c
@@ -565,25 +565,64 @@ void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
565 565
566 566
567/* 567/*
568** Completes the execution of an interrupted C function, calling its 568** Finish the job of 'lua_pcallk' after it was interrupted by an yield.
569** continuation function. 569** (The caller, 'finishCcall', does the final call to 'adjustresults'.)
570** The main job is to complete the 'luaD_pcall' called by 'lua_pcallk'.
571** If a '__close' method yields here, eventually control will be back
572** to 'finishCcall' (when that '__close' method finally returns) and
573** 'finishpcallk' will run again and close any still pending '__close'
574** methods. Similarly, if a '__close' method errs, 'precover' calls
575** 'unroll' which calls ''finishCcall' and we are back here again, to
576** close any pending '__close' methods.
577** Note that, up to the call to 'luaF_close', the corresponding
578** 'CallInfo' is not modified, so that this repeated run works like the
579** first one (except that it has at least one less '__close' to do). In
580** particular, field CIST_RECST preserves the error status across these
581** multiple runs, changing only if there is a new error.
570*/ 582*/
571static void finishCcall (lua_State *L, int status) { 583static int finishpcallk (lua_State *L, CallInfo *ci) {
572 CallInfo *ci = L->ci; 584 int status = getcistrecst(ci); /* get original status */
585 if (status == LUA_OK) /* no error? */
586 status = LUA_YIELD; /* was interrupted by an yield */
587 else { /* error */
588 StkId func = restorestack(L, ci->u2.funcidx);
589 L->allowhook = getoah(ci->callstatus); /* restore 'allowhook' */
590 luaF_close(L, func, status, 1); /* can yield or raise an error */
591 func = restorestack(L, ci->u2.funcidx); /* stack may be moved */
592 luaD_seterrorobj(L, status, func);
593 luaD_shrinkstack(L); /* restore stack size in case of overflow */
594 setcistrecst(ci, LUA_OK); /* clear original status */
595 }
596 ci->callstatus &= ~CIST_YPCALL;
597 L->errfunc = ci->u.c.old_errfunc;
598 /* if it is here, there were errors or yields; unlike 'lua_pcallk',
599 do not change status */
600 return status;
601}
602
603
604/*
605** Completes the execution of a C function interrupted by an yield.
606** The interruption must have happened while the function was
607** executing 'lua_callk' or 'lua_pcallk'. In the second case, the
608** call to 'finishpcallk' finishes the interrupted execution of
609** 'lua_pcallk'. After that, it calls the continuation of the
610** interrupted function and finally it completes the job of the
611** 'luaD_call' that called the function.
612** In the call to 'adjustresults', we do not know the number of
613** results of the function called by 'lua_callk'/'lua_pcallk',
614** so we are conservative and use LUA_MULTRET (always adjust).
615*/
616static void finishCcall (lua_State *L, CallInfo *ci) {
573 int n; 617 int n;
618 int status = LUA_YIELD; /* default if there were no errors */
574 /* must have a continuation and must be able to call it */ 619 /* must have a continuation and must be able to call it */
575 lua_assert(ci->u.c.k != NULL && yieldable(L)); 620 lua_assert(ci->u.c.k != NULL && yieldable(L));
576 /* error status can only happen in a protected call */ 621 if (ci->callstatus & CIST_YPCALL) /* was inside a 'lua_pcallk'? */
577 lua_assert((ci->callstatus & CIST_YPCALL) || status == LUA_YIELD); 622 status = finishpcallk(L, ci); /* finish it */
578 if (ci->callstatus & CIST_YPCALL) { /* was inside a pcall? */ 623 adjustresults(L, LUA_MULTRET); /* finish 'lua_callk' */
579 ci->callstatus &= ~CIST_YPCALL; /* continuation is also inside it */
580 L->errfunc = ci->u.c.old_errfunc; /* with the same error function */
581 }
582 /* finish 'lua_callk'/'lua_pcall'; CIST_YPCALL and 'errfunc' already
583 handled */
584 adjustresults(L, ci->nresults);
585 lua_unlock(L); 624 lua_unlock(L);
586 n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation function */ 625 n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation */
587 lua_lock(L); 626 lua_lock(L);
588 api_checknelems(L, n); 627 api_checknelems(L, n);
589 luaD_poscall(L, ci, n); /* finish 'luaD_call' */ 628 luaD_poscall(L, ci, n); /* finish 'luaD_call' */
@@ -600,7 +639,7 @@ static void unroll (lua_State *L, void *ud) {
600 UNUSED(ud); 639 UNUSED(ud);
601 while ((ci = L->ci) != &L->base_ci) { /* something in the stack */ 640 while ((ci = L->ci) != &L->base_ci) { /* something in the stack */
602 if (!isLua(ci)) /* C function? */ 641 if (!isLua(ci)) /* C function? */
603 finishCcall(L, LUA_YIELD); /* complete its execution */ 642 finishCcall(L, ci); /* complete its execution */
604 else { /* Lua function */ 643 else { /* Lua function */
605 luaV_finishOp(L); /* finish interrupted instruction */ 644 luaV_finishOp(L); /* finish interrupted instruction */
606 luaV_execute(L, ci); /* execute down to higher C 'boundary' */ 645 luaV_execute(L, ci); /* execute down to higher C 'boundary' */
@@ -624,40 +663,6 @@ static CallInfo *findpcall (lua_State *L) {
624 663
625 664
626/* 665/*
627** Auxiliary structure to call 'recover' in protected mode.
628*/
629struct RecoverS {
630 int status;
631 CallInfo *ci;
632};
633
634
635/*
636** Recovers from an error in a coroutine: completes the execution of the
637** interrupted 'luaD_pcall', completes the interrupted C function which
638** called 'lua_pcallk', and continues running the coroutine. If there is
639** an error in 'luaF_close', this function will be called again and the
640** coroutine will continue from where it left.
641*/
642static void recover (lua_State *L, void *ud) {
643 struct RecoverS *r = cast(struct RecoverS *, ud);
644 int status = r->status;
645 CallInfo *ci = r->ci; /* recover point */
646 StkId func = restorestack(L, ci->u2.funcidx);
647 /* "finish" luaD_pcall */
648 L->ci = ci;
649 L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */
650 luaF_close(L, func, status, 0); /* may change the stack */
651 func = restorestack(L, ci->u2.funcidx);
652 luaD_seterrorobj(L, status, func);
653 luaD_shrinkstack(L); /* restore stack size in case of overflow */
654 L->errfunc = ci->u.c.old_errfunc;
655 finishCcall(L, status); /* finish 'lua_pcallk' callee */
656 unroll(L, NULL); /* continue running the coroutine */
657}
658
659
660/*
661** Signal an error in the call to 'lua_resume', not in the execution 666** Signal an error in the call to 'lua_resume', not in the execution
662** of the coroutine itself. (Such errors should not be handled by any 667** of the coroutine itself. (Such errors should not be handled by any
663** coroutine error handler and should not kill the coroutine.) 668** coroutine error handler and should not kill the coroutine.)
@@ -705,19 +710,21 @@ static void resume (lua_State *L, void *ud) {
705 710
706 711
707/* 712/*
708** Calls 'recover' in protected mode, repeating while there are 713** Unrolls a coroutine in protected mode while there are recoverable
709** recoverable errors, that is, errors inside a protected call. (Any 714** errors, that is, errors inside a protected call. (Any error
710** error interrupts 'recover', and this loop protects it again so it 715** interrupts 'unroll', and this loop protects it again so it can
711** can continue.) Stops with a normal end (status == LUA_OK), an yield 716** continue.) Stops with a normal end (status == LUA_OK), an yield
712** (status == LUA_YIELD), or an unprotected error ('findpcall' doesn't 717** (status == LUA_YIELD), or an unprotected error ('findpcall' doesn't
713** find a recover point). 718** find a recover point).
714*/ 719*/
715static int p_recover (lua_State *L, int status) { 720static int precover (lua_State *L, int status) {
716 struct RecoverS r; 721 CallInfo *ci;
717 r.status = status; 722 while (errorstatus(status) && (ci = findpcall(L)) != NULL) {
718 while (errorstatus(status) && (r.ci = findpcall(L)) != NULL) 723 L->ci = ci; /* go down to recovery functions */
719 r.status = luaD_rawrunprotected(L, recover, &r); 724 setcistrecst(ci, status); /* status to finish 'pcall' */
720 return r.status; 725 status = luaD_rawrunprotected(L, unroll, NULL);
726 }
727 return status;
721} 728}
722 729
723 730
@@ -738,7 +745,7 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
738 api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs); 745 api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
739 status = luaD_rawrunprotected(L, resume, &nargs); 746 status = luaD_rawrunprotected(L, resume, &nargs);
740 /* continue running after recoverable errors */ 747 /* continue running after recoverable errors */
741 status = p_recover(L, status); 748 status = precover(L, status);
742 if (likely(!errorstatus(status))) 749 if (likely(!errorstatus(status)))
743 lua_assert(status == L->status); /* normal end or yield */ 750 lua_assert(status == L->status); /* normal end or yield */
744 else { /* unrecoverable error */ 751 else { /* unrecoverable error */
diff --git a/lstate.h b/lstate.h
index 38a6c9b6..38248e57 100644
--- a/lstate.h
+++ b/lstate.h
@@ -191,17 +191,33 @@ typedef struct CallInfo {
191*/ 191*/
192#define CIST_OAH (1<<0) /* original value of 'allowhook' */ 192#define CIST_OAH (1<<0) /* original value of 'allowhook' */
193#define CIST_C (1<<1) /* call is running a C function */ 193#define CIST_C (1<<1) /* call is running a C function */
194#define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */ 194#define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */
195#define CIST_HOOKED (1<<3) /* call is running a debug hook */ 195#define CIST_HOOKED (1<<3) /* call is running a debug hook */
196#define CIST_YPCALL (1<<4) /* call is a yieldable protected call */ 196#define CIST_YPCALL (1<<4) /* call is a yieldable protected call */
197#define CIST_TAIL (1<<5) /* call was tail called */ 197#define CIST_TAIL (1<<5) /* call was tail called */
198#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */ 198#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */
199#define CIST_FIN (1<<7) /* call is running a finalizer */ 199#define CIST_FIN (1<<7) /* call is running a finalizer */
200#define CIST_TRAN (1<<8) /* 'ci' has transfer information */ 200#define CIST_TRAN (1<<8) /* 'ci' has transfer information */
201/* Bits 9-11 are used for CIST_RECST (see below) */
202#define CIST_RECST 9
201#if defined(LUA_COMPAT_LT_LE) 203#if defined(LUA_COMPAT_LT_LE)
202#define CIST_LEQ (1<<9) /* using __lt for __le */ 204#define CIST_LEQ (1<<12) /* using __lt for __le */
203#endif 205#endif
204 206
207
208/*
209** Field CIST_RECST stores the "recover status", used to keep the error
210** status while closing to-be-closed variables in coroutines, so that
211** Lua can correctly resume after an yield from a __close method called
212** because of an error. (Three bits are enough for error status.)
213*/
214#define getcistrecst(ci) (((ci)->callstatus >> CIST_RECST) & 7)
215#define setcistrecst(ci,st) \
216 check_exp(((st) & 7) == (st), /* status must fit in three bits */ \
217 ((ci)->callstatus = ((ci)->callstatus & ~(7 << CIST_RECST)) \
218 | ((st) << CIST_RECST)))
219
220
205/* active function is a Lua function */ 221/* active function is a Lua function */
206#define isLua(ci) (!((ci)->callstatus & CIST_C)) 222#define isLua(ci) (!((ci)->callstatus & CIST_C))
207 223
diff --git a/testes/locals.lua b/testes/locals.lua
index c9c93ccf..8506195e 100644
--- a/testes/locals.lua
+++ b/testes/locals.lua
@@ -697,34 +697,66 @@ end
697 697
698 698
699do 699do
700 -- yielding inside closing metamethods after an error: 700 -- yielding inside closing metamethods after an error
701 -- not yet implemented; raises an error
702 701
703 local co = coroutine.wrap(function () 702 local co = coroutine.wrap(function ()
704 703
705 local function foo (err) 704 local function foo (err)
706 705
706 local z <close> = func2close(function(_, msg)
707 assert(msg == nil or msg == err + 20)
708 coroutine.yield("z")
709 return 100, 200
710 end)
711
712 local y <close> = func2close(function(_, msg)
713 -- still gets the original error (if any)
714 assert(msg == err or (msg == nil and err == 1))
715 coroutine.yield("y")
716 if err then error(err + 20) end -- creates or changes the error
717 end)
718
707 local x <close> = func2close(function(_, msg) 719 local x <close> = func2close(function(_, msg)
708 assert(msg == err) 720 assert(msg == err or (msg == nil and err == 1))
709 coroutine.yield("x") 721 coroutine.yield("x")
710 return 100, 200 722 return 100, 200
711 end) 723 end)
712 724
713 if err then error(err) else return 10, 20 end 725 if err == 10 then error(err) else return 10, 20 end
714 end 726 end
715 727
716 coroutine.yield(pcall(foo, nil)) -- no error 728 coroutine.yield(pcall(foo, nil)) -- no error
729 coroutine.yield(pcall(foo, 1)) -- error in __close
717 return pcall(foo, 10) -- 'foo' will raise an error 730 return pcall(foo, 10) -- 'foo' will raise an error
718 end) 731 end)
719 732
720 local a, b = co() 733 local a, b = co() -- first foo: no error
721 assert(a == "x" and b == nil) -- yields inside 'x'; Ok 734 assert(a == "x" and b == nil) -- yields inside 'x'; Ok
722 735 a, b = co()
736 assert(a == "y" and b == nil) -- yields inside 'y'; Ok
737 a, b = co()
738 assert(a == "z" and b == nil) -- yields inside 'z'; Ok
723 local a, b, c = co() 739 local a, b, c = co()
724 assert(a and b == 10 and c == 20) -- returns from 'pcall(foo, nil)' 740 assert(a and b == 10 and c == 20) -- returns from 'pcall(foo, nil)'
725 741
726 local st, msg = co() -- error yielding after an error 742 local a, b = co() -- second foo: error in __close
727 assert(not st and string.find(msg, "attempt to yield")) 743 assert(a == "x" and b == nil) -- yields inside 'x'; Ok
744 a, b = co()
745 assert(a == "y" and b == nil) -- yields inside 'y'; Ok
746 a, b = co()
747 assert(a == "z" and b == nil) -- yields inside 'z'; Ok
748 local st, msg = co() -- reports the error in 'y'
749 assert(not st and msg == 21)
750
751 local a, b = co() -- third foo: error in function body
752 assert(a == "x" and b == nil) -- yields inside 'x'; Ok
753 a, b = co()
754 assert(a == "y" and b == nil) -- yields inside 'y'; Ok
755 a, b = co()
756 assert(a == "z" and b == nil) -- yields inside 'z'; Ok
757 local st, msg = co() -- gets final error
758 assert(not st and msg == 10 + 20)
759
728end 760end
729 761
730 762