commit d36a31e6739bcd39c84f637344227af87cfd0ee5
parent 024a6071cac749504e0b26a915bda4f52c41a892
Author: Roberto Ierusalimschy <roberto@inf.puc-rio.br>
Date: Thu, 18 Jul 2019 14:57:48 -0300
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.
Diffstat:
4 files changed, 35 insertions(+), 15 deletions(-)
diff --git 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
@@ -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
@@ -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
@@ -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