Lua Add Emergency Garbage Collector

Derivative of work by RobertGabrielJakabosky
 http://lua-users.org/wiki/EmergencyGarbageCollector

I've only implemented the not enough memory part and
 expanded this idea to adding a mechanism to signal
 the OOM condition of the plugin buffer which allows us to only
 grab the playback buffer after garbage collection fails
 (SO THE MUSIC KEEPS PLAYING AS LONG AS POSSIBLE)

Change-Id: I684fb98b540ffc01f7ba324ab5b761ceb59b9f9b
diff --git a/apps/plugins/lua/SOURCES b/apps/plugins/lua/SOURCES
index 93fa5e9..481bf76 100644
--- a/apps/plugins/lua/SOURCES
+++ b/apps/plugins/lua/SOURCES
@@ -24,6 +24,7 @@
 ltablib.c
 ltm.c
 lundump.c
+lua_user.c
 lvm.c
 lzio.c
 rockaux.c
diff --git a/apps/plugins/lua/lapi.c b/apps/plugins/lua/lapi.c
index 487d6b1..6426cd9 100644
--- a/apps/plugins/lua/lapi.c
+++ b/apps/plugins/lua/lapi.c
@@ -547,7 +547,9 @@
   lua_lock(L);
   t = index2adr(L, idx);
   api_checkvalidindex(L, t);
+  fixedstack(L);
   setsvalue(L, &key, luaS_new(L, k));
+  unfixedstack(L);
   luaV_gettable(L, t, &key, L->top);
   api_incr_top(L);
   lua_unlock(L);
@@ -656,14 +658,14 @@
 
 LUA_API void lua_setfield (lua_State *L, int idx, const char *k) {
   StkId t;
-  TValue key;
   lua_lock(L);
   api_checknelems(L, 1);
   t = index2adr(L, idx);
   api_checkvalidindex(L, t);
-  setsvalue(L, &key, luaS_new(L, k));
-  luaV_settable(L, t, &key, L->top - 1);
-  L->top--;  /* pop value */
+  setsvalue2s(L, L->top, luaS_new(L, k));
+  api_incr_top(L);
+  luaV_settable(L, t, L->top - 1, L->top - 2);
+  L->top -= 2;  /* pop key and value */
   lua_unlock(L);
 }
 
@@ -674,7 +676,9 @@
   api_checknelems(L, 2);
   t = index2adr(L, idx);
   api_check(L, ttistable(t));
+  fixedstack(L);
   setobj2t(L, luaH_set(L, hvalue(t), L->top-2), L->top-1);
+  unfixedstack(L);
   luaC_barriert(L, hvalue(t), L->top-1);
   L->top -= 2;
   lua_unlock(L);
@@ -687,7 +691,9 @@
   api_checknelems(L, 1);
   o = index2adr(L, idx);
   api_check(L, ttistable(o));
+  fixedstack(L);
   setobj2t(L, luaH_setnum(L, hvalue(o), n), L->top-1);
+  unfixedstack(L);
   luaC_barriert(L, hvalue(o), L->top-1);
   L->top--;
   lua_unlock(L);
@@ -903,11 +909,11 @@
   g = G(L);
   switch (what) {
     case LUA_GCSTOP: {
-      g->GCthreshold = MAX_LUMEM;
+      set_block_gc(L);
       break;
     }
     case LUA_GCRESTART: {
-      g->GCthreshold = g->totalbytes;
+      unset_block_gc(L);
       break;
     }
     case LUA_GCCOLLECT: {
@@ -924,6 +930,10 @@
       break;
     }
     case LUA_GCSTEP: {
+      if(is_block_gc(L)) {
+        res = 1; /* gc is block so we need to pretend that the collection cycle finished. */
+        break;
+      }
       lu_mem a = (cast(lu_mem, data) << 10);
       if (a <= g->totalbytes)
         g->GCthreshold = g->totalbytes - a;
diff --git a/apps/plugins/lua/lauxlib.c b/apps/plugins/lua/lauxlib.c
index 2e4b3b1..acd7e0e 100644
--- a/apps/plugins/lua/lauxlib.c
+++ b/apps/plugins/lua/lauxlib.c
@@ -13,7 +13,6 @@
 #include <string.h>
 #include "lstring.h" /* ROCKLUA ADDED */
 
-
 /* This file uses only the official API of Lua.
 ** Any function declared here could be written as an application function.
 ** Note ** luaS_newlloc breaks this guarantee ROCKLUA ADDED
@@ -34,7 +33,9 @@
 #define abs_index(L, i)		((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \
 					lua_gettop(L) + (i) + 1)
 
-
+#ifndef LUA_OOM
+  #define LUA_OOM(L) {}
+#endif
 /*
 ** {======================================================
 ** Error-report functions
@@ -756,14 +757,30 @@
 
 
 static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
-  (void)ud;
-  (void)osize;
+  (void) osize;
+  lua_State *L = (lua_State *)ud;
+  void *nptr;
+
   if (nsize == 0) {
     free(ptr);
     return NULL;
   }
-  else
-    return realloc(ptr, nsize);
+
+  nptr = realloc(ptr, nsize);
+  if (nptr == NULL) {
+    if(L != NULL)
+    {
+      luaC_fullgc(L); /* emergency full collection. */
+      nptr = realloc(ptr, nsize); /* try allocation again */
+    }
+
+    if (nptr == NULL) {
+      LUA_OOM(L); /* if defined.. signal OOM condition */
+      nptr = realloc(ptr, nsize); /* try allocation again */
+    }
+  }
+
+  return nptr;
 }
 
 
