diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2025-03-17 14:32:08 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2025-03-17 14:32:08 -0300 |
commit | 921832be8d7f687d2cd891654c8680c6e9d6584c (patch) | |
tree | 0a2ec0b675d289feea0da26c8082450bdb18aaf8 | |
parent | 205f9aa67b43b3d9b5059769cfc1ed0265341586 (diff) | |
download | lua-921832be8d7f687d2cd891654c8680c6e9d6584c.tar.gz lua-921832be8d7f687d2cd891654c8680c6e9d6584c.tar.bz2 lua-921832be8d7f687d2cd891654c8680c6e9d6584c.zip |
New function 'resetCI'
New function 'resetCI' resets the CallInfo list of a thread, ensuring
a proper state when creating a new thread, closing a thread, or
closing a state, so that we can run code after that. (When closing a
thread, we need to run its __close metamethods; when closing a
state, we need to run its __close metamethods and its finalizers.)
-rw-r--r-- | lstate.c | 39 | ||||
-rw-r--r-- | testes/coroutine.lua | 19 |
2 files changed, 39 insertions, 19 deletions
@@ -143,25 +143,29 @@ LUAI_FUNC void luaE_incCstack (lua_State *L) { | |||
143 | } | 143 | } |
144 | 144 | ||
145 | 145 | ||
146 | static void resetCI (lua_State *L) { | ||
147 | CallInfo *ci = L->ci = &L->base_ci; | ||
148 | ci->func.p = L->stack.p; | ||
149 | setnilvalue(s2v(ci->func.p)); /* 'function' entry for basic 'ci' */ | ||
150 | ci->top.p = ci->func.p + 1 + LUA_MINSTACK; /* +1 for 'function' entry */ | ||
151 | ci->u.c.k = NULL; | ||
152 | ci->callstatus = CIST_C; | ||
153 | L->status = LUA_OK; | ||
154 | L->errfunc = 0; /* stack unwind can "throw away" the error function */ | ||
155 | } | ||
156 | |||
157 | |||
146 | static void stack_init (lua_State *L1, lua_State *L) { | 158 | static void stack_init (lua_State *L1, lua_State *L) { |
147 | int i; CallInfo *ci; | 159 | int i; |
148 | /* initialize stack array */ | 160 | /* initialize stack array */ |
149 | L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue); | 161 | L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue); |
150 | L1->tbclist.p = L1->stack.p; | 162 | L1->tbclist.p = L1->stack.p; |
151 | for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++) | 163 | for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++) |
152 | setnilvalue(s2v(L1->stack.p + i)); /* erase new stack */ | 164 | setnilvalue(s2v(L1->stack.p + i)); /* erase new stack */ |
153 | L1->top.p = L1->stack.p; | ||
154 | L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE; | 165 | L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE; |
155 | /* initialize first ci */ | 166 | /* initialize first ci */ |
156 | ci = &L1->base_ci; | 167 | resetCI(L1); |
157 | ci->next = ci->previous = NULL; | 168 | L1->top.p = L1->stack.p + 1; /* +1 for 'function' entry */ |
158 | ci->callstatus = CIST_C; | ||
159 | ci->func.p = L1->top.p; | ||
160 | ci->u.c.k = NULL; | ||
161 | setnilvalue(s2v(L1->top.p)); /* 'function' entry for this 'ci' */ | ||
162 | L1->top.p++; | ||
163 | ci->top.p = L1->top.p + LUA_MINSTACK; | ||
164 | L1->ci = ci; | ||
165 | } | 169 | } |
166 | 170 | ||
167 | 171 | ||
@@ -235,6 +239,7 @@ static void preinit_thread (lua_State *L, global_State *g) { | |||
235 | L->status = LUA_OK; | 239 | L->status = LUA_OK; |
236 | L->errfunc = 0; | 240 | L->errfunc = 0; |
237 | L->oldpc = 0; | 241 | L->oldpc = 0; |
242 | L->base_ci.previous = L->base_ci.next = NULL; | ||
238 | } | 243 | } |
239 | 244 | ||
240 | 245 | ||
@@ -252,8 +257,9 @@ static void close_state (lua_State *L) { | |||
252 | if (!completestate(g)) /* closing a partially built state? */ | 257 | if (!completestate(g)) /* closing a partially built state? */ |
253 | luaC_freeallobjects(L); /* just collect its objects */ | 258 | luaC_freeallobjects(L); /* just collect its objects */ |
254 | else { /* closing a fully built state */ | 259 | else { /* closing a fully built state */ |
255 | L->ci = &L->base_ci; /* unwind CallInfo list */ | 260 | resetCI(L); |
256 | luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */ | 261 | luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */ |
262 | L->top.p = L->stack.p + 1; /* empty the stack to run finalizers */ | ||
257 | luaC_freeallobjects(L); /* collect all objects */ | 263 | luaC_freeallobjects(L); /* collect all objects */ |
258 | luai_userstateclose(L); | 264 | luai_userstateclose(L); |
259 | } | 265 | } |
@@ -302,20 +308,15 @@ void luaE_freethread (lua_State *L, lua_State *L1) { | |||
302 | 308 | ||
303 | 309 | ||
304 | TStatus luaE_resetthread (lua_State *L, TStatus status) { | 310 | TStatus luaE_resetthread (lua_State *L, TStatus status) { |
305 | CallInfo *ci = L->ci = &L->base_ci; /* unwind CallInfo list */ | 311 | resetCI(L); |
306 | setnilvalue(s2v(L->stack.p)); /* 'function' entry for basic 'ci' */ | ||
307 | ci->func.p = L->stack.p; | ||
308 | ci->callstatus = CIST_C; | ||
309 | if (status == LUA_YIELD) | 312 | if (status == LUA_YIELD) |
310 | status = LUA_OK; | 313 | status = LUA_OK; |
311 | L->status = LUA_OK; /* so it can run __close metamethods */ | ||
312 | status = luaD_closeprotected(L, 1, status); | 314 | status = luaD_closeprotected(L, 1, status); |
313 | if (status != LUA_OK) /* errors? */ | 315 | if (status != LUA_OK) /* errors? */ |
314 | luaD_seterrorobj(L, status, L->stack.p + 1); | 316 | luaD_seterrorobj(L, status, L->stack.p + 1); |
315 | else | 317 | else |
316 | L->top.p = L->stack.p + 1; | 318 | L->top.p = L->stack.p + 1; |
317 | ci->top.p = L->top.p + LUA_MINSTACK; | 319 | luaD_reallocstack(L, cast_int(L->ci->top.p - L->stack.p), 0); |
318 | luaD_reallocstack(L, cast_int(ci->top.p - L->stack.p), 0); | ||
319 | return status; | 320 | return status; |
320 | } | 321 | } |
321 | 322 | ||
diff --git a/testes/coroutine.lua b/testes/coroutine.lua index 680fc605..17f6ceba 100644 --- a/testes/coroutine.lua +++ b/testes/coroutine.lua | |||
@@ -505,6 +505,25 @@ assert(not pcall(a, a)) | |||
505 | a = nil | 505 | a = nil |
506 | 506 | ||
507 | 507 | ||
508 | do | ||
509 | -- bug in 5.4: thread can use message handler higher in the stack | ||
510 | -- than the variable being closed | ||
511 | local c = coroutine.create(function() | ||
512 | local clo <close> = setmetatable({}, {__close=function() | ||
513 | local x = 134 -- will overwrite message handler | ||
514 | error(x) | ||
515 | end}) | ||
516 | -- yields coroutine but leaves a new message handler for it, | ||
517 | -- that would be used when closing the coroutine (except that it | ||
518 | -- will be overwritten) | ||
519 | xpcall(coroutine.yield, function() return "XXX" end) | ||
520 | end) | ||
521 | |||
522 | assert(coroutine.resume(c)) -- start coroutine | ||
523 | local st, msg = coroutine.close(c) | ||
524 | assert(not st and msg == 134) | ||
525 | end | ||
526 | |||
508 | -- access to locals of erroneous coroutines | 527 | -- access to locals of erroneous coroutines |
509 | local x = coroutine.create (function () | 528 | local x = coroutine.create (function () |
510 | local a = 10 | 529 | local a = 10 |