aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-02-12 13:36:30 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-02-12 13:36:30 -0300
commitbc970005ce2e258e29a5c315ea4e49f76a66586e (patch)
tree5575ca66aeddad2036df74f38fa2bed217c2801e
parentf79ccdca9bbe9d486d91a44a4464b99ce38de0e2 (diff)
downloadlua-bc970005ce2e258e29a5c315ea4e49f76a66586e.tar.gz
lua-bc970005ce2e258e29a5c315ea4e49f76a66586e.tar.bz2
lua-bc970005ce2e258e29a5c315ea4e49f76a66586e.zip
'__close' methods can yield in the return of a C function
When, inside a coroutine, a C function with to-be-closed slots return, the corresponding metamethods can yield. ('__close' metamethods called through 'lua_closeslot' still cannot yield, as there is no continuation to go when resuming.)
-rw-r--r--lapi.h2
-rw-r--r--ldo.c72
-rw-r--r--lstate.h12
-rw-r--r--manual/manual.of3
-rw-r--r--testes/locals.lua76
5 files changed, 131 insertions, 34 deletions
diff --git a/lapi.h b/lapi.h
index 41216b27..9e99cc44 100644
--- a/lapi.h
+++ b/lapi.h
@@ -42,6 +42,8 @@
42 42
43#define hastocloseCfunc(n) ((n) < LUA_MULTRET) 43#define hastocloseCfunc(n) ((n) < LUA_MULTRET)
44 44
45/* Map [-1, inf) (range of 'nresults') into (-inf, -2] */
45#define codeNresults(n) (-(n) - 3) 46#define codeNresults(n) (-(n) - 3)
47#define decodeNresults(n) (-(n) - 3)
46 48
47#endif 49#endif
diff --git a/ldo.c b/ldo.c
index bc7212c6..5587b602 100644
--- a/ldo.c
+++ b/ldo.c
@@ -408,24 +408,27 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
408 case LUA_MULTRET: 408 case LUA_MULTRET:
409 wanted = nres; /* we want all results */ 409 wanted = nres; /* we want all results */
410 break; 410 break;
411 default: /* multiple results (or to-be-closed variables) */ 411 default: /* two/more results and/or to-be-closed variables */
412 if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */ 412 if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */
413 ptrdiff_t savedres = savestack(L, res); 413 ptrdiff_t savedres = savestack(L, res);
414 luaF_close(L, res, CLOSEKTOP, 0); /* may change the stack */ 414 L->ci->callstatus |= CIST_CLSRET; /* in case of yields */
415 wanted = codeNresults(wanted); /* correct value */ 415 L->ci->u2.nres = nres;
416 if (wanted == LUA_MULTRET) 416 luaF_close(L, res, CLOSEKTOP, 1);
417 wanted = nres; 417 L->ci->callstatus &= ~CIST_CLSRET;
418 if (L->hookmask) /* if needed, call hook after '__close's */ 418 if (L->hookmask) /* if needed, call hook after '__close's */
419 rethook(L, L->ci, nres); 419 rethook(L, L->ci, nres);
420 res = restorestack(L, savedres); /* close and hook can move stack */ 420 res = restorestack(L, savedres); /* close and hook can move stack */
421 wanted = decodeNresults(wanted);
422 if (wanted == LUA_MULTRET)
423 wanted = nres; /* we want all results */
421 } 424 }
422 break; 425 break;
423 } 426 }
427 /* generic case */
424 firstresult = L->top - nres; /* index of first result */ 428 firstresult = L->top - nres; /* index of first result */
425 /* move all results to correct place */ 429 if (nres > wanted) /* extra results? */
426 if (nres > wanted) 430 nres = wanted; /* don't need them */
427 nres = wanted; /* don't need more than that */ 431 for (i = 0; i < nres; i++) /* move all results to correct place */
428 for (i = 0; i < nres; i++)
429 setobjs2s(L, res + i, firstresult + i); 432 setobjs2s(L, res + i, firstresult + i);
430 for (; i < wanted; i++) /* complete wanted number of results */ 433 for (; i < wanted; i++) /* complete wanted number of results */
431 setnilvalue(s2v(res + i)); 434 setnilvalue(s2v(res + i));
@@ -445,6 +448,9 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
445 rethook(L, ci, nres); 448 rethook(L, ci, nres);
446 /* move results to proper place */ 449 /* move results to proper place */
447 moveresults(L, ci->func, nres, wanted); 450 moveresults(L, ci->func, nres, wanted);
451 /* function cannot be in any of these cases when returning */
452 lua_assert(!(ci->callstatus &
453 (CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET)));
448 L->ci = ci->previous; /* back to caller (after closing variables) */ 454 L->ci = ci->previous; /* back to caller (after closing variables) */
449} 455}
450 456
@@ -615,28 +621,36 @@ static int finishpcallk (lua_State *L, CallInfo *ci) {
615 621
616/* 622/*
617** Completes the execution of a C function interrupted by an yield. 623** Completes the execution of a C function interrupted by an yield.
618** The interruption must have happened while the function was 624** The interruption must have happened while the function was either
619** executing 'lua_callk' or 'lua_pcallk'. In the second case, the 625** closing its tbc variables in 'moveresults' or executing
620** call to 'finishpcallk' finishes the interrupted execution of 626** 'lua_callk'/'lua_pcallk'. In the first case, it just redoes
621** 'lua_pcallk'. After that, it calls the continuation of the 627** 'luaD_poscall'. In the second case, the call to 'finishpcallk'
622** interrupted function and finally it completes the job of the 628** finishes the interrupted execution of 'lua_pcallk'. After that, it
623** 'luaD_call' that called the function. 629** calls the continuation of the interrupted function and finally it
624** In the call to 'adjustresults', we do not know the number of 630** completes the job of the 'luaD_call' that called the function. In
625** results of the function called by 'lua_callk'/'lua_pcallk', 631** the call to 'adjustresults', we do not know the number of results
626** so we are conservative and use LUA_MULTRET (always adjust). 632** of the function called by 'lua_callk'/'lua_pcallk', so we are
633** conservative and use LUA_MULTRET (always adjust).
627*/ 634*/
628static void finishCcall (lua_State *L, CallInfo *ci) { 635static void finishCcall (lua_State *L, CallInfo *ci) {
629 int n; 636 int n; /* actual number of results from C function */
630 int status = LUA_YIELD; /* default if there were no errors */ 637 if (ci->callstatus & CIST_CLSRET) { /* was returning? */
631 /* must have a continuation and must be able to call it */ 638 lua_assert(hastocloseCfunc(ci->nresults));
632 lua_assert(ci->u.c.k != NULL && yieldable(L)); 639 n = ci->u2.nres; /* just redo 'luaD_poscall' */
633 if (ci->callstatus & CIST_YPCALL) /* was inside a 'lua_pcallk'? */ 640 /* don't need to reset CIST_CLSRET, as it will be set again anyway */
634 status = finishpcallk(L, ci); /* finish it */ 641 }
635 adjustresults(L, LUA_MULTRET); /* finish 'lua_callk' */ 642 else {
636 lua_unlock(L); 643 int status = LUA_YIELD; /* default if there were no errors */
637 n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation */ 644 /* must have a continuation and must be able to call it */
638 lua_lock(L); 645 lua_assert(ci->u.c.k != NULL && yieldable(L));
639 api_checknelems(L, n); 646 if (ci->callstatus & CIST_YPCALL) /* was inside a 'lua_pcallk'? */
647 status = finishpcallk(L, ci); /* finish it */
648 adjustresults(L, LUA_MULTRET); /* finish 'lua_callk' */
649 lua_unlock(L);
650 n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation */
651 lua_lock(L);
652 api_checknelems(L, n);
653 }
640 luaD_poscall(L, ci, n); /* finish 'luaD_call' */ 654 luaD_poscall(L, ci, n); /* finish 'luaD_call' */
641} 655}
642 656
diff --git a/lstate.h b/lstate.h
index b6ade7c7..0322e2c6 100644
--- a/lstate.h
+++ b/lstate.h
@@ -164,6 +164,8 @@ typedef struct stringtable {
164** protected call; 164** protected call;
165** - field 'nyield' is used only while a function is "doing" an 165** - field 'nyield' is used only while a function is "doing" an
166** yield (from the yield until the next resume); 166** yield (from the yield until the next resume);
167** - field 'nres' is used only while closing tbc variables when
168** returning from a C function;
167** - field 'transferinfo' is used only during call/returnhooks, 169** - field 'transferinfo' is used only during call/returnhooks,
168** before the function starts or after it ends. 170** before the function starts or after it ends.
169*/ 171*/
@@ -186,6 +188,7 @@ typedef struct CallInfo {
186 union { 188 union {
187 int funcidx; /* called-function index */ 189 int funcidx; /* called-function index */
188 int nyield; /* number of values yielded */ 190 int nyield; /* number of values yielded */
191 int nres; /* number of values returned */
189 struct { /* info about transferred values (for call/return hooks) */ 192 struct { /* info about transferred values (for call/return hooks) */
190 unsigned short ftransfer; /* offset of first value transferred */ 193 unsigned short ftransfer; /* offset of first value transferred */
191 unsigned short ntransfer; /* number of values transferred */ 194 unsigned short ntransfer; /* number of values transferred */
@@ -203,15 +206,16 @@ typedef struct CallInfo {
203#define CIST_C (1<<1) /* call is running a C function */ 206#define CIST_C (1<<1) /* call is running a C function */
204#define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */ 207#define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */
205#define CIST_HOOKED (1<<3) /* call is running a debug hook */ 208#define CIST_HOOKED (1<<3) /* call is running a debug hook */
206#define CIST_YPCALL (1<<4) /* call is a yieldable protected call */ 209#define CIST_YPCALL (1<<4) /* doing a yieldable protected call */
207#define CIST_TAIL (1<<5) /* call was tail called */ 210#define CIST_TAIL (1<<5) /* call was tail called */
208#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */ 211#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */
209#define CIST_FIN (1<<7) /* call is running a finalizer */ 212#define CIST_FIN (1<<7) /* call is running a finalizer */
210#define CIST_TRAN (1<<8) /* 'ci' has transfer information */ 213#define CIST_TRAN (1<<8) /* 'ci' has transfer information */
211/* Bits 9-11 are used for CIST_RECST (see below) */ 214#define CIST_CLSRET (1<<9) /* function is closing tbc variables */
212#define CIST_RECST 9 215/* Bits 10-12 are used for CIST_RECST (see below) */
216#define CIST_RECST 10
213#if defined(LUA_COMPAT_LT_LE) 217#if defined(LUA_COMPAT_LT_LE)
214#define CIST_LEQ (1<<12) /* using __lt for __le */ 218#define CIST_LEQ (1<<13) /* using __lt for __le */
215#endif 219#endif
216 220
217 221
diff --git a/manual/manual.of b/manual/manual.of
index 89069281..e7040b2b 100644
--- a/manual/manual.of
+++ b/manual/manual.of
@@ -3102,6 +3102,9 @@ Close the to-be-closed slot at the given index and set its value to @nil.
3102The index must be the last index previously marked to be closed 3102The index must be the last index previously marked to be closed
3103@see{lua_toclose} that is still active (that is, not closed yet). 3103@see{lua_toclose} that is still active (that is, not closed yet).
3104 3104
3105A @Lid{__close} metamethod cannot yield
3106when called through this function.
3107
3105(Exceptionally, this function was introduced in release 5.4.3. 3108(Exceptionally, this function was introduced in release 5.4.3.
3106It is not present in previous 5.4 releases.) 3109It is not present in previous 5.4 releases.)
3107 3110
diff --git a/testes/locals.lua b/testes/locals.lua
index 446ec13a..a93839db 100644
--- a/testes/locals.lua
+++ b/testes/locals.lua
@@ -707,7 +707,6 @@ if rawget(_G, "T") then
707 -- results are correct 707 -- results are correct
708 checktable(t, {10, 20}) 708 checktable(t, {10, 20})
709 end 709 end
710
711end 710end
712 711
713 712
@@ -930,6 +929,81 @@ assert(co == nil) -- eventually it will be collected
930collectgarbage() 929collectgarbage()
931 930
932 931
932if rawget(_G, "T") then
933 print("to-be-closed variables x coroutines in C")
934 do
935 local token = 0
936 local count = 0
937 local f = T.makeCfunc[[
938 toclose 1
939 toclose 2
940 return .
941 ]]
942
943 local obj = func2close(function (_, msg)
944 count = count + 1
945 token = coroutine.yield(count, token)
946 end)
947
948 local co = coroutine.wrap(f)
949 local ct, res = co(obj, obj, 10, 20, 30, 3) -- will return 10, 20, 30
950 -- initial token value, after closing 2nd obj
951 assert(ct == 1 and res == 0)
952 -- run until yield when closing 1st obj
953 ct, res = co(100)
954 assert(ct == 2 and res == 100)
955 res = {co(200)} -- run until end
956 assert(res[1] == 10 and res[2] == 20 and res[3] == 30 and res[4] == nil)
957 assert(token == 200)
958 end
959
960 do
961 local f = T.makeCfunc[[
962 toclose 1
963 return .
964 ]]
965
966 local obj = func2close(function ()
967 local temp
968 local x <close> = func2close(function ()
969 coroutine.yield(temp)
970 return 1,2,3 -- to be ignored
971 end)
972 temp = coroutine.yield("closing obj")
973 return 1,2,3 -- to be ignored
974 end)
975
976 local co = coroutine.wrap(f)
977 local res = co(obj, 10, 30, 1) -- will return only 30
978 assert(res == "closing obj")
979 res = co("closing x")
980 assert(res == "closing x")
981 res = {co()}
982 assert(res[1] == 30 and res[2] == nil)
983 end
984
985 do
986 -- still cannot yield inside 'closeslot'
987 local f = T.makeCfunc[[
988 toclose 1
989 closeslot 1
990 ]]
991 local obj = func2close(coroutine.yield)
992 local co = coroutine.create(f)
993 local st, msg = coroutine.resume(co, obj)
994 assert(not st and string.find(msg, "attempt to yield across"))
995
996 -- nor outside a coroutine
997 local f = T.makeCfunc[[
998 toclose 1
999 ]]
1000 local st, msg = pcall(f, obj)
1001 assert(not st and string.find(msg, "attempt to yield from outside"))
1002 end
1003end
1004
1005
1006
933-- to-be-closed variables in generic for loops 1007-- to-be-closed variables in generic for loops
934do 1008do
935 local numopen = 0 1009 local numopen = 0