@@ -779,6 +796,7 @@
 
 LUALIB_API lua_State *luaL_newstate (void) {
   lua_State *L = lua_newstate(l_alloc, NULL);
+  lua_setallocf(L, l_alloc, L); /* allocator needs lua_State. */
   if (L) lua_atpanic(L, &panic);
   return L;
 }
diff --git a/apps/plugins/lua/ldo.c b/apps/plugins/lua/ldo.c
index 43266de..7eccc54 100644
--- a/apps/plugins/lua/ldo.c
+++ b/apps/plugins/lua/ldo.c
@@ -51,11 +51,13 @@
 void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
   switch (errcode) {
     case LUA_ERRMEM: {
-      setsvalue2s(L, oldtop, luaS_newliteral(L, MEMERRMSG));
+      ptrdiff_t oldtopr = savestack(L, oldtop);
+      setsvalue2s(L, restorestack(L, oldtopr), luaS_newliteral(L, MEMERRMSG));
       break;
     }
     case LUA_ERRERR: {
-      setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
+      ptrdiff_t oldtopr = savestack(L, oldtop);
+      setsvalue2s(L, restorestack(L, oldtopr), luaS_newliteral(L, "error in error handling"));
       break;
     }
     case LUA_ERRSYNTAX:
@@ -92,6 +94,8 @@
 
 
 void luaD_throw (lua_State *L, int errcode) {
+  unfixedstack(L); /* make sure the fixedstack & block_gc flags get reset. */
+  unset_block_gc(L);
   if (L->errorJmp) {
     L->errorJmp->status = errcode;
     LUAI_THROW(L, L->errorJmp);
@@ -208,7 +212,9 @@
 static StkId adjust_varargs (lua_State *L, Proto *p, int actual) {
   int i;
   int nfixargs = p->numparams;
+#if defined(LUA_COMPAT_VARARG)
   Table *htab = NULL;
+#endif
   StkId base, fixed;
   for (; actual < nfixargs; ++actual)
     setnilvalue(L->top++);
@@ -219,10 +225,15 @@
     luaC_checkGC(L);
     luaD_checkstack(L, p->maxstacksize);
     htab = luaH_new(L, nvar, 1);  /* create `arg' table */
+    sethvalue2s(L, L->top, htab); /* put table on stack */
+    incr_top(L);
+    fixedstack(L);
     for (i=0; i<nvar; i++)  /* put extra arguments into `arg' table */
-      setobj2n(L, luaH_setnum(L, htab, i+1), L->top - nvar + i);
+      setobj2n(L, luaH_setnum(L, htab, i+1), L->top - 1 - nvar + i);
+    unfixedstack(L);
     /* store counter in field `n' */
     setnvalue(luaH_setstr(L, htab, luaS_newliteral(L, "n")), cast_num(nvar));
+    L->top--; /* remove table from stack */
   }
 #endif
   /* move fixed parameters to final position */
@@ -232,11 +243,13 @@
     setobjs2s(L, L->top++, fixed+i);
     setnilvalue(fixed+i);
   }
+#if defined(LUA_COMPAT_VARARG)
   /* add `arg' parameter */
   if (htab) {
     sethvalue(L, L->top++, htab);
     lua_assert(iswhite(obj2gco(htab)));
   }
+#endif
   return base;
 }
 
@@ -495,6 +508,7 @@
   struct SParser *p = cast(struct SParser *, ud);
   int c = luaZ_lookahead(p->z);
   luaC_checkGC(L);
+  set_block_gc(L);  /* stop collector during parsing */
   tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z,
                                                              &p->buff, p->name);
   cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L)));
@@ -503,6 +517,7 @@
     cl->l.upvals[i] = luaF_newupval(L);
   setclvalue(L, L->top, cl);
   incr_top(L);
+  unset_block_gc(L);
 }
 
 
diff --git a/apps/plugins/lua/lfunc.c b/apps/plugins/lua/lfunc.c
index 813e88f..d2ce63d 100644
--- a/apps/plugins/lua/lfunc.c
+++ b/apps/plugins/lua/lfunc.c
@@ -66,7 +66,6 @@
   }
   uv = luaM_new(L, UpVal);  /* not found: create a new one */
   uv->tt = LUA_TUPVAL;
-  uv->marked = luaC_white(g);
   uv->v = level;  /* current value lives in the stack */
   uv->next = *pp;  /* chain it in the proper position */
   *pp = obj2gco(uv);
@@ -74,6 +73,7 @@
   uv->u.l.next = g->uvhead.u.l.next;
   uv->u.l.next->u.l.prev = uv;
   g->uvhead.u.l.next = uv;
+  luaC_marknew(L, obj2gco(uv));
   lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
   return uv;
 }
