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.
This commit is contained in:
Roberto Ierusalimschy 2019-07-18 14:58:15 -03:00
parent 024a6071ca
commit d36a31e673
4 changed files with 35 additions and 15 deletions

23
lmem.c
View File

@ -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)

9
lvm.c
View File

@ -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;

View File

@ -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

View File

@ -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