From 085f35da73ae63357d9fc65eae002f3c1f27aeaf Mon Sep 17 00:00:00 2001 From: Johny Mattsson Date: Fri, 6 Apr 2018 22:52:03 +1000 Subject: [PATCH] Tie in the EGC with the SDK's heap knowledge. (#2319) Added `node.egc.meminfo()` to expose LVM usage (to make the regular `node.egc.ON_MEM_LIMIT` option usable). Extended the `node.egc.ON_MEM_LIMIT` option to also take negative limits, in which case that's taken as a request to keep a certain amount of heap available for non-Lua use. --- app/lua/lauxlib.c | 6 ++++++ app/lua/legc.c | 2 +- app/lua/legc.h | 2 +- app/lua/lstate.h | 2 +- app/modules/node.c | 14 ++++++++++++-- docs/en/modules/node.md | 19 ++++++++++++++++++- 6 files changed, 39 insertions(+), 6 deletions(-) diff --git a/app/lua/lauxlib.c b/app/lua/lauxlib.c index bc9010fa..ce82ee29 100644 --- a/app/lua/lauxlib.c +++ b/app/lua/lauxlib.c @@ -14,6 +14,7 @@ #include C_HEADER_STRING #ifndef LUA_CROSS_COMPILER #include "vfs.h" +#include "user_interface.h" #else #endif @@ -791,6 +792,11 @@ static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { } if (L != NULL && (mode & EGC_ALWAYS)) /* always collect memory if requested */ luaC_fullgc(L); +#ifndef LUA_CROSS_COMPILER + if (L != NULL && (mode & EGC_ON_MEM_LIMIT) && G(L)->memlimit < 0 && + (system_get_free_heap_size() < (-G(L)->memlimit))) + luaC_fullgc(L); +#endif if(nsize > osize && L != NULL) { #if defined(LUA_STRESS_EMERGENCY_GC) luaC_fullgc(L); diff --git a/app/lua/legc.c b/app/lua/legc.c index d2285ca4..fdee4832 100644 --- a/app/lua/legc.c +++ b/app/lua/legc.c @@ -4,7 +4,7 @@ #include "lstate.h" #include "c_types.h" -void legc_set_mode(lua_State *L, int mode, unsigned limit) { +void legc_set_mode(lua_State *L, int mode, int limit) { global_State *g = G(L); g->egcmode = mode; diff --git a/app/lua/legc.h b/app/lua/legc.h index a0d9dde3..c85ebb6b 100644 --- a/app/lua/legc.h +++ b/app/lua/legc.h @@ -11,7 +11,7 @@ #define EGC_ON_MEM_LIMIT 2 // run EGC when an upper memory limit is hit #define EGC_ALWAYS 4 // always run EGC before an allocation -void legc_set_mode(lua_State *L, int mode, unsigned limit); +void legc_set_mode(lua_State *L, int mode, int limit); #endif diff --git a/app/lua/lstate.h b/app/lua/lstate.h index 1b3c363a..97a1a4b2 100644 --- a/app/lua/lstate.h +++ b/app/lua/lstate.h @@ -82,7 +82,7 @@ typedef struct global_State { Mbuffer buff; /* temporary buffer for string concatentation */ lu_mem GCthreshold; lu_mem totalbytes; /* number of bytes currently allocated */ - lu_mem memlimit; /* maximum number of bytes that can be allocated, 0 = no limit. */ + l_mem memlimit; /* maximum number of bytes that can be allocated, 0 = no limit. <0 used with EGC_ON_MEM_LIMIT when free heap falls below -memlimit */ lu_mem estimate; /* an estimate of number of bytes actually in use */ lu_mem gcdept; /* how much GC is `behind schedule' */ int gcpause; /* size of pause between successive GCs */ diff --git a/app/modules/node.c b/app/modules/node.c index 8302ec8c..1f207465 100644 --- a/app/modules/node.c +++ b/app/modules/node.c @@ -462,14 +462,23 @@ static int node_stripdebug (lua_State *L) { // See legc.h and lecg.c. static int node_egc_setmode(lua_State* L) { unsigned mode = luaL_checkinteger(L, 1); - unsigned limit = luaL_optinteger (L, 2, 0); + int limit = luaL_optinteger (L, 2, 0); luaL_argcheck(L, mode <= (EGC_ON_ALLOC_FAILURE | EGC_ON_MEM_LIMIT | EGC_ALWAYS), 1, "invalid mode"); - luaL_argcheck(L, !(mode & EGC_ON_MEM_LIMIT) || limit>0, 1, "limit must be non-zero"); + luaL_argcheck(L, !(mode & EGC_ON_MEM_LIMIT) || limit!=0, 1, "limit must be non-zero"); legc_set_mode( L, mode, limit ); return 0; } + +// totalallocated, estimatedused = node.egc.meminfo() +static int node_egc_meminfo(lua_State *L) { + global_State *g = G(L); + lua_pushinteger(L, g->totalbytes); + lua_pushinteger(L, g->estimate); + return 2; +} + // // Lua: osprint(true/false) // Allows you to turn on the native Espressif SDK printing @@ -560,6 +569,7 @@ static int node_random (lua_State *L) { // Module function map static const LUA_REG_TYPE node_egc_map[] = { + { LSTRKEY( "meminfo" ), LFUNCVAL( node_egc_meminfo ) }, { LSTRKEY( "setmode" ), LFUNCVAL( node_egc_setmode ) }, { LSTRKEY( "NOT_ACTIVE" ), LNUMVAL( EGC_NOT_ACTIVE ) }, { LSTRKEY( "ON_ALLOC_FAILURE" ), LNUMVAL( EGC_ON_ALLOC_FAILURE ) }, diff --git a/docs/en/modules/node.md b/docs/en/modules/node.md index e34c31f3..2e1d092f 100644 --- a/docs/en/modules/node.md +++ b/docs/en/modules/node.md @@ -484,7 +484,7 @@ provides more detailed information on the EGC. - `mode` - `node.egc.NOT_ACTIVE` EGC inactive, no collection cycle will be forced in low memory situations - `node.egc.ON_ALLOC_FAILURE` Try to allocate a new block of memory, and run the garbage collector if the allocation fails. If the allocation fails even after running the garbage collector, the allocator will return with error. - - `node.egc.ON_MEM_LIMIT` Run the garbage collector when the memory used by the Lua script goes beyond an upper `limit`. If the upper limit can't be satisfied even after running the garbage collector, the allocator will return with error. + - `node.egc.ON_MEM_LIMIT` Run the garbage collector when the memory used by the Lua script goes beyond an upper `limit`. If the upper limit can't be satisfied even after running the garbage collector, the allocator will return with error. If the given limit is negative, it is interpreted as the desired amount of heap which should be left available. Whenever the free heap (as reported by `node.heap()` falls below the requested limit, the garbage collector will be run. - `node.egc.ALWAYS` Run the garbage collector before each memory allocation. If the allocation fails even after running the garbage collector, the allocator will return with error. This mode is very efficient with regards to memory savings, but it's also the slowest. - `level` in the case of `node.egc.ON_MEM_LIMIT`, this specifies the memory limit. @@ -495,6 +495,23 @@ provides more detailed information on the EGC. `node.egc.setmode(node.egc.ALWAYS, 4096) -- This is the default setting at startup.` `node.egc.setmode(node.egc.ON_ALLOC_FAILURE) -- This is the fastest activeEGC mode.` +`node.egc.setmode(node.egc.ON_MEM_LIMIT, 30720) -- Only allow the Lua runtime to allocate at most 30k, collect garbage if limit is about to be hit` +`node.egc.setmode(node.egc.ON_MEM_LIMIT, -6144) -- Try to keep at least 6k heap available for non-Lua use (e.g. network buffers)` + + +## node.egc.meminfo() + +Returns memory usage information for the Lua runtime. + +####Syntax +`total_allocated, estimated_used = node.egc.meminfo()` + +#### Parameters +None. + +#### Returns + - `total_allocated` The total number of bytes allocated by the Lua runtime. This is the number which is relevant when using the `node.egc.ON_MEM_LIMIT` option with positive limit values. + - `estimated_used` This value shows the estimated usage of the allocated memory. # node.task module