diff --git a/apps/plugins/lua/lgc.c b/apps/plugins/lua/lgc.c
index 98194c1..17b64a1 100644
--- a/apps/plugins/lua/lgc.c
+++ b/apps/plugins/lua/lgc.c
@@ -58,6 +58,9 @@
 
 #define setthreshold(g)  (g->GCthreshold = (g->estimate/100) * g->gcpause)
 
+#ifndef yield
+  #define yield() {}
+#endif
 
 static void removeentry (Node *n) {
   lua_assert(ttisnil(gval(n)));
@@ -232,8 +235,10 @@
     int i;
     lua_assert(cl->l.nupvalues == cl->l.p->nups);
     markobject(g, cl->l.p);
-    for (i=0; i<cl->l.nupvalues; i++)  /* mark its upvalues */
-      markobject(g, cl->l.upvals[i]);
+    for (i=0; i<cl->l.nupvalues; i++) { /* mark its upvalues */
+      if(cl->l.upvals[i])
+        markobject(g, cl->l.upvals[i]);
+    }
   }
 }
 
@@ -258,6 +263,7 @@
   CallInfo *ci;
   markvalue(g, gt(l));
   lim = l->top;
+  if(l->stack == NULL) return; /* no stack to traverse */
   for (ci = l->base_ci; ci <= l->ci; ci++) {
     lua_assert(ci->top <= l->stack_last);
     if (lim < ci->top) lim = ci->top;
@@ -266,7 +272,8 @@
     markvalue(g, o);
   for (; o <= lim; o++)
     setnilvalue(o);
-  checkstacksizes(l, lim);
+  if (!isfixedstack(l)) /* if stack size is fixed, can't resize it. */
+    checkstacksizes(l, lim);
 }
 
 
@@ -419,8 +426,6 @@
     else {  /* must erase `curr' */
       lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT));
       *p = curr->gch.next;
-      if (curr == g->rootgc)  /* is the first element of the list? */
-        g->rootgc = curr->gch.next;  /* adjust first */
       freeobj(L, curr);
     }
   }
@@ -434,6 +439,8 @@
   if (g->strt.nuse < cast(lu_int32, g->strt.size/4) &&
       g->strt.size > MINSTRTABSIZE*2)
     luaS_resize(L, g->strt.size/2);  /* table is too big */
+  /* it is not safe to re-size the buffer if it is in use. */
+  if (luaZ_bufflen(&g->buff) > 0) return;
   /* check size of buffer */
   if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER*2) {  /* buffer too big? */
     size_t newsize = luaZ_sizebuffer(&g->buff) / 2;
@@ -552,6 +559,15 @@
   g->estimate = g->totalbytes - udsize;  /* first estimate */
 }
 
