diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2020-12-30 11:20:22 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2020-12-30 11:20:22 -0300 |
| commit | ce101dcaf73ff6d610593230d41b63c163a91519 (patch) | |
| tree | 6417f02cb96257a835fa908bfea15c2557a41413 | |
| parent | 553b37ce4ff758d8cf80d48a21287526c92221c6 (diff) | |
| download | lua-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'.
Diffstat (limited to '')
| -rw-r--r-- | ldo.c | 66 | ||||
| -rw-r--r-- | testes/coroutine.lua | 41 |
2 files changed, 85 insertions, 22 deletions
| @@ -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 | */ |
| 601 | static void unroll (lua_State *L, void *ud) { | 598 | static 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 | */ |
| 635 | static int recover (lua_State *L, int status) { | 629 | struct 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 | */ | ||
| 642 | static 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 | */ | ||
| 715 | static 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 | |||
| 695 | LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, | 724 | LUA_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) | |||
| 123 | x, a = nil | 123 | x, a = nil |
| 124 | 124 | ||
| 125 | 125 | ||
| 126 | -- coroutine closing | 126 | print("to-be-closed variables in coroutines") |
| 127 | 127 | ||
| 128 | local function func2close (f) | 128 | local 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 | |||
| 193 | end | 192 | end |
| 194 | 193 | ||
| 195 | do | 194 | do |
| @@ -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) | ||
| 210 | end | 247 | end |
| 211 | 248 | ||
| 212 | 249 | ||
