From d36a31e6739bcd39c84f637344227af87cfd0ee5 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Thu, 18 Jul 2019 14:58:15 -0300 Subject: Reviving HARDMEMTESTS This commit brings a new implementation for HARDMEMTESTS, which forces an emergency GC whenever possible. It also fixes some issues detected with this option: - A small bug in lvm.c: a closure could be collected by an emergency GC while being initialized. - Some tests: a memory address can be immediatly reused after a GC; for instance, two consecutive '{}' expressions can return exactly the same address, if the first one is not anchored. --- lmem.c | 23 ++++++++++++++++------- lvm.c | 9 ++++++--- testes/api.lua | 7 +++++-- testes/strings.lua | 11 ++++++++--- 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/lmem.c b/lmem.c index 53f8dcb9..0186a86b 100644 --- a/lmem.c +++ b/lmem.c @@ -23,14 +23,25 @@ #if defined(HARDMEMTESTS) -#define hardtest(L,os,s) /* force a GC whenever possible */ \ - if ((s) > (os) && (G(L))->gcrunning) luaC_fullgc(L, 1); +/* +** First allocation will fail whenever not building initial state +** and not shrinking a block. (This fail will trigger 'tryagain' and +** a full GC cycle at every alocation.) +*/ +static void *firsttry (global_State *g, void *block, size_t os, size_t ns) { + if (ttisnil(&g->nilvalue) && ns > os) + return NULL; /* fail */ + else /* normal allocation */ + return (*g->frealloc)(g->ud, block, os, ns); +} #else -#define hardtest(L,os,s) ((void)0) +#define firsttry(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns)) #endif + + /* ** About the realloc function: ** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize); @@ -138,8 +149,7 @@ void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { void *newblock; global_State *g = G(L); lua_assert((osize == 0) == (block == NULL)); - hardtest(L, osize, nsize); - newblock = (*g->frealloc)(g->ud, block, osize, nsize); + newblock = firsttry(g, block, osize, nsize); if (unlikely(newblock == NULL && nsize > 0)) { if (nsize > osize) /* not shrinking a block? */ newblock = tryagain(L, block, osize, nsize); @@ -162,12 +172,11 @@ void *luaM_saferealloc_ (lua_State *L, void *block, size_t osize, void *luaM_malloc_ (lua_State *L, size_t size, int tag) { - hardtest(L, 0, size); if (size == 0) return NULL; /* that's all */ else { global_State *g = G(L); - void *newblock = (*g->frealloc)(g->ud, NULL, tag, size); + void *newblock = firsttry(g, NULL, tag, size); if (unlikely(newblock == NULL)) { newblock = tryagain(L, NULL, tag, size); if (newblock == NULL) diff --git a/lvm.c b/lvm.c index c1b6749d..d9bd0ab3 100644 --- a/lvm.c +++ b/lvm.c @@ -1038,7 +1038,10 @@ void luaV_finishOp (lua_State *L) { ** errors. (That is, it will not return to the interpreter main loop ** after changing the stack or hooks.) */ -#define halfProtect(exp) (savepc(L), (exp)) +#define halfProtect(exp) (savestate(L,ci), (exp)) + +/* idem, but without changing the stack */ +#define halfProtectNT(exp) (savepc(L), (exp)) #define checkGC(L,c) \ @@ -1620,7 +1623,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmcase(OP_RETURN0) { if (L->hookmask) { L->top = ra; - halfProtect(luaD_poscall(L, ci, 0)); /* no hurry... */ + halfProtectNT(luaD_poscall(L, ci, 0)); /* no hurry... */ } else { /* do the 'poscall' here */ int nres = ci->nresults; @@ -1634,7 +1637,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmcase(OP_RETURN1) { if (L->hookmask) { L->top = ra + 1; - halfProtect(luaD_poscall(L, ci, 1)); /* no hurry... */ + halfProtectNT(luaD_poscall(L, ci, 1)); /* no hurry... */ } else { /* do the 'poscall' here */ int nres = ci->nresults; diff --git a/testes/api.lua b/testes/api.lua index 8f4e89ac..5da03641 100644 --- a/testes/api.lua +++ b/testes/api.lua @@ -354,8 +354,11 @@ assert(to("topointer", nil) == null) assert(to("topointer", "abc") ~= null) assert(to("topointer", string.rep("x", 10)) == to("topointer", string.rep("x", 10))) -- short strings -assert(to("topointer", string.rep("x", 300)) ~= - to("topointer", string.rep("x", 300))) -- long strings +do -- long strings + local s1 = string.rep("x", 300) + local s2 = string.rep("x", 300) + assert(to("topointer", s1) ~= to("topointer", s2)) +end assert(to("topointer", T.pushuserdata(20)) ~= null) assert(to("topointer", io.read) ~= null) -- light C function assert(to("topointer", hfunc) ~= null) -- "heavy" C function diff --git a/testes/strings.lua b/testes/strings.lua index 1b2b570e..2540fdef 100644 --- a/testes/strings.lua +++ b/testes/strings.lua @@ -163,11 +163,16 @@ do -- tests for '%p' format assert(string.format("%p", 4) == null) assert(string.format("%p", print) ~= null) assert(string.format("%p", coroutine.running()) ~= null) - assert(string.format("%p", {}) ~= string.format("%p", {})) + do + local t1 = {}; local t2 = {} + assert(string.format("%p", t1) ~= string.format("%p", t2)) + end assert(string.format("%p", string.rep("a", 10)) == string.format("%p", string.rep("a", 10))) -- short strings - assert(string.format("%p", string.rep("a", 300)) ~= - string.format("%p", string.rep("a", 300))) -- long strings + do -- long strings + local s1 = string.rep("a", 300); local s2 = string.rep("a", 300) + assert(string.format("%p", s1) ~= string.format("%p", s2)) + end assert(#string.format("%90p", {}) == 90) end -- cgit v1.2.3-55-g6feb