+static void sweepstrstep (global_State *g, lua_State *L) {
+  lu_mem old = g->totalbytes;
+  sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]);
+  if (g->sweepstrgc >= g->strt.size)  /* nothing more to sweep? */
+    g->gcstate = GCSsweep;  /* end sweep-string phase */
+  lua_assert(old >= g->totalbytes);
+  g->estimate -= old - g->totalbytes;
+}
+
 
 static l_mem singlestep (lua_State *L) {
   global_State *g = G(L);
@@ -570,12 +586,7 @@
       }
     }
     case GCSsweepstring: {
-      lu_mem old = g->totalbytes;
-      sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]);
-      if (g->sweepstrgc >= g->strt.size)  /* nothing more to sweep? */
-        g->gcstate = GCSsweep;  /* end sweep-string phase */
-      lua_assert(old >= g->totalbytes);
-      g->estimate -= old - g->totalbytes;
+      sweepstrstep(g, L);
       return GCSWEEPCOST;
     }
     case GCSsweep: {
@@ -609,10 +620,14 @@
 
 void luaC_step (lua_State *L) {
   global_State *g = G(L);
+  if(is_block_gc(L)) return;
+  set_block_gc(L);
   l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul;
   if (lim == 0)
     lim = (MAX_LUMEM-1)/2;  /* no limit */
   g->gcdept += g->totalbytes - g->GCthreshold;
+  if (g->estimate > g->totalbytes)
+    g->estimate = g->totalbytes;
   do {
     lim -= singlestep(L);
     if (g->gcstate == GCSpause)
@@ -629,11 +644,23 @@
   else {
     setthreshold(g);
   }
+  unset_block_gc(L);
 }
 
 
+int luaC_sweepstrgc (lua_State *L) {
+  global_State *g = G(L);
+  if (g->gcstate == GCSsweepstring) {
+    sweepstrstep(g, L);
+    return (g->gcstate == GCSsweepstring) ? 1 : 0;
+  }
+  return 0;
+}
+
 void luaC_fullgc (lua_State *L) {
   global_State *g = G(L);
+  if(is_block_gc(L)) return;
+  set_block_gc(L);
   if (g->gcstate <= GCSpropagate) {
     /* reset sweep marks to sweep all elements (returning them to white) */
     g->sweepstrgc = 0;
@@ -649,12 +676,15 @@
   while (g->gcstate != GCSfinalize) {
     lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep);
     singlestep(L);
+    yield();
   }
   markroot(L);
   while (g->gcstate != GCSpause) {
     singlestep(L);
+    yield();
   }
   setthreshold(g);
+  unset_block_gc(L);
 }
 
 
@@ -682,6 +712,14 @@
 }
 
 
+void luaC_marknew (lua_State *L, GCObject *o) {
+  global_State *g = G(L);
+  o->gch.marked = luaC_white(g);
+  if (g->gcstate == GCSpropagate)
+    reallymarkobject(g, o);  /* mark new objects as gray during propagate state. */
+}
+
+
 void luaC_link (lua_State *L, GCObject *o, lu_byte tt) {
   global_State *g = G(L);
   o->gch.next = g->rootgc;
diff --git a/apps/plugins/lua/lgc.h b/apps/plugins/lua/lgc.h
index 5123ccb..115008d 100644
--- a/apps/plugins/lua/lgc.h
+++ b/apps/plugins/lua/lgc.h
@@ -37,12 +37,30 @@
 #define test2bits(x,b1,b2)	testbits(x, (bit2mask(b1, b2)))
 
 
+/*
+** Possible Garbage Collector flags.
+** Layout for bit use in 'gsflags' field in global_State structure.
+** bit 0 - Protect GC from recursive calls.
+** bit 1 - Don't try to shrink string table if EGC was called during a string table resize.
+*/
+#define GCFlagsNone          0
+#define GCBlockGCBit         0
+#define GCResizingStringsBit 1
+
+
+#define is_block_gc(L)    testbit(G(L)->gcflags, GCBlockGCBit)
+#define set_block_gc(L)   l_setbit(G(L)->gcflags, GCBlockGCBit)
+#define unset_block_gc(L) resetbit(G(L)->gcflags, GCBlockGCBit)
+#define is_resizing_strings_gc(L)    testbit(G(L)->gcflags, GCResizingStringsBit)
+#define set_resizing_strings_gc(L)   l_setbit(G(L)->gcflags, GCResizingStringsBit)
+#define unset_resizing_strings_gc(L) resetbit(G(L)->gcflags, GCResizingStringsBit)
 
 /*
 ** Layout for bit use in `marked' field:
 ** bit 0 - object is white (type 0)
 ** bit 1 - object is white (type 1)
 ** bit 2 - object is black
+** bit 3 - for thread: Don't resize thread's stack
 ** bit 3 - for userdata: has been finalized
 ** bit 3 - for tables: has weak keys
 ** bit 4 - for tables: has weak values
@@ -54,6 +72,7 @@
 #define WHITE0BIT	0
 #define WHITE1BIT	1
 #define BLACKBIT	2
+#define FIXEDSTACKBIT	3
 #define FINALIZEDBIT	3
 #define KEYWEAKBIT	3
 #define VALUEWEAKBIT	4
@@ -76,6 +95,9 @@
 
 #define luaC_white(g)	cast(lu_byte, (g)->currentwhite & WHITEBITS)
 
+#define isfixedstack(x)	testbit((x)->marked, FIXEDSTACKBIT)
+#define fixedstack(x)	l_setbit((x)->marked, FIXEDSTACKBIT)
+#define unfixedstack(x)	resetbit((x)->marked, FIXEDSTACKBIT)
 
 #define luaC_checkGC(L) { \
   condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); \
@@ -101,6 +123,8 @@
 LUAI_FUNC void luaC_freeall (lua_State *L);
 LUAI_FUNC void luaC_step (lua_State *L);
 LUAI_FUNC void luaC_fullgc (lua_State *L);
+LUAI_FUNC int luaC_sweepstrgc (lua_State *L);
+LUAI_FUNC void luaC_marknew (lua_State *L, GCObject *o);
 LUAI_FUNC void luaC_link (lua_State *L, GCObject *o, lu_byte tt);
 LUAI_FUNC void luaC_linkupval (lua_State *L, UpVal *uv);
 LUAI_FUNC void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v);
diff --git a/apps/plugins/lua/lobject.h b/apps/plugins/lua/lobject.h
index 26d2a81..028ff03 100644
--- a/apps/plugins/lua/lobject.h
+++ b/apps/plugins/lua/lobject.h
@@ -117,42 +117,48 @@
 #define setnilvalue(obj) ((obj)->tt=LUA_TNIL)
 
 #define setnvalue(obj,x) \
-  { TValue *i_o=(obj); i_o->value.n=(x); i_o->tt=LUA_TNUMBER; }
+  { lua_Number i_x = (x); TValue *i_o=(obj); i_o->value.n=i_x; i_o->tt=LUA_TNUMBER; }
 
 #define setpvalue(obj,x) \
-  { TValue *i_o=(obj); i_o->value.p=(x); i_o->tt=LUA_TLIGHTUSERDATA; }
+  { void *i_x = (x); TValue *i_o=(obj); i_o->value.p=i_x; i_o->tt=LUA_TLIGHTUSERDATA; }
 
 #define setbvalue(obj,x) \
-  { TValue *i_o=(obj); i_o->value.b=(x); i_o->tt=LUA_TBOOLEAN; }
+  { int i_x = (x); TValue *i_o=(obj); i_o->value.b=i_x; i_o->tt=LUA_TBOOLEAN; }
 
 #define setsvalue(L,obj,x) \
-  { TValue *i_o=(obj); \
-    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TSTRING; \
+  { GCObject *i_x = cast(GCObject *, (x)); \
+    TValue *i_o=(obj); \
+    i_o->value.gc=i_x; i_o->tt=LUA_TSTRING; \
     checkliveness(G(L),i_o); }
 
 #define setuvalue(L,obj,x) \
-  { TValue *i_o=(obj); \
-    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUSERDATA; \
+  { GCObject *i_x = cast(GCObject *, (x)); \
+    TValue *i_o=(obj); \
+    i_o->value.gc=i_x; i_o->tt=LUA_TUSERDATA; \
     checkliveness(G(L),i_o); }
 
 #define setthvalue(L,obj,x) \
-  { TValue *i_o=(obj); \
-    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTHREAD; \
+  { GCObject *i_x = cast(GCObject *, (x)); \
+    TValue *i_o=(obj); \
+    i_o->value.gc=i_x; i_o->tt=LUA_TTHREAD; \
     checkliveness(G(L),i_o); }
 
 #define setclvalue(L,obj,x) \
-  { TValue *i_o=(obj); \
-    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TFUNCTION; \
+  { GCObject *i_x = cast(GCObject *, (x)); \
+    TValue *i_o=(obj); \
+    i_o->value.gc=i_x; i_o->tt=LUA_TFUNCTION; \
     checkliveness(G(L),i_o); }
 
 #define sethvalue(L,obj,x) \
-  { TValue *i_o=(obj); \
-    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTABLE; \
+  { GCObject *i_x = cast(GCObject *, (x)); \
+    TValue *i_o=(obj); \
+    i_o->value.gc=i_x; i_o->tt=LUA_TTABLE; \
     checkliveness(G(L),i_o); }
 
 #define setptvalue(L,obj,x) \
-  { TValue *i_o=(obj); \
-    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TPROTO; \
+  { GCObject *i_x = cast(GCObject *, (x)); \
+    TValue *i_o=(obj); \
+    i_o->value.gc=i_x; i_o->tt=LUA_TPROTO; \
     checkliveness(G(L),i_o); }
 
 
diff --git a/apps/plugins/lua/lparser.c b/apps/plugins/lua/lparser.c
index dda7488..d002e96 100644
--- a/apps/plugins/lua/lparser.c
+++ b/apps/plugins/lua/lparser.c
@@ -383,14 +383,18 @@
 Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {
   struct LexState lexstate;
   struct FuncState funcstate;
+  TString *tname = luaS_new(L, name);
+  setsvalue2s(L, L->top, tname);  /* protect name */
+  incr_top(L);
   lexstate.buff = buff;
-  luaX_setinput(L, &lexstate, z, luaS_new(L, name));
+  luaX_setinput(L, &lexstate, z, tname);
   open_func(&lexstate, &funcstate);
   funcstate.f->is_vararg = VARARG_ISVARARG;  /* main func. is always vararg */
   luaX_next(&lexstate);  /* read first token */
   chunk(&lexstate);
   check(&lexstate, TK_EOS);
   close_func(&lexstate);
+  L->top--; /* remove 'name' from stack */
   lua_assert(funcstate.prev == NULL);
   lua_assert(funcstate.f->nups == 0);
   lua_assert(lexstate.fs == NULL);
diff --git a/apps/plugins/lua/lstate.c b/apps/plugins/lua/lstate.c
index 4313b83..67921d8 100644
--- a/apps/plugins/lua/lstate.c
+++ b/apps/plugins/lua/lstate.c
@@ -119,6 +119,8 @@
 lua_State *luaE_newthread (lua_State *L) {
   lua_State *L1 = tostate(luaM_malloc(L, state_size(lua_State)));
   luaC_link(L, obj2gco(L1), LUA_TTHREAD);
+  setthvalue(L, L->top, L1); /* put thread on stack */
+  incr_top(L);
   preinit_state(L1, G(L));
   stack_init(L1, L);  /* init stack */
   setobj2n(L, gt(L1), gt(L));  /* share table of globals */
@@ -126,7 +128,8 @@
   L1->basehookcount = L->basehookcount;
   L1->hook = L->hook;
   resethookcount(L1);
-  lua_assert(iswhite(obj2gco(L1)));
+  lua_assert(!isdead(G(L), obj2gco(L1)));
+  L->top--; /* remove thread from stack */
   return L1;
 }
 
@@ -160,6 +163,7 @@
   g->uvhead.u.l.prev = &g->uvhead;
   g->uvhead.u.l.next = &g->uvhead;
   g->GCthreshold = 0;  /* mark it as unfinished state */
+  g->estimate = 0;
   g->strt.size = 0;
   g->strt.nuse = 0;
   g->strt.hash = NULL;
@@ -167,6 +171,7 @@
   luaZ_initbuffer(L, &g->buff);
   g->panic = NULL;
   g->gcstate = GCSpause;
+  g->gcflags = GCFlagsNone;
   g->rootgc = obj2gco(L);
   g->sweepstrgc = 0;
   g->sweepgc = &g->rootgc;
diff --git a/apps/plugins/lua/lstate.h b/apps/plugins/lua/lstate.h
index 94a6249..82431bc 100644
--- a/apps/plugins/lua/lstate.h
+++ b/apps/plugins/lua/lstate.h
@@ -71,6 +71,7 @@
   void *ud;         /* auxiliary data to `frealloc' */
   lu_byte currentwhite;
   lu_byte gcstate;  /* state of garbage collector */
+  lu_byte gcflags;  /* flags for the garbage collector */
   int sweepstrgc;  /* position of sweep in `strt' */
   GCObject *rootgc;  /* list of all collectable objects */
   GCObject **sweepgc;  /* position of sweep in `rootgc' */
diff --git a/apps/plugins/lua/lstring.c b/apps/plugins/lua/lstring.c
index bf0536e..6d73f1e 100644
--- a/apps/plugins/lua/lstring.c
+++ b/apps/plugins/lua/lstring.c
@@ -22,30 +22,34 @@
 
 
 void luaS_resize (lua_State *L, int newsize) {
-  GCObject **newhash;
   stringtable *tb;
   int i;
-  if (G(L)->gcstate == GCSsweepstring)
-    return;  /* cannot resize during GC traverse */
-  newhash = luaM_newvector(L, newsize, GCObject *);
   tb = &G(L)->strt;
-  for (i=0; i<newsize; i++) newhash[i] = NULL;
+  if (luaC_sweepstrgc(L) || newsize == tb->size || is_resizing_strings_gc(L))
+    return;  /* cannot resize during GC traverse or doesn't need to be resized */
+  set_resizing_strings_gc(L);
+  if (newsize > tb->size) {
+    luaM_reallocvector(L, tb->hash, tb->size, newsize, GCObject *);
+    for (i=tb->size; i<newsize; i++) tb->hash[i] = NULL;
+  }
   /* rehash */
   for (i=0; i<tb->size; i++) {
     GCObject *p = tb->hash[i];
+    tb->hash[i] = NULL;
     while (p) {  /* for each node in the list */
       GCObject *next = p->gch.next;  /* save next */
       unsigned int h = gco2ts(p)->hash;
       int h1 = lmod(h, newsize);  /* new position */
       lua_assert(cast_int(h%newsize) == lmod(h, newsize));
-      p->gch.next = newhash[h1];  /* chain it */
-      newhash[h1] = p;
+      p->gch.next = tb->hash[h1];  /* chain it */
+      tb->hash[h1] = p;
       p = next;
     }
   }
-  luaM_freearray(L, tb->hash, tb->size, TString *);
+  if (newsize < tb->size)
+    luaM_reallocvector(L, tb->hash, tb->size, newsize, GCObject *);
   tb->size = newsize;
-  tb->hash = newhash;
+  unset_resizing_strings_gc(L);
 }
 
 
@@ -55,6 +59,9 @@
   stringtable *tb;
   if (l > ((MAX_SIZET - sizeof(TString))/sizeof(char)) - sizeof(""))
     luaM_toobig(L);
+  tb = &G(L)->strt;
+  if ((tb->nuse + 1) > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2)
+    luaS_resize(L, tb->size*2);  /* too crowded */
   ts = cast(TString *, luaM_malloc(L, sizetstring(type, l)));
   ts->tsv.len = l;
   ts->tsv.hash = h;
@@ -70,13 +77,10 @@
     memcpy(ts+1, str, l*sizeof(char));
     ((char *)(ts+1))[l] = '\0';  /* ending 0 */
   }
-  tb = &G(L)->strt;
   h = lmod(h, tb->size);
   ts->tsv.next = tb->hash[h];  /* chain new entry */
   tb->hash[h] = obj2gco(ts);
   tb->nuse++;
-  if (tb->nuse > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2)
-    luaS_resize(L, tb->size*2);  /* too crowded */
   return ts;
 }
 
