aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-12-30 11:20:22 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-12-30 11:20:22 -0300
commitce101dcaf73ff6d610593230d41b63c163a91519 (patch)
tree6417f02cb96257a835fa908bfea15c2557a41413
parent553b37ce4ff758d8cf80d48a21287526c92221c6 (diff)
downloadlua-ce101dcaf73ff6d610593230d41b63c163a91519.tar.gz
lua-ce101dcaf73ff6d610593230d41b63c163a91519.tar.bz2
lua-ce101dcaf73ff6d610593230d41b63c163a91519.zip
Handles '__close' errors in coroutines in "coroutine style"
Errors in '__close' metamethods in coroutines are handled by the same logic that handles other errors, through 'recover'.
-rw-r--r--ldo.c66
-rw-r--r--testes/coroutine.lua41
2 files changed, 85 insertions, 22 deletions
diff --git a/ldo.c b/ldo.c
index 5e3828f4..ba0c93b8 100644
--- a/ldo.c
+++ b/ldo.c
@@ -103,7 +103,7 @@ void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
103 break; 103 break;
104 } 104 }
105 default: { 105 default: {
106 lua_assert(errcode >= LUA_ERRRUN); /* real error */ 106 lua_assert(errorstatus(errcode)); /* real error */
107 setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ 107 setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
108 break; 108 break;
109 } 109 }
@@ -593,15 +593,11 @@ static void finishCcall (lua_State *L, int status) {
593/* 593/*
594** Executes "full continuation" (everything in the stack) of a 594** Executes "full continuation" (everything in the stack) of a
595** previously interrupted coroutine until the stack is empty (or another 595** previously interrupted coroutine until the stack is empty (or another
596** interruption long-jumps out of the loop). If the coroutine is 596** interruption long-jumps out of the loop).
597** recovering from an error, 'ud' points to the error status, which must
598** be passed to the first continuation function (otherwise the default
599** status is LUA_YIELD).
600*/ 597*/
601static void unroll (lua_State *L, void *ud) { 598static void unroll (lua_State *L, void *ud) {
602 CallInfo *ci; 599 CallInfo *ci;
603 if (ud != NULL) /* error status? */ 600 UNUSED(ud);
604 finishCcall(L, *(int *)ud); /* finish 'lua_pcallk' callee */
605 while ((ci = L->ci) != &L->base_ci) { /* something in the stack */ 601 while ((ci = L->ci) != &L->base_ci) { /* something in the stack */
606 if (!isLua(ci)) /* C function? */ 602 if (!isLua(ci)) /* C function? */
607 finishCcall(L, LUA_YIELD); /* complete its execution */ 603 finishCcall(L, LUA_YIELD); /* complete its execution */
@@ -628,21 +624,36 @@ static CallInfo *findpcall (lua_State *L) {
628 624
629 625
630/* 626/*
631** Recovers from an error in a coroutine. Finds a recover point (if 627** Auxiliary structure to call 'recover' in protected mode.
632** there is one) and completes the execution of the interrupted
633** 'luaD_pcall'. If there is no recover point, returns zero.
634*/ 628*/
635static int recover (lua_State *L, int status) { 629struct RecoverS {
636 CallInfo *ci = findpcall(L); 630 int status;
637 if (ci == NULL) return 0; /* no recovery point */ 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);
638 /* "finish" luaD_pcall */ 647 /* "finish" luaD_pcall */
639 L->ci = ci; 648 L->ci = ci;
640 L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */ 649 L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */
641 status = luaD_closeprotected(L, ci->u2.funcidx, status); 650 luaF_close(L, func, status); /* may change the stack */
642 luaD_seterrorobj(L, status, restorestack(L, ci->u2.funcidx)); 651 func = restorestack(L, ci->u2.funcidx);
652 luaD_seterrorobj(L, status, func);
643 luaD_shrinkstack(L); /* restore stack size in case of overflow */ 653 luaD_shrinkstack(L); /* restore stack size in case of overflow */
644 L->errfunc = ci->u.c.old_errfunc; 654 L->errfunc = ci->u.c.old_errfunc;
645 return 1; /* continue running the coroutine */ 655 finishCcall(L, status); /* finish 'lua_pcallk' callee */
656 unroll(L, NULL); /* continue running the coroutine */
646} 657}
647 658
648 659
@@ -692,6 +703,24 @@ static void resume (lua_State *L, void *ud) {
692 } 703 }
693} 704}
694 705
706
707/*
708** Calls 'recover' in protected mode, repeating while there are
709** recoverable errors, that is, errors inside a protected call. (Any
710** error interrupts 'recover', and this loop protects it again so it
711** can continue.) Stops with a normal end (status == LUA_OK), an yield
712** (status == LUA_YIELD), or an unprotected error ('findpcall' doesn't
713** find a recover point).
714*/
715static int p_recover (lua_State *L, int status) {
716 struct RecoverS r;
717 r.status = status;
718 while (errorstatus(status) && (r.ci = findpcall(L)) != NULL)
719 r.status = luaD_rawrunprotected(L, recover, &r);
720 return r.status;
721}
722
723
695LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, 724LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
696 int *nresults) { 725 int *nresults) {
697 int status; 726 int status;
@@ -709,10 +738,7 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
709 api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs); 738 api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
710 status = luaD_rawrunprotected(L, resume, &nargs); 739 status = luaD_rawrunprotected(L, resume, &nargs);
711 /* continue running after recoverable errors */ 740 /* continue running after recoverable errors */
712 while (errorstatus(status) && recover(L, status)) { 741 status = p_recover(L, status);
713 /* unroll continuation */
714 status = luaD_rawrunprotected(L, unroll, &status);
715 }
716 if (likely(!errorstatus(status))) 742 if (likely(!errorstatus(status)))
717 lua_assert(status == L->status); /* normal end or yield */ 743 lua_assert(status == L->status); /* normal end or yield */
718 else { /* unrecoverable error */ 744 else { /* unrecoverable error */
diff --git a/testes/coroutine.lua b/testes/coroutine.lua
index 0a970e98..fbeabd07 100644
--- a/testes/coroutine.lua
+++ b/testes/coroutine.lua
@@ -123,7 +123,7 @@ assert(#a == 22 and a[#a] == 79)
123x, a = nil 123x, a = nil
124 124
125 125
126-- coroutine closing 126print("to-be-closed variables in coroutines")
127 127
128local function func2close (f) 128local function func2close (f)
129 return setmetatable({}, {__close = f}) 129 return setmetatable({}, {__close = f})
@@ -189,7 +189,6 @@ do
189 local st, msg = coroutine.close(co) 189 local st, msg = coroutine.close(co)
190 assert(st == false and coroutine.status(co) == "dead" and msg == 200) 190 assert(st == false and coroutine.status(co) == "dead" and msg == 200)
191 assert(x == 200) 191 assert(x == 200)
192
193end 192end
194 193
195do 194do
@@ -207,6 +206,44 @@ do
207 local st1, st2, err = coroutine.resume(co) 206 local st1, st2, err = coroutine.resume(co)
208 assert(st1 and not st2 and err == 43) 207 assert(st1 and not st2 and err == 43)
209 assert(X == 43 and Y.name == "pcall") 208 assert(X == 43 and Y.name == "pcall")
209
210 -- recovering from errors in __close metamethods
211 local track = {}
212
213 local function h (o)
214 local hv <close> = o
215 return 1
216 end
217
218 local function foo ()
219 local x <close> = func2close(function(_,msg)
220 track[#track + 1] = msg or false
221 error(20)
222 end)
223 local y <close> = func2close(function(_,msg)
224 track[#track + 1] = msg or false
225 return 1000
226 end)
227 local z <close> = func2close(function(_,msg)
228 track[#track + 1] = msg or false
229 error(10)
230 end)
231 coroutine.yield(1)
232 h(func2close(function(_,msg)
233 track[#track + 1] = msg or false
234 error(2)
235 end))
236 end
237
238 local co = coroutine.create(pcall)
239
240 local st, res = coroutine.resume(co, foo) -- call 'foo' protected
241 assert(st and res == 1) -- yield 1
242 local st, res1, res2 = coroutine.resume(co) -- continue
243 assert(coroutine.status(co) == "dead")
244 assert(st and not res1 and res2 == 20) -- last error (20)
245 assert(track[1] == false and track[2] == 2 and track[3] == 10 and
246 track[4] == 10)
210end 247end
211 248
212 249