diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-11-08 11:55:25 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-11-08 11:55:25 -0300 |
commit | bfbff3703edae789fa5efa9bf174f8e7cff4ded8 (patch) | |
tree | 5e8466f78f52b5d53691f800354a3aed313d11a9 | |
parent | 74d99057a5146755e737c479850f87fd0e3b6868 (diff) | |
download | lua-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.c | 4 | ||||
-rw-r--r-- | lstate.c | 4 | ||||
-rw-r--r-- | testes/coroutine.lua | 44 |
3 files changed, 47 insertions, 5 deletions
@@ -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 | } |
@@ -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) | ||
192 | end | 218 | end |
193 | 219 | ||
194 | do | 220 | do |
@@ -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 | |||
427 | end | 453 | end |
428 | 454 | ||
429 | 455 | ||
456 | -- bug in 5.4.1 | ||
457 | do | ||
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")) | ||
469 | end | ||
470 | |||
471 | |||
430 | -- attempt to resume 'normal' coroutine | 472 | -- attempt to resume 'normal' coroutine |
431 | local co1, co2 | 473 | local co1, co2 |
432 | co1 = coroutine.create(function () return co2() end) | 474 | co1 = coroutine.create(function () return co2() end) |