diff --git a/apps/plugins/lua/ltable.c b/apps/plugins/lua/ltable.c
index ec84f4f..31162fe 100644
--- a/apps/plugins/lua/ltable.c
+++ b/apps/plugins/lua/ltable.c
@@ -358,6 +358,8 @@
 Table *luaH_new (lua_State *L, int narray, int nhash) {
   Table *t = luaM_new(L, Table);
   luaC_link(L, obj2gco(t), LUA_TTABLE);
+  sethvalue2s(L, L->top, t); /* put table on stack */
+  incr_top(L);
   t->metatable = NULL;
   t->flags = cast_byte(~0);
   /* temporary values (kept only if some malloc fails) */
@@ -367,6 +369,7 @@
   t->node = cast(Node *, dummynode);
   setarrayvector(L, t, narray);
   setnodevector(L, t, nhash);
+  L->top--; /* remove table from stack */
   return t;
 }
 
diff --git a/apps/plugins/lua/lua_user.c b/apps/plugins/lua/lua_user.c
new file mode 100644
index 0000000..8d77dcd
--- /dev/null
+++ b/apps/plugins/lua/lua_user.c
@@ -0,0 +1,18 @@
+#include "plugin.h"
+#include "lstate.h"
+#include LUA_USER_H
+
+/* lua Out Of Memory */
+static struct lua_OOM l_oom = {NULL, 0};
+
+int set_lua_OOM(lua_State * L)
+{
+    l_oom.L = L;
+    l_oom.count++;
+    return 0;
+}
+
+struct lua_OOM *get_lua_OOM(void)
+{
+    return &l_oom;
+}
diff --git a/apps/plugins/lua/lua_user.h b/apps/plugins/lua/lua_user.h
new file mode 100644
index 0000000..f18f5e9
--- /dev/null
+++ b/apps/plugins/lua/lua_user.h
@@ -0,0 +1,14 @@
+#ifndef _LUA_USER_H_
+#define _LUA_USER_H_
+
+#define LUA_OOM(L) set_lua_OOM(L)
+
+struct lua_OOM {
+    lua_State * L;
+    int         count;
+};
+
+int set_lua_OOM(lua_State * L);
+
+struct lua_OOM* get_lua_OOM(void);
+#endif
diff --git a/apps/plugins/lua/luaconf.h b/apps/plugins/lua/luaconf.h
index 582968d..62a3ea0 100644
--- a/apps/plugins/lua/luaconf.h
+++ b/apps/plugins/lua/luaconf.h
@@ -810,7 +810,13 @@
 /*Rocklua functions*/
 #include "rockconf.h"
 
