mirror of https://github.com/rusefi/lua.git
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...)
This commit is contained in:
parent
3b06f983ae
commit
fdc25a1ebf
58
lcorolib.c
58
lcorolib.c
|
@ -107,29 +107,40 @@ static int luaB_yield (lua_State *L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int luaB_costatus (lua_State *L) {
|
#define COS_RUN 0
|
||||||
lua_State *co = getco(L);
|
#define COS_DEAD 1
|
||||||
if (L == co) lua_pushliteral(L, "running");
|
#define COS_YIELD 2
|
||||||
|
#define COS_NORM 3
|
||||||
|
|
||||||
|
|
||||||
|
static const char *statname[] = {"running", "dead", "suspended", "normal"};
|
||||||
|
|
||||||
|
|
||||||
|
static int auxstatus (lua_State *L, lua_State *co) {
|
||||||
|
if (L == co) return COS_RUN;
|
||||||
else {
|
else {
|
||||||
switch (lua_status(co)) {
|
switch (lua_status(co)) {
|
||||||
case LUA_YIELD:
|
case LUA_YIELD:
|
||||||
lua_pushliteral(L, "suspended");
|
return COS_YIELD;
|
||||||
break;
|
|
||||||
case LUA_OK: {
|
case LUA_OK: {
|
||||||
lua_Debug ar;
|
lua_Debug ar;
|
||||||
if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */
|
if (lua_getstack(co, 0, &ar)) /* does it have frames? */
|
||||||
lua_pushliteral(L, "normal"); /* it is running */
|
return COS_NORM; /* it is running */
|
||||||
else if (lua_gettop(co) == 0)
|
else if (lua_gettop(co) == 0)
|
||||||
lua_pushliteral(L, "dead");
|
return COS_DEAD;
|
||||||
else
|
else
|
||||||
lua_pushliteral(L, "suspended"); /* initial state */
|
return COS_YIELD; /* initial state */
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
default: /* some error occurred */
|
default: /* some error occurred */
|
||||||
lua_pushliteral(L, "dead");
|
return COS_DEAD;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_costatus (lua_State *L) {
|
||||||
|
lua_State *co = getco(L);
|
||||||
|
lua_pushstring(L, statname[auxstatus(L, co)]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,6 +158,28 @@ static int luaB_corunning (lua_State *L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int luaB_kill (lua_State *L) {
|
||||||
|
lua_State *co = getco(L);
|
||||||
|
int status = auxstatus(L, co);
|
||||||
|
switch (status) {
|
||||||
|
case COS_DEAD: case COS_YIELD: {
|
||||||
|
status = lua_resetthread(co);
|
||||||
|
if (status == LUA_OK) {
|
||||||
|
lua_pushboolean(L, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lua_pushboolean(L, 0);
|
||||||
|
lua_xmove(co, L, 1); /* copy error message */
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: /* normal or running coroutine */
|
||||||
|
return luaL_error(L, "cannot kill a %s coroutine", statname[status]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static const luaL_Reg co_funcs[] = {
|
static const luaL_Reg co_funcs[] = {
|
||||||
{"create", luaB_cocreate},
|
{"create", luaB_cocreate},
|
||||||
{"resume", luaB_coresume},
|
{"resume", luaB_coresume},
|
||||||
|
@ -155,6 +188,7 @@ static const luaL_Reg co_funcs[] = {
|
||||||
{"wrap", luaB_cowrap},
|
{"wrap", luaB_cowrap},
|
||||||
{"yield", luaB_yield},
|
{"yield", luaB_yield},
|
||||||
{"isyieldable", luaB_yieldable},
|
{"isyieldable", luaB_yieldable},
|
||||||
|
{"kill", luaB_kill},
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
4
ldo.c
4
ldo.c
|
@ -98,6 +98,10 @@ void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
|
||||||
setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
|
setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case CLOSEPROTECT: {
|
||||||
|
setnilvalue(s2v(oldtop)); /* no error message */
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
|
setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
|
||||||
break;
|
break;
|
||||||
|
|
18
lfunc.c
18
lfunc.c
|
@ -127,17 +127,18 @@ static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) {
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Prepare and call a closing method. If status is OK, code is
|
** Prepare and call a closing method. If status is OK, code is still
|
||||||
** still inside the original protected call, and so any error
|
** inside the original protected call, and so any error will be handled
|
||||||
** will be handled there. Otherwise, a previous error already
|
** there. Otherwise, a previous error already activated original
|
||||||
** activated original protected call, and so the call to the
|
** protected call, and so the call to the closing method must be
|
||||||
** closing method must be protected here.
|
** protected here. (A status = CLOSEPROTECT behaves like a previous
|
||||||
|
** error, to also run the closing method in protected mode).
|
||||||
** If status is OK, the call to the closing method will be pushed
|
** If status is OK, the call to the closing method will be pushed
|
||||||
** at the top of the stack. Otherwise, values are pushed after
|
** at the top of the stack. Otherwise, values are pushed after
|
||||||
** the 'level' of the upvalue being closed, as everything after
|
** the 'level' of the upvalue being closed, as everything after
|
||||||
** that won't be used again.
|
** that won't be used again.
|
||||||
*/
|
*/
|
||||||
static int closeupval (lua_State *L, TValue *uv, StkId level, int status) {
|
static int callclosemth (lua_State *L, TValue *uv, StkId level, int status) {
|
||||||
if (likely(status == LUA_OK)) {
|
if (likely(status == LUA_OK)) {
|
||||||
if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */
|
if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */
|
||||||
callclose(L, NULL); /* call closing method */
|
callclose(L, NULL); /* call closing method */
|
||||||
|
@ -207,9 +208,10 @@ int luaF_close (lua_State *L, StkId level, int status) {
|
||||||
if (!iswhite(uv))
|
if (!iswhite(uv))
|
||||||
gray2black(uv); /* closed upvalues cannot be gray */
|
gray2black(uv); /* closed upvalues cannot be gray */
|
||||||
luaC_barrier(L, uv, slot);
|
luaC_barrier(L, uv, slot);
|
||||||
if (status >= 0 && uv->tt == LUA_TUPVALTBC) { /* must be closed? */
|
if (uv->tt == LUA_TUPVALTBC && status != NOCLOSINGMETH) {
|
||||||
|
/* must run closing method */
|
||||||
ptrdiff_t levelrel = savestack(L, level);
|
ptrdiff_t levelrel = savestack(L, level);
|
||||||
status = closeupval(L, uv->v, upl, status); /* may realloc. the stack */
|
status = callclosemth(L, uv->v, upl, status); /* may change the stack */
|
||||||
level = restorestack(L, levelrel);
|
level = restorestack(L, levelrel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
11
lfunc.h
11
lfunc.h
|
@ -42,6 +42,17 @@
|
||||||
#define MAXMISS 10
|
#define MAXMISS 10
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Special "status" for 'luaF_close'
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* close upvalues without running their closing methods */
|
||||||
|
#define NOCLOSINGMETH (-1)
|
||||||
|
|
||||||
|
/* close upvalues running all closing methods in protected mode */
|
||||||
|
#define CLOSEPROTECT (-2)
|
||||||
|
|
||||||
|
|
||||||
LUAI_FUNC Proto *luaF_newproto (lua_State *L);
|
LUAI_FUNC Proto *luaF_newproto (lua_State *L);
|
||||||
LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nelems);
|
LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nelems);
|
||||||
LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems);
|
LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems);
|
||||||
|
|
27
lstate.c
27
lstate.c
|
@ -258,7 +258,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
|
||||||
|
|
||||||
static void close_state (lua_State *L) {
|
static void close_state (lua_State *L) {
|
||||||
global_State *g = G(L);
|
global_State *g = G(L);
|
||||||
luaF_close(L, L->stack, -1); /* close all upvalues for this thread */
|
luaF_close(L, L->stack, CLOSEPROTECT); /* close all upvalues */
|
||||||
luaC_freeallobjects(L); /* collect all objects */
|
luaC_freeallobjects(L); /* collect all objects */
|
||||||
if (ttisnil(&g->nilvalue)) /* closing a fully built state? */
|
if (ttisnil(&g->nilvalue)) /* closing a fully built state? */
|
||||||
luai_userstateclose(L);
|
luai_userstateclose(L);
|
||||||
|
@ -301,7 +301,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
|
||||||
|
|
||||||
void luaE_freethread (lua_State *L, lua_State *L1) {
|
void luaE_freethread (lua_State *L, lua_State *L1) {
|
||||||
LX *l = fromstate(L1);
|
LX *l = fromstate(L1);
|
||||||
luaF_close(L1, L1->stack, -1); /* close all upvalues for this thread */
|
luaF_close(L1, L1->stack, NOCLOSINGMETH); /* close all upvalues */
|
||||||
lua_assert(L1->openupval == NULL);
|
lua_assert(L1->openupval == NULL);
|
||||||
luai_userstatefree(L, L1);
|
luai_userstatefree(L, L1);
|
||||||
freestack(L1);
|
freestack(L1);
|
||||||
|
@ -309,6 +309,29 @@ void luaE_freethread (lua_State *L, lua_State *L1) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int lua_resetthread (lua_State *L) {
|
||||||
|
CallInfo *ci;
|
||||||
|
int status;
|
||||||
|
lua_lock(L);
|
||||||
|
ci = &L->base_ci;
|
||||||
|
status = luaF_close(L, L->stack, CLOSEPROTECT);
|
||||||
|
setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */
|
||||||
|
if (status != CLOSEPROTECT) /* real errors? */
|
||||||
|
luaD_seterrorobj(L, status, L->stack + 1);
|
||||||
|
else {
|
||||||
|
status = LUA_OK;
|
||||||
|
L->top = L->stack + 1;
|
||||||
|
}
|
||||||
|
ci->callstatus = CIST_C;
|
||||||
|
ci->func = L->stack;
|
||||||
|
ci->top = L->top + LUA_MINSTACK;
|
||||||
|
L->ci = ci;
|
||||||
|
L->status = status;
|
||||||
|
lua_unlock(L);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
|
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
|
||||||
int i;
|
int i;
|
||||||
lua_State *L;
|
lua_State *L;
|
||||||
|
|
3
ltests.c
3
ltests.c
|
@ -1366,6 +1366,9 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) {
|
||||||
else if EQ("newthread") {
|
else if EQ("newthread") {
|
||||||
lua_newthread(L1);
|
lua_newthread(L1);
|
||||||
}
|
}
|
||||||
|
else if EQ("resetthread") {
|
||||||
|
lua_pushinteger(L1, lua_resetthread(L1));
|
||||||
|
}
|
||||||
else if EQ("newuserdata") {
|
else if EQ("newuserdata") {
|
||||||
lua_newuserdata(L1, getnum);
|
lua_newuserdata(L1, getnum);
|
||||||
}
|
}
|
||||||
|
|
1
lua.h
1
lua.h
|
@ -147,6 +147,7 @@ extern const char lua_ident[];
|
||||||
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
|
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
|
||||||
LUA_API void (lua_close) (lua_State *L);
|
LUA_API void (lua_close) (lua_State *L);
|
||||||
LUA_API lua_State *(lua_newthread) (lua_State *L);
|
LUA_API lua_State *(lua_newthread) (lua_State *L);
|
||||||
|
LUA_API int (lua_resetthread) (lua_State *L);
|
||||||
|
|
||||||
LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
|
LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
|
||||||
|
|
||||||
|
|
2
lvm.c
2
lvm.c
|
@ -1565,7 +1565,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||||
if (nparams1) /* vararg function? */
|
if (nparams1) /* vararg function? */
|
||||||
delta = ci->u.l.nextraargs + nparams1;
|
delta = ci->u.l.nextraargs + nparams1;
|
||||||
/* close upvalues from current call */
|
/* close upvalues from current call */
|
||||||
luaF_close(L, base, -1); /* (no to-be-closed vars. here) */
|
luaF_close(L, base, LUA_OK);
|
||||||
updatestack(ci);
|
updatestack(ci);
|
||||||
}
|
}
|
||||||
if (!ttisfunction(s2v(ra))) { /* not a function? */
|
if (!ttisfunction(s2v(ra))) { /* not a function? */
|
||||||
|
|
|
@ -3927,6 +3927,19 @@ and then pops the top element.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@APIEntry{int lua_resetthread (lua_State *L);|
|
||||||
|
@apii{0,?,-}
|
||||||
|
|
||||||
|
Resets a thread, cleaning its call stack and closing all pending
|
||||||
|
to-be-closed variables.
|
||||||
|
Returns a status code:
|
||||||
|
@Lid{LUA_OK} for no errors in closing methods,
|
||||||
|
or an error status otherwise.
|
||||||
|
In case of error,
|
||||||
|
leave the error object on the stack,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@APIEntry{int lua_resume (lua_State *L, lua_State *from, int nargs,
|
@APIEntry{int lua_resume (lua_State *L, lua_State *from, int nargs,
|
||||||
int *nresults);|
|
int *nresults);|
|
||||||
@apii{?,?,-}
|
@apii{?,?,-}
|
||||||
|
@ -3948,11 +3961,8 @@ or returned by the body function.
|
||||||
@Lid{LUA_OK} if the coroutine finishes its execution
|
@Lid{LUA_OK} if the coroutine finishes its execution
|
||||||
without errors,
|
without errors,
|
||||||
or an error code in case of errors @seeC{lua_pcall}.
|
or an error code in case of errors @seeC{lua_pcall}.
|
||||||
|
|
||||||
In case of errors,
|
In case of errors,
|
||||||
the stack is not unwound,
|
the error object is on the top of the stack.
|
||||||
so you can use the debug API over it.
|
|
||||||
The error object is on the top of the stack.
|
|
||||||
|
|
||||||
To resume a coroutine,
|
To resume a coroutine,
|
||||||
you remove all results from the last @Lid{lua_yield},
|
you remove all results from the last @Lid{lua_yield},
|
||||||
|
@ -6285,6 +6295,17 @@ it is not inside a non-yieldable @N{C function}.
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@LibEntry{coroutine.kill(co)|
|
||||||
|
|
||||||
|
Kills coroutine @id{co},
|
||||||
|
closing all its pending to-be-closed variables
|
||||||
|
and putting the coroutine in a dead state.
|
||||||
|
In case of error closing some variable,
|
||||||
|
returns @false plus the error object;
|
||||||
|
otherwise returns @true.
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@LibEntry{coroutine.resume (co [, val1, @Cdots])|
|
@LibEntry{coroutine.resume (co [, val1, @Cdots])|
|
||||||
|
|
||||||
Starts or continues the execution of coroutine @id{co}.
|
Starts or continues the execution of coroutine @id{co}.
|
||||||
|
@ -8648,6 +8669,11 @@ has been removed.
|
||||||
When needed, this metamethod must be explicitly defined.
|
When needed, this metamethod must be explicitly defined.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@item{
|
||||||
|
When a coroutine finishes with an error,
|
||||||
|
its stack is unwound (to run any pending closing methods).
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,6 +119,51 @@ end
|
||||||
assert(#a == 25 and a[#a] == 97)
|
assert(#a == 25 and a[#a] == 97)
|
||||||
x, a = nil
|
x, a = nil
|
||||||
|
|
||||||
|
|
||||||
|
-- coroutine kill
|
||||||
|
do
|
||||||
|
-- ok to kill a dead coroutine
|
||||||
|
local co = coroutine.create(print)
|
||||||
|
assert(coroutine.resume(co, "testing 'coroutine.kill'"))
|
||||||
|
assert(coroutine.status(co) == "dead")
|
||||||
|
assert(coroutine.kill(co))
|
||||||
|
|
||||||
|
-- cannot kill the running coroutine
|
||||||
|
local st, msg = pcall(coroutine.kill, coroutine.running())
|
||||||
|
assert(not st and string.find(msg, "running"))
|
||||||
|
|
||||||
|
local main = coroutine.running()
|
||||||
|
|
||||||
|
-- cannot kill a "normal" coroutine
|
||||||
|
;(coroutine.wrap(function ()
|
||||||
|
local st, msg = pcall(coroutine.kill, main)
|
||||||
|
assert(not st and string.find(msg, "normal"))
|
||||||
|
end))()
|
||||||
|
|
||||||
|
-- to-be-closed variables in coroutines
|
||||||
|
local X
|
||||||
|
co = coroutine.create(function ()
|
||||||
|
local *toclose x = function (err) assert(err == nil); X = false end
|
||||||
|
X = true
|
||||||
|
coroutine.yield()
|
||||||
|
end)
|
||||||
|
coroutine.resume(co)
|
||||||
|
assert(X)
|
||||||
|
assert(coroutine.kill(co))
|
||||||
|
assert(not X and coroutine.status(co) == "dead")
|
||||||
|
|
||||||
|
-- error killing a coroutine
|
||||||
|
co = coroutine.create(function()
|
||||||
|
local *toclose x = function (err) assert(err == nil); error(111) end
|
||||||
|
coroutine.yield()
|
||||||
|
end)
|
||||||
|
coroutine.resume(co)
|
||||||
|
local st, msg = coroutine.kill(co)
|
||||||
|
assert(not st and coroutine.status(co) == "dead" and msg == 111)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- yielding across C boundaries
|
-- yielding across C boundaries
|
||||||
|
|
||||||
co = coroutine.wrap(function()
|
co = coroutine.wrap(function()
|
||||||
|
|
|
@ -306,6 +306,20 @@ NoRun("", "lua %s", prog) -- no message
|
||||||
prepfile("os.exit(false, true)")
|
prepfile("os.exit(false, true)")
|
||||||
NoRun("", "lua %s", prog) -- no message
|
NoRun("", "lua %s", prog) -- no message
|
||||||
|
|
||||||
|
|
||||||
|
-- to-be-closed variables in main chunk
|
||||||
|
prepfile[[
|
||||||
|
local *toclose x = function (err)
|
||||||
|
assert(err == 120)
|
||||||
|
print("Ok")
|
||||||
|
end
|
||||||
|
local *toclose e1 = function () error(120) end
|
||||||
|
os.exit(true, true)
|
||||||
|
]]
|
||||||
|
RUN('lua %s > %s', prog, out)
|
||||||
|
checkprogout("Ok")
|
||||||
|
|
||||||
|
|
||||||
-- remove temporary files
|
-- remove temporary files
|
||||||
assert(os.remove(prog))
|
assert(os.remove(prog))
|
||||||
assert(os.remove(otherprog))
|
assert(os.remove(otherprog))
|
||||||
|
|
Loading…
Reference in New Issue