aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-11-08 11:55:25 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-11-08 11:55:25 -0300
commitbfbff3703edae789fa5efa9bf174f8e7cff4ded8 (patch)
tree5e8466f78f52b5d53691f800354a3aed313d11a9
parent74d99057a5146755e737c479850f87fd0e3b6868 (diff)
downloadlua-bfbff3703edae789fa5efa9bf174f8e7cff4ded8.tar.gz
lua-bfbff3703edae789fa5efa9bf174f8e7cff4ded8.tar.bz2
lua-bfbff3703edae789fa5efa9bf174f8e7cff4ded8.zip
Bug: Wrong status in coroutine during reset
When closing variables during 'coroutine.close' or 'lua_resetthread', the status of a coroutine must be set to LUA_OK; a coroutine should not run with any other status. (See assertion in 'lua_callk'.) After the reset, the status should be kept as normal, as any error was already reported.
-rw-r--r--lcorolib.c4
-rw-r--r--lstate.c4
-rw-r--r--testes/coroutine.lua44
3 files changed, 47 insertions, 5 deletions
diff --git a/lcorolib.c b/lcorolib.c
index fedbebec..785a1e81 100644
--- a/lcorolib.c
+++ b/lcorolib.c
@@ -78,7 +78,7 @@ static int luaB_auxwrap (lua_State *L) {
78 if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */ 78 if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */
79 stat = lua_resetthread(co); /* close its tbc variables */ 79 stat = lua_resetthread(co); /* close its tbc variables */
80 lua_assert(stat != LUA_OK); 80 lua_assert(stat != LUA_OK);
81 lua_xmove(co, L, 1); /* copy error message */ 81 lua_xmove(co, L, 1); /* move error message to the caller */
82 } 82 }
83 if (stat != LUA_ERRMEM && /* not a memory error and ... */ 83 if (stat != LUA_ERRMEM && /* not a memory error and ... */
84 lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */ 84 lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */
@@ -179,7 +179,7 @@ static int luaB_close (lua_State *L) {
179 } 179 }
180 else { 180 else {
181 lua_pushboolean(L, 0); 181 lua_pushboolean(L, 0);
182 lua_xmove(co, L, 1); /* copy error message */ 182 lua_xmove(co, L, 1); /* move error message */
183 return 2; 183 return 2;
184 } 184 }
185 } 185 }
diff --git a/lstate.c b/lstate.c
index bfc59026..5cb0847c 100644
--- a/lstate.c
+++ b/lstate.c
@@ -166,7 +166,7 @@ void luaE_checkcstack (lua_State *L) {
166 if (getCcalls(L) == LUAI_MAXCCALLS) 166 if (getCcalls(L) == LUAI_MAXCCALLS)
167 luaG_runerror(L, "C stack overflow"); 167 luaG_runerror(L, "C stack overflow");
168 else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11)) 168 else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11))
169 luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ 169 luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
170} 170}
171 171
172 172
@@ -330,13 +330,13 @@ int luaE_resetthread (lua_State *L, int status) {
330 ci->callstatus = CIST_C; 330 ci->callstatus = CIST_C;
331 if (status == LUA_YIELD) 331 if (status == LUA_YIELD)
332 status = LUA_OK; 332 status = LUA_OK;
333 L->status = LUA_OK; /* so it can run __close metamethods */
333 status = luaD_closeprotected(L, 1, status); 334 status = luaD_closeprotected(L, 1, status);
334 if (status != LUA_OK) /* errors? */ 335 if (status != LUA_OK) /* errors? */
335 luaD_seterrorobj(L, status, L->stack + 1); 336 luaD_seterrorobj(L, status, L->stack + 1);
336 else 337 else
337 L->top = L->stack + 1; 338 L->top = L->stack + 1;
338 ci->top = L->top + LUA_MINSTACK; 339 ci->top = L->top + LUA_MINSTACK;
339 L->status = cast_byte(status);
340 luaD_reallocstack(L, cast_int(ci->top - L->stack), 0); 340 luaD_reallocstack(L, cast_int(ci->top - L->stack), 0);
341 return status; 341 return status;
342} 342}
diff --git a/testes/coroutine.lua b/testes/coroutine.lua
index 461e0377..76c9d6e6 100644
--- a/testes/coroutine.lua
+++ b/testes/coroutine.lua
@@ -136,6 +136,10 @@ do
136 assert(coroutine.status(co) == "dead") 136 assert(coroutine.status(co) == "dead")
137 local st, msg = coroutine.close(co) 137 local st, msg = coroutine.close(co)
138 assert(st and msg == nil) 138 assert(st and msg == nil)
139 -- also ok to close it again
140 st, msg = coroutine.close(co)
141 assert(st and msg == nil)
142
139 143
140 -- cannot close the running coroutine 144 -- cannot close the running coroutine
141 local st, msg = pcall(coroutine.close, coroutine.running()) 145 local st, msg = pcall(coroutine.close, coroutine.running())
@@ -149,6 +153,22 @@ do
149 assert(not st and string.find(msg, "normal")) 153 assert(not st and string.find(msg, "normal"))
150 end))() 154 end))()
151 155
156 -- cannot close a coroutine while closing it
157 do
158 local co
159 co = coroutine.create(
160 function()
161 local x <close> = func2close(function()
162 coroutine.close(co) -- try to close it again
163 end)
164 coroutine.yield(20)
165 end)
166 local st, msg = coroutine.resume(co)
167 assert(st and msg == 20)
168 st, msg = coroutine.close(co)
169 assert(not st and string.find(msg, "running coroutine"))
170 end
171
152 -- to-be-closed variables in coroutines 172 -- to-be-closed variables in coroutines
153 local X 173 local X
154 174
@@ -158,6 +178,9 @@ do
158 assert(not st and msg == 100) 178 assert(not st and msg == 100)
159 st, msg = coroutine.close(co) 179 st, msg = coroutine.close(co)
160 assert(not st and msg == 100) 180 assert(not st and msg == 100)
181 -- after closing, no more errors
182 st, msg = coroutine.close(co)
183 assert(st and msg == nil)
161 184
162 co = coroutine.create(function () 185 co = coroutine.create(function ()
163 local x <close> = func2close(function (self, err) 186 local x <close> = func2close(function (self, err)
@@ -189,6 +212,9 @@ do
189 local st, msg = coroutine.close(co) 212 local st, msg = coroutine.close(co)
190 assert(st == false and coroutine.status(co) == "dead" and msg == 200) 213 assert(st == false and coroutine.status(co) == "dead" and msg == 200)
191 assert(x == 200) 214 assert(x == 200)
215 -- after closing, no more errors
216 st, msg = coroutine.close(co)
217 assert(st and msg == nil)
192end 218end
193 219
194do 220do
@@ -419,7 +445,7 @@ do
419 445
420 local X = false 446 local X = false
421 A = coroutine.wrap(function() 447 A = coroutine.wrap(function()
422 local _ <close> = setmetatable({}, {__close = function () X = true end}) 448 local _ <close> = func2close(function () X = true end)
423 return pcall(A, 1) 449 return pcall(A, 1)
424 end) 450 end)
425 st, res = A() 451 st, res = A()
@@ -427,6 +453,22 @@ do
427end 453end
428 454
429 455
456-- bug in 5.4.1
457do
458 -- coroutine ran close metamethods with invalid status during a
459 -- reset.
460 local co
461 co = coroutine.wrap(function()
462 local x <close> = func2close(function() return pcall(co) end)
463 error(111)
464 end)
465 local st, errobj = pcall(co)
466 assert(not st and errobj == 111)
467 st, errobj = pcall(co)
468 assert(not st and string.find(errobj, "dead coroutine"))
469end
470
471
430-- attempt to resume 'normal' coroutine 472-- attempt to resume 'normal' coroutine
431local co1, co2 473local co1, co2
432co1 = coroutine.create(function () return co2() end) 474co1 = coroutine.create(function () return co2() end)