aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-12-13 13:07:53 -0200
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-12-13 13:07:53 -0200
commitfdc25a1ebfe9968dcec390dd556375105aa0be40 (patch)
tree43759131636a501ec92475d453fd1a1c73bc8090
parent3b06f983ae0e57b90cdeb500c84bb524e5c3635b (diff)
downloadlua-fdc25a1ebfe9968dcec390dd556375105aa0be40.tar.gz
lua-fdc25a1ebfe9968dcec390dd556375105aa0be40.tar.bz2
lua-fdc25a1ebfe9968dcec390dd556375105aa0be40.zip
New functions 'lua_resetthread' and 'coroutine.kill'
New functions to reset/kill a thread/coroutine, mainly (only?) to close any pending to-be-closed variable. ('lua_resetthread' also allows a thread to be reused...)
-rw-r--r--lcorolib.c58
-rw-r--r--ldo.c4
-rw-r--r--lfunc.c18
-rw-r--r--lfunc.h11
-rw-r--r--lstate.c27
-rw-r--r--ltests.c3
-rw-r--r--lua.h1
-rw-r--r--lvm.c2
-rw-r--r--manual/manual.of34
-rw-r--r--testes/coroutine.lua45
-rw-r--r--testes/main.lua24
11 files changed, 195 insertions, 32 deletions
diff --git a/lcorolib.c b/lcorolib.c
index 34462b53..cdb5fedc 100644
--- a/lcorolib.c
+++ b/lcorolib.c
@@ -107,29 +107,40 @@ static int luaB_yield (lua_State *L) {
107} 107}
108 108
109 109
110static int luaB_costatus (lua_State *L) { 110#define COS_RUN 0
111 lua_State *co = getco(L); 111#define COS_DEAD 1
112 if (L == co) lua_pushliteral(L, "running"); 112#define COS_YIELD 2
113#define COS_NORM 3
114
115
116static const char *statname[] = {"running", "dead", "suspended", "normal"};
117
118
119static int auxstatus (lua_State *L, lua_State *co) {
120 if (L == co) return COS_RUN;
113 else { 121 else {
114 switch (lua_status(co)) { 122 switch (lua_status(co)) {
115 case LUA_YIELD: 123 case LUA_YIELD:
116 lua_pushliteral(L, "suspended"); 124 return COS_YIELD;
117 break;
118 case LUA_OK: { 125 case LUA_OK: {
119 lua_Debug ar; 126 lua_Debug ar;
120 if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */ 127 if (lua_getstack(co, 0, &ar)) /* does it have frames? */
121 lua_pushliteral(L, "normal"); /* it is running */ 128 return COS_NORM; /* it is running */
122 else if (lua_gettop(co) == 0) 129 else if (lua_gettop(co) == 0)
123 lua_pushliteral(L, "dead"); 130 return COS_DEAD;
124 else 131 else
125 lua_pushliteral(L, "suspended"); /* initial state */ 132 return COS_YIELD; /* initial state */
126 break;
127 } 133 }
128 default: /* some error occurred */ 134 default: /* some error occurred */
129 lua_pushliteral(L, "dead"); 135 return COS_DEAD;
130 break;
131 } 136 }
132 } 137 }
138}
139
140
141static int luaB_costatus (lua_State *L) {
142 lua_State *co = getco(L);
143 lua_pushstring(L, statname[auxstatus(L, co)]);
133 return 1; 144 return 1;
134} 145}
135 146
@@ -147,6 +158,28 @@ static int luaB_corunning (lua_State *L) {
147} 158}
148 159
149 160
161static int luaB_kill (lua_State *L) {
162 lua_State *co = getco(L);
163 int status = auxstatus(L, co);
164 switch (status) {
165 case COS_DEAD: case COS_YIELD: {
166 status = lua_resetthread(co);
167 if (status == LUA_OK) {
168 lua_pushboolean(L, 1);
169 return 1;
170 }
171 else {
172 lua_pushboolean(L, 0);
173 lua_xmove(co, L, 1); /* copy error message */
174 return 2;
175 }
176 }
177 default: /* normal or running coroutine */
178 return luaL_error(L, "cannot kill a %s coroutine", statname[status]);
179 }
180}
181
182
150static const luaL_Reg co_funcs[] = { 183static const luaL_Reg co_funcs[] = {
151 {"create", luaB_cocreate}, 184 {"create", luaB_cocreate},
152 {"resume", luaB_coresume}, 185 {"resume", luaB_coresume},
@@ -155,6 +188,7 @@ static const luaL_Reg co_funcs[] = {
155 {"wrap", luaB_cowrap}, 188 {"wrap", luaB_cowrap},
156 {"yield", luaB_yield}, 189 {"yield", luaB_yield},
157 {"isyieldable", luaB_yieldable}, 190 {"isyieldable", luaB_yieldable},
191 {"kill", luaB_kill},
158 {NULL, NULL} 192 {NULL, NULL}
159}; 193};
160 194
diff --git a/ldo.c b/ldo.c
index bdd7fb6d..056fef0c 100644
--- a/ldo.c
+++ b/ldo.c
@@ -98,6 +98,10 @@ void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
98 setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); 98 setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
99 break; 99 break;
100 } 100 }
101 case CLOSEPROTECT: {
102 setnilvalue(s2v(oldtop)); /* no error message */
103 break;
104 }
101 default: { 105 default: {
102 setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ 106 setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
103 break; 107 break;
diff --git a/lfunc.c b/lfunc.c
index 11d2850f..bdf3cd25 100644
--- a/lfunc.c
+++ b/lfunc.c
@@ -127,17 +127,18 @@ static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) {
127 127
128 128
129/* 129/*
130** Prepare and call a closing method. If status is OK, code is 130** Prepare and call a closing method. If status is OK, code is still
131** still inside the original protected call, and so any error 131** inside the original protected call, and so any error will be handled
132** will be handled there. Otherwise, a previous error already 132** there. Otherwise, a previous error already activated original
133** activated original protected call, and so the call to the 133** protected call, and so the call to the closing method must be
134** closing method must be protected here. 134** protected here. (A status = CLOSEPROTECT behaves like a previous
135** error, to also run the closing method in protected mode).
135** If status is OK, the call to the closing method will be pushed 136** If status is OK, the call to the closing method will be pushed
136** at the top of the stack. Otherwise, values are pushed after 137** at the top of the stack. Otherwise, values are pushed after
137** the 'level' of the upvalue being closed, as everything after 138** the 'level' of the upvalue being closed, as everything after
138** that won't be used again. 139** that won't be used again.
139*/ 140*/
140static int closeupval (lua_State *L, TValue *uv, StkId level, int status) { 141static int callclosemth (lua_State *L, TValue *uv, StkId level, int status) {
141 if (likely(status == LUA_OK)) { 142 if (likely(status == LUA_OK)) {
142 if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */ 143 if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */
143 callclose(L, NULL); /* call closing method */ 144 callclose(L, NULL); /* call closing method */
@@ -207,9 +208,10 @@ int luaF_close (lua_State *L, StkId level, int status) {
207 if (!iswhite(uv)) 208 if (!iswhite(uv))
208 gray2black(uv); /* closed upvalues cannot be gray */ 209 gray2black(uv); /* closed upvalues cannot be gray */
209 luaC_barrier(L, uv, slot); 210 luaC_barrier(L, uv, slot);
210 if (status >= 0 && uv->tt == LUA_TUPVALTBC) { /* must be closed? */ 211 if (uv->tt == LUA_TUPVALTBC && status != NOCLOSINGMETH) {
212 /* must run closing method */
211 ptrdiff_t levelrel = savestack(L, level); 213 ptrdiff_t levelrel = savestack(L, level);
212 status = closeupval(L, uv->v, upl, status); /* may realloc. the stack */ 214 status = callclosemth(L, uv->v, upl, status); /* may change the stack */
213 level = restorestack(L, levelrel); 215 level = restorestack(L, levelrel);
214 } 216 }
215 } 217 }
diff --git a/lfunc.h b/lfunc.h
index c9fe1314..0ed79c48 100644
--- a/lfunc.h
+++ b/lfunc.h
@@ -42,6 +42,17 @@
42#define MAXMISS 10 42#define MAXMISS 10
43 43
44 44
45/*
46** Special "status" for 'luaF_close'
47*/
48
49/* close upvalues without running their closing methods */
50#define NOCLOSINGMETH (-1)
51
52/* close upvalues running all closing methods in protected mode */
53#define CLOSEPROTECT (-2)
54
55
45LUAI_FUNC Proto *luaF_newproto (lua_State *L); 56LUAI_FUNC Proto *luaF_newproto (lua_State *L);
46LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nelems); 57LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nelems);
47LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems); 58LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems);
diff --git a/lstate.c b/lstate.c
index 9d399959..5ee024fc 100644
--- a/lstate.c
+++ b/lstate.c
@@ -258,7 +258,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
258 258
259static void close_state (lua_State *L) { 259static void close_state (lua_State *L) {
260 global_State *g = G(L); 260 global_State *g = G(L);
261 luaF_close(L, L->stack, -1); /* close all upvalues for this thread */ 261 luaF_close(L, L->stack, CLOSEPROTECT); /* close all upvalues */
262 luaC_freeallobjects(L); /* collect all objects */ 262 luaC_freeallobjects(L); /* collect all objects */
263 if (ttisnil(&g->nilvalue)) /* closing a fully built state? */ 263 if (ttisnil(&g->nilvalue)) /* closing a fully built state? */
264 luai_userstateclose(L); 264 luai_userstateclose(L);
@@ -301,7 +301,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
301 301
302void luaE_freethread (lua_State *L, lua_State *L1) { 302void luaE_freethread (lua_State *L, lua_State *L1) {
303 LX *l = fromstate(L1); 303 LX *l = fromstate(L1);
304 luaF_close(L1, L1->stack, -1); /* close all upvalues for this thread */ 304 luaF_close(L1, L1->stack, NOCLOSINGMETH); /* close all upvalues */
305 lua_assert(L1->openupval == NULL); 305 lua_assert(L1->openupval == NULL);
306 luai_userstatefree(L, L1); 306 luai_userstatefree(L, L1);
307 freestack(L1); 307 freestack(L1);
@@ -309,6 +309,29 @@ void luaE_freethread (lua_State *L, lua_State *L1) {
309} 309}
310 310
311 311
312int lua_resetthread (lua_State *L) {
313 CallInfo *ci;
314 int status;
315 lua_lock(L);
316 ci = &L->base_ci;
317 status = luaF_close(L, L->stack, CLOSEPROTECT);
318 setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */
319 if (status != CLOSEPROTECT) /* real errors? */
320 luaD_seterrorobj(L, status, L->stack + 1);
321 else {
322 status = LUA_OK;
323 L->top = L->stack + 1;
324 }
325 ci->callstatus = CIST_C;
326 ci->func = L->stack;
327 ci->top = L->top + LUA_MINSTACK;
328 L->ci = ci;
329 L->status = status;
330 lua_unlock(L);
331 return status;
332}
333
334
312LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { 335LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
313 int i; 336 int i;
314 lua_State *L; 337 lua_State *L;
diff --git a/ltests.c b/ltests.c
index 63d423e0..a38a8926 100644
--- a/ltests.c
+++ b/ltests.c
@@ -1366,6 +1366,9 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
1366 else if EQ("newthread") { 1366 else if EQ("newthread") {
1367 lua_newthread(L1); 1367 lua_newthread(L1);
1368 } 1368 }
1369 else if EQ("resetthread") {
1370 lua_pushinteger(L1, lua_resetthread(L1));
1371 }
1369 else if EQ("newuserdata") { 1372 else if EQ("newuserdata") {
1370 lua_newuserdata(L1, getnum); 1373 lua_newuserdata(L1, getnum);
1371 } 1374 }
diff --git a/lua.h b/lua.h
index 6aa184d1..95ce5a2e 100644
--- a/lua.h
+++ b/lua.h
@@ -147,6 +147,7 @@ extern const char lua_ident[];
147LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); 147LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
148LUA_API void (lua_close) (lua_State *L); 148LUA_API void (lua_close) (lua_State *L);
149LUA_API lua_State *(lua_newthread) (lua_State *L); 149LUA_API lua_State *(lua_newthread) (lua_State *L);
150LUA_API int (lua_resetthread) (lua_State *L);
150 151
151LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); 152LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
152 153
diff --git a/lvm.c b/lvm.c
index fc8722a8..652095dc 100644
--- a/lvm.c
+++ b/lvm.c
@@ -1565,7 +1565,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1565 if (nparams1) /* vararg function? */ 1565 if (nparams1) /* vararg function? */
1566 delta = ci->u.l.nextraargs + nparams1; 1566 delta = ci->u.l.nextraargs + nparams1;
1567 /* close upvalues from current call */ 1567 /* close upvalues from current call */
1568 luaF_close(L, base, -1); /* (no to-be-closed vars. here) */ 1568 luaF_close(L, base, LUA_OK);
1569 updatestack(ci); 1569 updatestack(ci);
1570 } 1570 }
1571 if (!ttisfunction(s2v(ra))) { /* not a function? */ 1571 if (!ttisfunction(s2v(ra))) { /* not a function? */
diff --git a/manual/manual.of b/manual/manual.of
index 0e8e3d72..862d032b 100644
--- a/manual/manual.of
+++ b/manual/manual.of
@@ -3927,6 +3927,19 @@ and then pops the top element.
3927 3927
3928} 3928}
3929 3929
3930@APIEntry{int lua_resetthread (lua_State *L);|
3931@apii{0,?,-}
3932
3933Resets a thread, cleaning its call stack and closing all pending
3934to-be-closed variables.
3935Returns a status code:
3936@Lid{LUA_OK} for no errors in closing methods,
3937or an error status otherwise.
3938In case of error,
3939leave the error object on the stack,
3940
3941}
3942
3930@APIEntry{int lua_resume (lua_State *L, lua_State *from, int nargs, 3943@APIEntry{int lua_resume (lua_State *L, lua_State *from, int nargs,
3931 int *nresults);| 3944 int *nresults);|
3932@apii{?,?,-} 3945@apii{?,?,-}
@@ -3948,11 +3961,8 @@ or returned by the body function.
3948@Lid{LUA_OK} if the coroutine finishes its execution 3961@Lid{LUA_OK} if the coroutine finishes its execution
3949without errors, 3962without errors,
3950or an error code in case of errors @seeC{lua_pcall}. 3963or an error code in case of errors @seeC{lua_pcall}.
3951
3952In case of errors, 3964In case of errors,
3953the stack is not unwound, 3965the error object is on the top of the stack.
3954so you can use the debug API over it.
3955The error object is on the top of the stack.
3956 3966
3957To resume a coroutine, 3967To resume a coroutine,
3958you remove all results from the last @Lid{lua_yield}, 3968you remove all results from the last @Lid{lua_yield},
@@ -6285,6 +6295,17 @@ it is not inside a non-yieldable @N{C function}.
6285 6295
6286} 6296}
6287 6297
6298@LibEntry{coroutine.kill(co)|
6299
6300Kills coroutine @id{co},
6301closing all its pending to-be-closed variables
6302and putting the coroutine in a dead state.
6303In case of error closing some variable,
6304returns @false plus the error object;
6305otherwise returns @true.
6306
6307}
6308
6288@LibEntry{coroutine.resume (co [, val1, @Cdots])| 6309@LibEntry{coroutine.resume (co [, val1, @Cdots])|
6289 6310
6290Starts or continues the execution of coroutine @id{co}. 6311Starts or continues the execution of coroutine @id{co}.
@@ -8648,6 +8669,11 @@ has been removed.
8648When needed, this metamethod must be explicitly defined. 8669When needed, this metamethod must be explicitly defined.
8649} 8670}
8650 8671
8672@item{
8673When a coroutine finishes with an error,
8674its stack is unwound (to run any pending closing methods).
8675}
8676
8651} 8677}
8652 8678
8653} 8679}
diff --git a/testes/coroutine.lua b/testes/coroutine.lua
index 7d42eadd..5674a4dd 100644
--- a/testes/coroutine.lua
+++ b/testes/coroutine.lua
@@ -119,6 +119,51 @@ end
119assert(#a == 25 and a[#a] == 97) 119assert(#a == 25 and a[#a] == 97)
120x, a = nil 120x, a = nil
121 121
122
123-- coroutine kill
124do
125 -- ok to kill a dead coroutine
126 local co = coroutine.create(print)
127 assert(coroutine.resume(co, "testing 'coroutine.kill'"))
128 assert(coroutine.status(co) == "dead")
129 assert(coroutine.kill(co))
130
131 -- cannot kill the running coroutine
132 local st, msg = pcall(coroutine.kill, coroutine.running())
133 assert(not st and string.find(msg, "running"))
134
135 local main = coroutine.running()
136
137 -- cannot kill a "normal" coroutine
138 ;(coroutine.wrap(function ()
139 local st, msg = pcall(coroutine.kill, main)
140 assert(not st and string.find(msg, "normal"))
141 end))()
142
143 -- to-be-closed variables in coroutines
144 local X
145 co = coroutine.create(function ()
146 local *toclose x = function (err) assert(err == nil); X = false end
147 X = true
148 coroutine.yield()
149 end)
150 coroutine.resume(co)
151 assert(X)
152 assert(coroutine.kill(co))
153 assert(not X and coroutine.status(co) == "dead")
154
155 -- error killing a coroutine
156 co = coroutine.create(function()
157 local *toclose x = function (err) assert(err == nil); error(111) end
158 coroutine.yield()
159 end)
160 coroutine.resume(co)
161 local st, msg = coroutine.kill(co)
162 assert(not st and coroutine.status(co) == "dead" and msg == 111)
163
164end
165
166
122-- yielding across C boundaries 167-- yielding across C boundaries
123 168
124co = coroutine.wrap(function() 169co = coroutine.wrap(function()
diff --git a/testes/main.lua b/testes/main.lua
index c7bde0d9..b9dcab1c 100644
--- a/testes/main.lua
+++ b/testes/main.lua
@@ -254,15 +254,15 @@ NoRun("error object is a table value", [[lua %s]], prog)
254 254
255 255
256-- chunk broken in many lines 256-- chunk broken in many lines
257s = [=[ -- 257s = [=[ --
258function f ( x ) 258function f ( x )
259 local a = [[ 259 local a = [[
260xuxu 260xuxu
261]] 261]]
262 local b = "\ 262 local b = "\
263xuxu\n" 263xuxu\n"
264 if x == 11 then return 1 + 12 , 2 + 20 end --[[ test multiple returns ]] 264 if x == 11 then return 1 + 12 , 2 + 20 end --[[ test multiple returns ]]
265 return x + 1 265 return x + 1
266 --\\ 266 --\\
267end 267end
268return( f( 100 ) ) 268return( f( 100 ) )
@@ -272,10 +272,10 @@ s = string.gsub(s, ' ', '\n\n') -- change all spaces for newlines
272prepfile(s) 272prepfile(s)
273RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out) 273RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out)
274checkprogout("101\n13\t22\n\n") 274checkprogout("101\n13\t22\n\n")
275 275
276prepfile[[#comment in 1st line without \n at the end]] 276prepfile[[#comment in 1st line without \n at the end]]
277RUN('lua %s', prog) 277RUN('lua %s', prog)
278 278
279prepfile[[#test line number when file starts with comment line 279prepfile[[#test line number when file starts with comment line
280debug = require"debug" 280debug = require"debug"
281print(debug.getinfo(1).currentline) 281print(debug.getinfo(1).currentline)
@@ -306,6 +306,20 @@ NoRun("", "lua %s", prog) -- no message
306prepfile("os.exit(false, true)") 306prepfile("os.exit(false, true)")
307NoRun("", "lua %s", prog) -- no message 307NoRun("", "lua %s", prog) -- no message
308 308
309
310-- to-be-closed variables in main chunk
311prepfile[[
312 local *toclose x = function (err)
313 assert(err == 120)
314 print("Ok")
315 end
316 local *toclose e1 = function () error(120) end
317 os.exit(true, true)
318]]
319RUN('lua %s > %s', prog, out)
320checkprogout("Ok")
321
322
309-- remove temporary files 323-- remove temporary files
310assert(os.remove(prog)) 324assert(os.remove(prog))
311assert(os.remove(otherprog)) 325assert(os.remove(otherprog))