issue #163 Fix issue with handling of deferred functions when a Lua thread is closed.

memtest
Dibyendu Majumdar 4 years ago
parent d8a60ddd23
commit e832dcc32f

@ -34,7 +34,7 @@ Features
* Type specific bytecodes to improve performance
* Compatibility with Lua 5.3 (see Compatibility section below)
* Generational GC from Lua 5.4
* ``defer`` statement added
* ``defer`` statement for releasing resources
* Compact JIT backend `MIR <https://github.com/vnmakarov/mir>`_; only Linux and x86-64 supported for now.
* `LLVM <http://www.llvm.org/>`_ supported as alternative JIT backend.
* A `distribution with batteries <https://github.com/dibyendumajumdar/Suravi>`_.

@ -33,6 +33,17 @@
#define upisopen(up) ((up)->v != &(up)->u.value)
/*
** 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 CClosure *luaF_newCclosure (lua_State *L, int nelems);
LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nelems);

@ -226,6 +226,7 @@ extern const char lua_ident[];
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
LUA_API void (lua_close) (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);

@ -75,8 +75,11 @@ static int luaB_auxwrap (lua_State *L) {
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
int r = auxresume(L, co, lua_gettop(L));
if (r < 0) {
int stat = lua_status(co);
if (stat != LUA_OK && stat != LUA_YIELD)
lua_resetthread(co); /* close variables in case of errors */
if (lua_type(L, -1) == LUA_TSTRING) { /* error object is a string? */
luaL_where(L, 1); /* add extra info */
luaL_where(L, 1); /* add extra info, if available */
lua_insert(L, -2);
lua_concat(L, 2);
}

@ -104,6 +104,10 @@ void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
break;
}
case CLOSEPROTECT: {
setnilvalue(oldtop); /* no error message */
break;
}
default: {
setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
break;

@ -123,7 +123,7 @@ static int preparetocall(lua_State *L, TValue *func, TValue *err) {
** inside the original protected call, and so any error will be handled
** there. Otherwise, a previous error already activated the original
** protected call, and so the call to the deferred method must be
** protected here. (A status == -1 behaves like a previous
** 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 deferred method will be pushed
** at the top of the stack. Otherwise, values are pushed after
@ -143,7 +143,7 @@ static int calldeferredfunction(lua_State *L, StkId level, int status) {
luaD_seterrorobj(L, status, level); /* set error message */
preparetocall(L, uv, level);
int newstatus = luaD_pcall(L, calldeferred, NULL, oldtop, 0);
if (newstatus != LUA_OK && status == -1) /* first error? */
if (newstatus != LUA_OK && status == CLOSEPROTECT) /* first error? */
status = newstatus; /* this will be the new error */
else {
/* leave original error (or nil) on top */
@ -161,7 +161,7 @@ int luaF_close (lua_State *L, StkId level, int status) {
if (uv->refcount == 0) { /* no references? */
UpVal uv1 = *uv; /* copy the upvalue as we will free it below */
luaM_free(L, uv); /* free upvalue before invoking any deferred functions */
if (uv1.flags && ttisfunction(uv1.v)) {
if (status != NOCLOSINGMETH && uv1.flags && ttisfunction(uv1.v)) {
ptrdiff_t levelrel = savestack(L, level);
status = calldeferredfunction(L, uv1.v, status);
level = restorestack(L, levelrel);

@ -670,6 +670,7 @@ static int traverseLclosure (global_State *g, LClosure *cl) {
** That ensures that the entire stack have valid (non-dead) objects.
*/
static lu_mem traversethread (global_State *g, lua_State *th) {
UpVal *uv;
StkId o = th->stack;
if (o == NULL)
return 1; /* stack not completely built yet */
@ -677,6 +678,8 @@ static lu_mem traversethread (global_State *g, lua_State *th) {
th->openupval == NULL || isintwups(th));
for (; o < th->top; o++) /* mark live elements in the stack */
markvalue(g, o);
for (uv = th->openupval; uv != NULL; uv = uv->u.open.next)
markvalue(g, uv->v); /* open upvalues cannot be collected */
if (g->gcstate == GCSatomic) { /* final traversal? */
StkId lim = th->stack + th->stacksize; /* real end of stack */
for (; o < lim; o++) /* clear not-marked stack slice */

@ -259,7 +259,7 @@ void *ravi_alloc_f(void *msp, void *ptr, size_t osize, size_t nsize)
static void close_state (lua_State *L) {
global_State *g = G(L);
#ifdef RAVI_DEFER_STATEMENT
luaF_close(L, L->stack, -1); /* close all upvalues for this thread */
luaF_close(L, L->stack, CLOSEPROTECT); /* close all upvalues for this thread */
#else
luaF_close(L, L->stack); /* close all upvalues for this thread */
#endif
@ -310,7 +310,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
void luaE_freethread (lua_State *L, lua_State *L1) {
LX *l = fromstate(L1);
#ifdef RAVI_DEFER_STATEMENT
luaF_close(L1, L1->stack, -1); /* close all upvalues for this thread */
luaF_close(L1, L1->stack, NOCLOSINGMETH); /* close all upvalues for this thread */
#else
luaF_close(L1, L1->stack); /* close all upvalues for this thread */
#endif
@ -320,6 +320,27 @@ void luaE_freethread (lua_State *L, lua_State *L1) {
luaM_free(L, l);
}
int lua_resetthread (lua_State *L) {
CallInfo *ci;
int status;
lua_lock(L);
L->ci = ci = &L->base_ci; /* unwind CallInfo list */
setnilvalue(L->stack); /* 'function' entry for basic 'ci' */
ci->func = L->stack;
ci->callstatus = 0;
status = luaF_close(L, L->stack, CLOSEPROTECT);
if (status != CLOSEPROTECT) /* real errors? */
luaD_seterrorobj(L, status, L->stack + 1);
else {
status = LUA_OK;
L->top = L->stack + 1;
}
ci->top = L->top + LUA_MINSTACK;
L->status = status;
lua_unlock(L);
return status;
}
/* TODO following should probably not live here*/
static void raviE_default_writestring(const char *s, size_t l) {

@ -1788,7 +1788,7 @@ int luaV_execute (lua_State *L) {
/* close all upvalues from previous call */
#ifdef RAVI_DEFER_STATEMENT
if (cl->p->sizep > 0)
Protect_base(luaF_close(L, oci->u.l.base, LUA_OK));
Protect_base(luaF_close(L, oci->u.l.base, NOCLOSINGMETH));
#else
if (cl->p->sizep > 0) luaF_close(L, oci->u.l.base);
#endif

@ -2,7 +2,6 @@ if ravi then
ravi.auto(true)
end
print "testing code generation and optimizations"
T = ravi
local opcodes_coverage = {}
@ -395,4 +394,34 @@ do
print 'Test 9 OK'
end
do
local function t()
-- an error in a wrapped coroutine closes variables
local x = false
local y = false
local co = coroutine.wrap(function ()
local xv = func2close(function () x = true end)
defer getmetatable(xv).__close(xv) end
do
local yv = func2close(function () y = true end)
defer getmetatable(yv).__close(yv) end
coroutine.yield(100) -- yield doesn't close variable
end
coroutine.yield(200) -- yield doesn't close variable
error(23) -- error does
end)
local b = co()
assert(b == 100 and not x and not y)
b = co()
assert(b == 200 and not x and y)
local a, b = pcall(co)
assert(not a and b == 23 and x and y)
end
t()
compile(t)
t()
print 'Test 10 OK'
end
print 'OK'

Loading…
Cancel
Save