--- a/src/lapi.c +++ b/src/lapi.c @@ -716,14 +716,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); } @@ -971,7 +971,12 @@ break; } case LUA_GCCOLLECT: { - luaC_fullgc(L); + lu_mem old_thres = g->GCthreshold; + if(g->GCthreshold != MAX_LUMEM) { + g->GCthreshold = MAX_LUMEM; + luaC_fullgc(L); + g->GCthreshold = old_thres; + } break; } case LUA_GCCOUNT: { --- a/src/ldo.c +++ b/src/ldo.c @@ -494,6 +494,7 @@ struct SParser *p = cast(struct SParser *, ud); int c = luaZ_lookahead(p->z); luaC_checkGC(L); + lua_gc(L, LUA_GCSTOP, 0); /* 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))); @@ -502,6 +503,7 @@ cl->l.upvals[i] = luaF_newupval(L); setclvalue(L, L->top, cl); incr_top(L); + lua_gc(L, LUA_GCRESTART, 0); } --- a/src/lgc.c +++ b/src/lgc.c @@ -437,7 +437,10 @@ /* check size of buffer */ if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER*2) { /* buffer too big? */ size_t newsize = luaZ_sizebuffer(&g->buff) / 2; - luaZ_resizebuffer(L, &g->buff, newsize); + /* make sure newsize is larger then the buffer's in use size. */ + newsize = (luaZ_bufflen(&g->buff) > newsize) ? luaZ_bufflen(&g->buff) : newsize; + if(newsize < luaZ_sizebuffer(&g->buff)) + luaZ_resizebuffer(L, &g->buff, newsize); } } --- a/src/lstate.c +++ b/src/lstate.c @@ -118,7 +118,6 @@ 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); preinit_state(L1, G(L)); stack_init(L1, L); /* init stack */ setobj2n(L, gt(L1), gt(L)); /* share table of globals */ @@ -126,6 +125,7 @@ L1->basehookcount = L->basehookcount; L1->hook = L->hook; resethookcount(L1); + luaC_link(L, obj2gco(L1), LUA_TTHREAD); lua_assert(iswhite(obj2gco(L1))); return L1; } --- a/src/lstring.c +++ b/src/lstring.c @@ -53,6 +53,9 @@ stringtable *tb; if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char)) 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, (l+1)*sizeof(char)+sizeof(TString))); ts->tsv.len = l; ts->tsv.hash = h; @@ -61,13 +64,10 @@ ts->tsv.reserved = 0; 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; } --- a/src/ltable.c +++ b/src/ltable.c @@ -371,7 +371,6 @@ Table *luaH_new (lua_State *L, int narray, int nhash) { Table *t = luaM_new(L, Table); - luaC_link(L, obj2gco(t), LUA_TTABLE); t->metatable = NULL; t->flags = cast_byte(~0); /* temporary values (kept only if some malloc fails) */ @@ -381,6 +380,7 @@ t->node = cast(Node *, dummynode); setarrayvector(L, t, narray); setnodevector(L, t, nhash); + luaC_link(L, obj2gco(t), LUA_TTABLE); return t; } --- a/src/lvm.c +++ b/src/lvm.c @@ -375,6 +375,7 @@ 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 */ @@ -383,6 +384,7 @@ tl += l; } setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl)); + luaZ_resetbuffer(&G(L)->buff); } total -= n-1; /* got `n' strings to create 1 new */ last -= n-1; --- a/src/lua.c +++ b/src/lua.c @@ -19,6 +19,94 @@ #include "llimits.h" +typedef struct { + char *name; + lua_State *L; + size_t memused; + size_t peak_memused; + size_t gc_memused; + size_t max_memused; + int collecting; +} script_info_t; + + +static void *script_alloc(void *ud, void *ptr, size_t osize, size_t nsize) +{ + script_info_t *info=(script_info_t *)ud; + size_t old_size = info->memused; + + info->memused -= osize; + if (nsize == 0) { + free(ptr); + return NULL; + } + info->memused += nsize; + if(info->max_memused > 0 && nsize > osize && + (info->memused >= info->max_memused || info->memused >= info->gc_memused)) { +#ifdef LOW_MEM_DEBUG + printf("LOW MEM: 1 osize=%zd, nsize=%zd, used=%zu, peak=%zu, need=%zd\n", osize, nsize, + info->memused, info->peak_memused, (info->memused - info->max_memused)); +#endif + info->memused = old_size; + /* don't allow a recursive garbage collection call. */ + if(info->collecting != 0) { + return NULL; + } + info->collecting = 1; + /* try to free memory by collecting garbage. */ + lua_gc(info->L, LUA_GCCOLLECT, 0); + info->collecting = 0; +#ifdef LOW_MEM_DEBUG + printf("LOW MEM: 2 used=%zu, peak=%zu\n", info->memused, info->peak_memused); +#endif + /* check memory usage again. */ + old_size = info->memused; + info->memused -= osize; + info->memused += nsize; + if(info->memused >= info->max_memused) { + info->memused = old_size; +#ifdef LOW_MEM_DEBUG + printf("OUT OF MEMORY: memused=%zd, osize=%zd, nsize=%zd\n", info->memused, osize, nsize); +#endif + return NULL; + } + } + if(info->memused > info->peak_memused) info->peak_memused = info->memused; + return realloc(ptr, nsize); +} + +static int set_memory_limit(lua_State *L) +{ + int hardlimit = luaL_checknumber(L, 1); + int softlimit = luaL_optnumber(L, 2, 0); + + script_info_t *info; + lua_getallocf(L, (void *)(&info)); + + if( hardlimit >= 0 ) + { + if( softlimit <= 0 ) + softlimit = (int)((float)hardlimit * 0.75); + + info->max_memused = hardlimit; + info->gc_memused = softlimit; + } + + lua_pushnumber(L, hardlimit); + lua_pushnumber(L, softlimit); + return 2; +} + +static int get_memory_limit(lua_State *L) +{ + script_info_t *info; + lua_getallocf(L, (void *)(&info)); + lua_pushnumber(L, info->max_memused); + lua_pushnumber(L, info->gc_memused); + return 2; +} + + static lua_State *globalL = NULL; static const char *progname = LUA_PROGNAME; @@ -377,11 +465,28 @@ int main (int argc, char **argv) { int status; struct Smain s; - lua_State *L = lua_open(); /* create state */ + script_info_t *info; + + info = (script_info_t *)calloc(1, sizeof(script_info_t)); + info->max_memused = 0; + info->collecting = 0; + info->name = argv[0]; + info->memused = 0; + info->peak_memused = 0; + + lua_State *L = lua_newstate(script_alloc, info); + if (L == NULL) { l_message(argv[0], "cannot create state: not enough memory"); return EXIT_FAILURE; } + + info->L = L; + + luaL_openlibs(L); + lua_register(L, "set_memory_limit", set_memory_limit); + lua_register(L, "get_memory_limit", get_memory_limit); + /* Checking 'sizeof(lua_Integer)' cannot be made in preprocessor on all compilers. */ #ifdef LNUM_INT16 @@ -396,6 +501,14 @@ status = lua_cpcall(L, &pmain, &s); report(L, status); lua_close(L); + +#ifdef LOW_MEM_DEBUG + printf("%s: memused=%zd, peak_memused=%zd\n", info->name, + info->memused, info->peak_memused); +#endif + + free(info); + return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS; }