+/* heap */
+#undef LUAI_GCPAUSE /*200*/
+#define LUAI_GCPAUSE  125
+#define MINSTRTABSIZE 512 /*32*/
+
 /*else*/
+#define LUA_USER_H "lua_user.h"
 #define LUA_DISABLE_BYTECODE
 
 #endif
diff --git a/apps/plugins/lua/lvm.c b/apps/plugins/lua/lvm.c
index 35a931d..4979b6a 100644
--- a/apps/plugins/lua/lvm.c
+++ b/apps/plugins/lua/lvm.c
@@ -49,9 +49,10 @@
     return 0;
   else {
     char s[LUAI_MAXNUMBER2STR];
+    ptrdiff_t objr = savestack(L, obj);
     lua_Number n = nvalue(obj);
     lua_number2str(s, n);
-    setsvalue2s(L, obj, luaS_new(L, s));
+    setsvalue2s(L, restorestack(L, objr), luaS_new(L, s));
     return 1;
   }
 }
@@ -134,6 +135,9 @@
 void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {
   int loop;
   TValue temp;
+  setnilvalue(L->top);
+  L->top++;
+  fixedstack(L);
   for (loop = 0; loop < MAXTAGLOOP; loop++) {
     const TValue *tm;
     if (ttistable(t)) {  /* `t' is a table? */
@@ -141,6 +145,8 @@
       TValue *oldval = luaH_set(L, h, key); /* do a primitive set */
       if (!ttisnil(oldval) ||  /* result is no nil? */
           (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */
+        L->top--;
+        unfixedstack(L);
         setobj2t(L, oldval, val);
         h->flags = 0;
         luaC_barriert(L, h, val);
@@ -151,12 +157,15 @@
     else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
       luaG_typeerror(L, t, "index");
     if (ttisfunction(tm)) {
+      L->top--;
+      unfixedstack(L);
       callTM(L, tm, t, key, val);
       return;
     }
     /* else repeat with `tm' */
     setobj(L, &temp, tm);  /* avoid pointing inside table (may rehash) */
     t = &temp;
+    setobj2s(L, L->top-1, t);  /* need to protect value from EGC. */
   }
   luaG_runerror(L, "loop in settable");
 }
@@ -284,8 +293,11 @@
     StkId top = L->base + last + 1;
     int n = 2;  /* number of elements handled in this pass (at least 2) */
     if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) {
-      if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT))
+      if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT)) {
+        /* restore 'top' pointer, since stack might have been reallocted */
+        top = L->base + last + 1;
         luaG_concaterror(L, top-2, top-1);
+      }
     } else if (tsvalue(top-1)->len == 0)  /* second op is empty? */
       (void)tostring(L, top - 2);  /* result is first op (as string) */
     else {
@@ -293,12 +305,14 @@
       size_t tl = tsvalue(top-1)->len;
       char *buffer;
       int i;
+      fixedstack(L);
       /* collect total length */
       for (n = 1; n < total && tostring(L, top-n-1); n++) {
         size_t l = tsvalue(top-n-1)->len;
         if (l >= MAX_SIZET - tl) luaG_runerror(L, "string length overflow");
         tl += l;
       }
+      G(L)->buff.n = tl;
       buffer = luaZ_openspace(L, &G(L)->buff, tl);
       tl = 0;
       for (i=n; i>0; i--) {  /* concat all strings */
@@ -307,6 +321,8 @@
         tl += l;
       }
       setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl));
