From 6b347d8a1bc3248219204bd2f420d82bc3872004 Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Sat, 13 Jun 2020 03:50:03 +0100 Subject: [PATCH] cron: use Lua table rather than native array Pin a table to the registry and use that instead of manually allocating and reallocating and shuffling integers around. Thanks to @TerryE for some style suggestions. --- app/modules/cron.c | 111 +++++++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 49 deletions(-) diff --git a/app/modules/cron.c b/app/modules/cron.c index aa85e8b0..6d315c85 100644 --- a/app/modules/cron.c +++ b/app/modules/cron.c @@ -27,8 +27,7 @@ typedef struct cronent_ud { static ETSTimer cron_timer; -static int *cronent_list = 0; -static size_t cronent_count = 0; +static int cronent_table_ref; static uint64_t lcron_parsepart(lua_State *L, char *str, char **end, uint8_t min, uint8_t max) { uint64_t res = 0; @@ -86,12 +85,20 @@ static int lcron_parsedesc(lua_State *L, char *str, struct cronent_desc *desc) { static int lcron_create(lua_State *L) { // Check arguments char *strdesc = (char*)luaL_checkstring(L, 1); - void *newlist; luaL_checktype(L, 2, LUA_TFUNCTION); - // Parse description - struct cronent_desc desc; - lcron_parsedesc(L, strdesc, &desc); - // Allocate userdata + + // Grab the table of all entries onto the stack + lua_rawgeti(L, LUA_REGISTRYINDEX, cronent_table_ref); + + // Find a free index + int ix = 1; + while(lua_rawgeti(L, -1, ix) != LUA_TNIL) { + lua_pop(L, 1); + ix++; + } + lua_pop(L, 1); // pop the nil off the stack + + // Allocate userdata onto the stack cronent_ud_t *ud = lua_newuserdata(L, sizeof(cronent_ud_t)); // Set metatable luaL_getmetatable(L, "cron.entry"); @@ -100,29 +107,30 @@ static int lcron_create(lua_State *L) { lua_pushvalue(L, 2); ud->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); // Set entry - ud->desc = desc; - // Store entry - newlist = os_realloc(cronent_list, sizeof(int) * (cronent_count + 1)); - if (newlist == NULL) { - return luaL_error(L, "out of memory"); - } - lua_pushvalue(L, -1); - cronent_list = newlist; - cronent_list[cronent_count++] = luaL_ref(L, LUA_REGISTRYINDEX); - return 1; + lcron_parsedesc(L, strdesc, &ud->desc); + + // Store entry to table while preserving userdata + lua_pushvalue(L, -1); // clone userdata + lua_rawseti(L, -3, ix); // -userdata; userdata, cronent table, cb, desc + + return 1; // just the userdata } +// Finds index, leaves table at top of stack for convenience static size_t lcron_findindex(lua_State *L, cronent_ud_t *ud) { - cronent_ud_t *eud; - size_t i; - for (i = 0; i < cronent_count; i++) { - lua_rawgeti(L, LUA_REGISTRYINDEX, cronent_list[i]); - eud = lua_touserdata(L, -1); + lua_rawgeti(L, LUA_REGISTRYINDEX, cronent_table_ref); + size_t count = lua_objlen(L, -1); + + for (size_t i = 1; i <= count; i++) { + lua_rawgeti(L, -1, i); + cronent_ud_t *eud = lua_touserdata(L, -1); lua_pop(L, 1); - if (eud == ud) break; + if (eud == ud) { + return i; + } } - if (i == cronent_count) return -1; - return i; + + return count + 1; } static int lcron_schedule(lua_State *L) { @@ -131,17 +139,12 @@ static int lcron_schedule(lua_State *L) { struct cronent_desc desc; lcron_parsedesc(L, strdesc, &desc); ud->desc = desc; + size_t i = lcron_findindex(L, ud); - if (i == -1) { - void *newlist; - newlist = os_realloc(cronent_list, sizeof(int) * (cronent_count + 1)); - if (newlist == NULL) { - return luaL_error(L, "out of memory"); - } - cronent_list = newlist; - lua_pushvalue(L, 1); - cronent_list[cronent_count++] = luaL_ref(L, LUA_REGISTRYINDEX); - } + + lua_pushvalue(L, 1); // copy ud to top of stack + lua_rawseti(L, -2, i); // install into table + return 0; } @@ -157,27 +160,25 @@ static int lcron_handler(lua_State *L) { static int lcron_unschedule(lua_State *L) { cronent_ud_t *ud = luaL_checkudata(L, 1, "cron.entry"); size_t i = lcron_findindex(L, ud); - if (i == -1) return 0; - luaL_unref(L, LUA_REGISTRYINDEX, cronent_list[i]); - memmove(cronent_list + i, cronent_list + i + 1, sizeof(int) * (cronent_count - i - 1)); - cronent_count--; + + lua_pushnil(L); + lua_rawseti(L, -2, i); + return 0; } +// scheduled entries are pinned, so we cannot arrive at the __gc metamethod static int lcron_delete(lua_State *L) { cronent_ud_t *ud = luaL_checkudata(L, 1, "cron.entry"); - lcron_unschedule(L); luaL_unref(L, LUA_REGISTRYINDEX, ud->cb_ref); return 0; } static int lcron_reset(lua_State *L) { - for (size_t i = 0; i < cronent_count; i++) { - luaL_unref(L, LUA_REGISTRYINDEX, cronent_list[i]); - } - cronent_count = 0; - free(cronent_list); - cronent_list = 0; + lua_newtable(L); + luaL_unref(L, LUA_REGISTRYINDEX, cronent_table_ref); + cronent_table_ref = luaL_ref(L, LUA_REGISTRYINDEX); + return 0; } @@ -189,19 +190,27 @@ static void cron_handle_time(uint8_t mon, uint8_t dom, uint8_t dow, uint8_t hour desc.dow = ( uint8_t)1 << dow; desc.hour = (uint32_t)1 << hour; desc.min = (uint64_t)1 << min; - for (size_t i = 0; i < cronent_count; i++) { - lua_rawgeti(L, LUA_REGISTRYINDEX, cronent_list[i]); + + lua_rawgeti(L, LUA_REGISTRYINDEX, cronent_table_ref); + size_t count = lua_objlen(L, -1); + + for (size_t i = 1; i <= count; i++) { + lua_rawgeti(L, -1, i); cronent_ud_t *ent = lua_touserdata(L, -1); lua_pop(L, 1); + if ((ent->desc.mon & desc.mon ) == 0) continue; if ((ent->desc.dom & desc.dom ) == 0) continue; if ((ent->desc.dow & desc.dow ) == 0) continue; if ((ent->desc.hour & desc.hour) == 0) continue; if ((ent->desc.min & desc.min ) == 0) continue; + lua_rawgeti(L, LUA_REGISTRYINDEX, ent->cb_ref); - lua_rawgeti(L, LUA_REGISTRYINDEX, cronent_list[i]); + lua_rawgeti(L, -2, i); // get ud again luaL_pcallx(L, 1, 0); } + + lua_pop(L, 1); // pop table } static void cron_handle_tmr() { @@ -250,6 +259,10 @@ int luaopen_cron( lua_State *L ) { //My guess: To be sure to give the other modules required by cron enough time to get to a ready state, restart cron_timer. os_timer_arm(&cron_timer, 1000, 0); luaL_rometatable(L, "cron.entry", LROT_TABLEREF(cronent)); + + cronent_table_ref = LUA_NOREF; + lcron_reset(L); + return 0; }