aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2025-06-12 11:15:09 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2025-06-12 11:15:09 -0300
commitfd897027f19288ce2cb0249cb8c1818e2f3f1c4c (patch)
tree7fd131ca204c4100a24157405eda4239d2155a46
parentd05fe48bfdd89956c0ebd115dca0fb115aa28dd6 (diff)
downloadlua-fd897027f19288ce2cb0249cb8c1818e2f3f1c4c.tar.gz
lua-fd897027f19288ce2cb0249cb8c1818e2f3f1c4c.tar.bz2
lua-fd897027f19288ce2cb0249cb8c1818e2f3f1c4c.zip
A coroutine can close itself
A call to close itself will close all its to-be-closed variables and return to the resume that (re)started the coroutine.
-rw-r--r--lcorolib.c13
-rw-r--r--ldo.c10
-rw-r--r--ldo.h1
-rw-r--r--lstate.c2
-rw-r--r--manual/manual.of36
-rw-r--r--testes/coroutine.lua62
6 files changed, 103 insertions, 21 deletions
diff --git a/lcorolib.c b/lcorolib.c
index 3d95f873..5b9736f1 100644
--- a/lcorolib.c
+++ b/lcorolib.c
@@ -154,8 +154,13 @@ static int luaB_costatus (lua_State *L) {
154} 154}
155 155
156 156
157static lua_State *getoptco (lua_State *L) {
158 return (lua_isnone(L, 1) ? L : getco(L));
159}
160
161
157static int luaB_yieldable (lua_State *L) { 162static int luaB_yieldable (lua_State *L) {
158 lua_State *co = lua_isnone(L, 1) ? L : getco(L); 163 lua_State *co = getoptco(L);
159 lua_pushboolean(L, lua_isyieldable(co)); 164 lua_pushboolean(L, lua_isyieldable(co));
160 return 1; 165 return 1;
161} 166}
@@ -169,7 +174,7 @@ static int luaB_corunning (lua_State *L) {
169 174
170 175
171static int luaB_close (lua_State *L) { 176static int luaB_close (lua_State *L) {
172 lua_State *co = getco(L); 177 lua_State *co = getoptco(L);
173 int status = auxstatus(L, co); 178 int status = auxstatus(L, co);
174 switch (status) { 179 switch (status) {
175 case COS_DEAD: case COS_YIELD: { 180 case COS_DEAD: case COS_YIELD: {
@@ -184,6 +189,10 @@ static int luaB_close (lua_State *L) {
184 return 2; 189 return 2;
185 } 190 }
186 } 191 }
192 case COS_RUN: /* running coroutine? */
193 lua_closethread(co, L); /* close itself */
194 lua_assert(0); /* previous call does not return */
195 return 0;
187 default: /* normal or running coroutine */ 196 default: /* normal or running coroutine */
188 return luaL_error(L, "cannot close a %s coroutine", statname[status]); 197 return luaL_error(L, "cannot close a %s coroutine", statname[status]);
189 } 198 }
diff --git a/ldo.c b/ldo.c
index 820b5a9a..776519dc 100644
--- a/ldo.c
+++ b/ldo.c
@@ -139,6 +139,16 @@ l_noret luaD_throw (lua_State *L, TStatus errcode) {
139} 139}
140 140
141 141
142l_noret luaD_throwbaselevel (lua_State *L, TStatus errcode) {
143 if (L->errorJmp) {
144 /* unroll error entries up to the first level */
145 while (L->errorJmp->previous != NULL)
146 L->errorJmp = L->errorJmp->previous;
147 }
148 luaD_throw(L, errcode);
149}
150
151
142TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { 152TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
143 l_uint32 oldnCcalls = L->nCcalls; 153 l_uint32 oldnCcalls = L->nCcalls;
144 struct lua_longjmp lj; 154 struct lua_longjmp lj;
diff --git a/ldo.h b/ldo.h
index 465f4fb8..2d4ca8be 100644
--- a/ldo.h
+++ b/ldo.h
@@ -91,6 +91,7 @@ LUAI_FUNC void luaD_shrinkstack (lua_State *L);
91LUAI_FUNC void luaD_inctop (lua_State *L); 91LUAI_FUNC void luaD_inctop (lua_State *L);
92 92
93LUAI_FUNC l_noret luaD_throw (lua_State *L, TStatus errcode); 93LUAI_FUNC l_noret luaD_throw (lua_State *L, TStatus errcode);
94LUAI_FUNC l_noret luaD_throwbaselevel (lua_State *L, TStatus errcode);
94LUAI_FUNC TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); 95LUAI_FUNC TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud);
95 96
96#endif 97#endif
diff --git a/lstate.c b/lstate.c
index 20ed838f..70a11aae 100644
--- a/lstate.c
+++ b/lstate.c
@@ -326,6 +326,8 @@ LUA_API int lua_closethread (lua_State *L, lua_State *from) {
326 lua_lock(L); 326 lua_lock(L);
327 L->nCcalls = (from) ? getCcalls(from) : 0; 327 L->nCcalls = (from) ? getCcalls(from) : 0;
328 status = luaE_resetthread(L, L->status); 328 status = luaE_resetthread(L, L->status);
329 if (L == from) /* closing itself? */
330 luaD_throwbaselevel(L, status);
329 lua_unlock(L); 331 lua_unlock(L);
330 return APIstatus(status); 332 return APIstatus(status);
331} 333}
diff --git a/manual/manual.of b/manual/manual.of
index 0d473eed..7c504d97 100644
--- a/manual/manual.of
+++ b/manual/manual.of
@@ -3267,17 +3267,25 @@ when called through this function.
3267 3267
3268Resets a thread, cleaning its call stack and closing all pending 3268Resets a thread, cleaning its call stack and closing all pending
3269to-be-closed variables. 3269to-be-closed variables.
3270Returns a status code: 3270The parameter @id{from} represents the coroutine that is resetting @id{L}.
3271If there is no such coroutine,
3272this parameter can be @id{NULL}.
3273
3274Unless @id{L} is equal to @id{from},
3275the call returns a status code:
3271@Lid{LUA_OK} for no errors in the thread 3276@Lid{LUA_OK} for no errors in the thread
3272(either the original error that stopped the thread or 3277(either the original error that stopped the thread or
3273errors in closing methods), 3278errors in closing methods),
3274or an error status otherwise. 3279or an error status otherwise.
3275In case of error, 3280In case of error,
3276leaves the error object on the top of the stack. 3281the error object is put on the top of the stack.
3277 3282
3278The parameter @id{from} represents the coroutine that is resetting @id{L}. 3283If @id{L} is equal to @id{from},
3279If there is no such coroutine, 3284it corresponds to a thread closing itself.
3280this parameter can be @id{NULL}. 3285In that case,
3286the call does not return;
3287instead, the resume or the protected call
3288that (re)started the thread returns.
3281 3289
3282} 3290}
3283 3291
@@ -6939,18 +6947,26 @@ which come inside the table @defid{coroutine}.
6939See @See{coroutine} for a general description of coroutines. 6947See @See{coroutine} for a general description of coroutines.
6940 6948
6941 6949
6942@LibEntry{coroutine.close (co)| 6950@LibEntry{coroutine.close ([co])|
6943 6951
6944Closes coroutine @id{co}, 6952Closes coroutine @id{co},
6945that is, 6953that is,
6946closes all its pending to-be-closed variables 6954closes all its pending to-be-closed variables
6947and puts the coroutine in a dead state. 6955and puts the coroutine in a dead state.
6948The given coroutine must be dead or suspended. 6956The default for @id{co} is the running coroutine.
6949In case of error 6957
6958The given coroutine must be dead, suspended,
6959or be the running coroutine.
6960For the running coroutine,
6961this function does not return.
6962Instead, the resume that (re)started the coroutine returns.
6963
6964For other coroutines,
6965in case of error
6950(either the original error that stopped the coroutine or 6966(either the original error that stopped the coroutine or
6951errors in closing methods), 6967errors in closing methods),
6952returns @false plus the error object; 6968this function returns @false plus the error object;
6953otherwise returns @true. 6969otherwise ir returns @true.
6954 6970
6955} 6971}
6956 6972
diff --git a/testes/coroutine.lua b/testes/coroutine.lua
index 17f6ceba..02536ee5 100644
--- a/testes/coroutine.lua
+++ b/testes/coroutine.lua
@@ -156,11 +156,6 @@ do
156 st, msg = coroutine.close(co) 156 st, msg = coroutine.close(co)
157 assert(st and msg == nil) 157 assert(st and msg == nil)
158 158
159
160 -- cannot close the running coroutine
161 local st, msg = pcall(coroutine.close, coroutine.running())
162 assert(not st and string.find(msg, "running"))
163
164 local main = coroutine.running() 159 local main = coroutine.running()
165 160
166 -- cannot close a "normal" coroutine 161 -- cannot close a "normal" coroutine
@@ -169,20 +164,19 @@ do
169 assert(not st and string.find(msg, "normal")) 164 assert(not st and string.find(msg, "normal"))
170 end))() 165 end))()
171 166
172 -- cannot close a coroutine while closing it 167 do -- close a coroutine while closing it
173 do
174 local co 168 local co
175 co = coroutine.create( 169 co = coroutine.create(
176 function() 170 function()
177 local x <close> = func2close(function() 171 local x <close> = func2close(function()
178 coroutine.close(co) -- try to close it again 172 coroutine.close(co) -- close it again
179 end) 173 end)
180 coroutine.yield(20) 174 coroutine.yield(20)
181 end) 175 end)
182 local st, msg = coroutine.resume(co) 176 local st, msg = coroutine.resume(co)
183 assert(st and msg == 20) 177 assert(st and msg == 20)
184 st, msg = coroutine.close(co) 178 st, msg = coroutine.close(co)
185 assert(not st and string.find(msg, "running coroutine")) 179 assert(st and msg == nil)
186 end 180 end
187 181
188 -- to-be-closed variables in coroutines 182 -- to-be-closed variables in coroutines
@@ -289,6 +283,56 @@ do
289end 283end
290 284
291 285
286do print("coroutines closing itself")
287 global <const> coroutine, string, os
288 global <const> assert, error, pcall
289
290 local X = nil
291
292 local function new ()
293 return coroutine.create(function (what)
294
295 local <close>var = func2close(function (t, err)
296 if what == "yield" then
297 coroutine.yield()
298 elseif what == "error" then
299 error(200)
300 else
301 X = "Ok"
302 return X
303 end
304 end)
305
306 -- do an unprotected call so that coroutine becomes non-yieldable
307 string.gsub("a", "a", function ()
308 assert(not coroutine.isyieldable())
309 -- do protected calls while non-yieldable, to add recovery
310 -- entries (setjmp) to the stack
311 assert(pcall(pcall, function ()
312 -- 'close' works even while non-yieldable
313 coroutine.close() -- close itself
314 os.exit(false) -- not reacheable
315 end))
316 end)
317 end)
318 end
319
320 local co = new()
321 local st, msg = coroutine.resume(co, "ret")
322 assert(st and msg == nil)
323 assert(X == "Ok")
324
325 local co = new()
326 local st, msg = coroutine.resume(co, "error")
327 assert(not st and msg == 200)
328
329 local co = new()
330 local st, msg = coroutine.resume(co, "yield")
331 assert(not st and string.find(msg, "attempt to yield"))
332
333end
334
335
292-- yielding across C boundaries 336-- yielding across C boundaries
293 337
294local co = coroutine.wrap(function() 338local co = coroutine.wrap(function()