+      luaZ_resetbuffer(&G(L)->buff);
+      unfixedstack(L);
     }
     total -= n-1;  /* got `n' strings to create 1 new */
     last -= n-1;
@@ -332,8 +348,13 @@
       default: lua_assert(0); break;
     }
   }
-  else if (!call_binTM(L, rb, rc, ra, op))
-    luaG_aritherror(L, rb, rc);
+  else {
+    ptrdiff_t br = savestack(L, rb);
+    ptrdiff_t cr = savestack(L, rc);
+    if (!call_binTM(L, rb, rc, ra, op)) {
+      luaG_aritherror(L, restorestack(L, br), restorestack(L, cr));
+    }
+  }
 }
 
 
@@ -461,7 +482,9 @@
       case OP_NEWTABLE: {
         int b = GETARG_B(i);
         int c = GETARG_C(i);
-        sethvalue(L, ra, luaH_new(L, luaO_fb2int(b), luaO_fb2int(c)));
+        Table *h;
+        Protect(h = luaH_new(L, luaO_fb2int(b), luaO_fb2int(c)));
+        sethvalue(L, RA(i), h);
         Protect(luaC_checkGC(L));
         continue;
       }
@@ -547,9 +570,10 @@
             break;
           }
           default: {  /* try metamethod */
+            ptrdiff_t br = savestack(L, rb);
             Protect(
               if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN))
