From ff39a6c6f5cae8bb4750369898f44e1a806f41f8 Mon Sep 17 00:00:00 2001 From: jow Date: Thu, 9 Jul 2009 17:45:23 +0000 Subject: package/lua: implement memory limits, scripts can use get_memory_limit() and set_memory_limit() to cap the maximum ram usage git-svn-id: svn://svn.openwrt.org/openwrt/trunk@16753 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- package/lua/patches/040-memory-limits.patch | 277 ++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 package/lua/patches/040-memory-limits.patch (limited to 'package/lua/patches/040-memory-limits.patch') diff --git a/package/lua/patches/040-memory-limits.patch b/package/lua/patches/040-memory-limits.patch new file mode 100644 index 0000000000..52bae6ae85 --- /dev/null +++ b/package/lua/patches/040-memory-limits.patch @@ -0,0 +1,277 @@ +--- 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,82 @@ + #include "llimits.h" + + ++typedef struct { ++ char *name; ++ lua_State *L; ++ size_t memused; ++ size_t peak_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) { ++#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 limit = luaL_checknumber(L, 1); ++ script_info_t *info; ++ lua_getallocf(L, (void *)(&info)); ++ ++ if( limit >= 0 ) ++ info->max_memused = limit; ++ ++ lua_pushnumber(L, limit); ++ return 1; ++} ++ ++static int get_memory_limit(lua_State *L) ++{ ++ script_info_t *info; ++ lua_getallocf(L, (void *)(&info)); ++ lua_pushnumber(L, info->max_memused); ++ return 1; ++} ++ ++ + static lua_State *globalL = NULL; + + static const char *progname = LUA_PROGNAME; +@@ -377,11 +453,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 +489,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; + } + -- cgit v1.2.3