Procházet zdrojové kódy

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.
Roberto Ierusalimschy před 6 roky
rodič
revize
d36a31e673
4 změnil soubory, kde provedl 35 přidání a 15 odebrání
  1. 16 7
      lmem.c
  2. 6 3
      lvm.c
  3. 5 2
      testes/api.lua
  4. 8 3
      testes/strings.lua

+ 16 - 7
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)

+ 6 - 3
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;

+ 5 - 2
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

+ 8 - 3
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