-                luaG_typeerror(L, rb, "get length of");
+                luaG_typeerror(L, restorestack(L, br), "get length of");
             )
           }
         }
@@ -723,6 +747,7 @@
         int c = GETARG_C(i);
         int last;
         Table *h;
+        fixedstack(L);
         if (n == 0) {
           n = cast_int(L->top - ra) - 1;
           L->top = L->ci->top;
@@ -738,6 +763,7 @@
           setobj2t(L, luaH_setnum(L, h, last--), val);
           luaC_barriert(L, h, val);
         }
+        unfixedstack(L);
         continue;
       }
       case OP_CLOSE: {
@@ -750,7 +776,9 @@
         int nup, j;
         p = cl->p->p[GETARG_Bx(i)];
         nup = p->nups;
+        fixedstack(L);
         ncl = luaF_newLclosure(L, nup, cl->env);
+        setclvalue(L, ra, ncl);
         ncl->l.p = p;
         for (j=0; j<nup; j++, pc++) {
           if (GET_OPCODE(*pc) == OP_GETUPVAL)
@@ -760,7 +788,7 @@
             ncl->l.upvals[j] = luaF_findupval(L, base + GETARG_B(*pc));
           }
         }
-        setclvalue(L, ra, ncl);
+        unfixedstack(L);
         Protect(luaC_checkGC(L));
         continue;
       }
diff --git a/apps/plugins/lua/lzio.h b/apps/plugins/lua/lzio.h
index 9aa9e4b..cb32e6a 100644
--- a/apps/plugins/lua/lzio.h
+++ b/apps/plugins/lua/lzio.h
@@ -27,7 +27,7 @@
   size_t buffsize;
 } Mbuffer;
 
-#define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0)
+#define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->n = 0, (buff)->buffsize = 0)
 
 #define luaZ_buffer(buff)	((buff)->buffer)
 #define luaZ_sizebuffer(buff)	((buff)->buffsize)
diff --git a/apps/plugins/lua/rockconf.h b/apps/plugins/lua/rockconf.h
index 89ab82e..6a1141f 100644
--- a/apps/plugins/lua/rockconf.h
+++ b/apps/plugins/lua/rockconf.h
@@ -63,6 +63,7 @@
 #define strcmp   rb->strcmp
 #define strcpy   rb->strcpy
 #define strlen   rb->strlen
+#define yield()  rb->yield()
 
 #endif /* _ROCKCONF_H_ */
 
diff --git a/apps/plugins/lua/rocklib.c b/apps/plugins/lua/rocklib.c
index d1ef3ad..c266516 100644
--- a/apps/plugins/lua/rocklib.c
+++ b/apps/plugins/lua/rocklib.c
@@ -304,7 +304,7 @@
             break;
     }
 
-    rb->yield();
+    yield();
     lua_pushinteger(L, result);
     return 1;
 }
@@ -382,7 +382,7 @@
             return 1;
     }
 
-    rb->yield();
+    yield();
     lua_pushinteger(L, status); /* return previous (or current) audio status */
     return 1;
 }
@@ -502,7 +502,7 @@
             break;
     }
 
-    rb->yield();
+    yield();
     return 1;
 }
 
diff --git a/apps/plugins/lua/tlsf_helper.c b/apps/plugins/lua/tlsf_helper.c
index edf32ee..097d39c 100644
--- a/apps/plugins/lua/tlsf_helper.c
+++ b/apps/plugins/lua/tlsf_helper.c
@@ -20,6 +20,7 @@
 
 #include "plugin.h"
 #include <tlsf.h>
+#include "lua.h"
 
 void *get_new_area(size_t *size)
 {
@@ -36,7 +37,8 @@
         return pluginbuf_ptr;
     }
 
-    if (audiobuf_ptr == NULL)
+    /* only grab the next area if lua already tried + failed to garbage collect*/
+    if (audiobuf_ptr == NULL && (get_lua_OOM())->count > 0)
     {
         /* grab audiobuffer */
         audiobuf_ptr = rb->plugin_get_audio_buffer(size);