From: Aki Tuomi Date: Fri, 5 Feb 2021 14:47:53 +0000 (+0200) Subject: lib-lua: Add new thread/close thread dlua wrappers X-Git-Tag: 2.3.15~176 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a84bfdaf6df8fd6a4cbbf0845c18b09e6ceada36;p=thirdparty%2Fdovecot%2Fcore.git lib-lua: Add new thread/close thread dlua wrappers Code rewritten to use lua registry, warn about unclosed threads, and moved to a separate file by Josef 'Jeff' Sipek --- diff --git a/src/lib-lua/Makefile.am b/src/lib-lua/Makefile.am index a76e9b8e90..48c1f9bb69 100644 --- a/src/lib-lua/Makefile.am +++ b/src/lib-lua/Makefile.am @@ -8,7 +8,8 @@ libdovecot_lua_la_SOURCES = \ dlua-script.c \ dlua-dovecot.c \ dlua-compat.c \ - dlua-table.c + dlua-table.c \ + dlua-thread.c # Note: the only things this lib should depend on are libdovecot and lua. libdovecot_lua_la_DEPENDENCIES = ../lib-dovecot/libdovecot.la libdovecot_lua_la_LIBADD = ../lib-dovecot/libdovecot.la $(LUA_LIBS) diff --git a/src/lib-lua/dlua-script-private.h b/src/lib-lua/dlua-script-private.h index 5e1809ac25..ef378caec4 100644 --- a/src/lib-lua/dlua-script-private.h +++ b/src/lib-lua/dlua-script-private.h @@ -39,7 +39,7 @@ struct dlua_script { struct dlua_script *prev,*next; pool_t pool; - lua_State *L; + lua_State *L; /* base lua context */ struct event *event; const char *filename; @@ -169,4 +169,14 @@ int dlua_table_get_by_thread(lua_State *L, int idx, int type); /* dumps current stack as i_debug lines */ void dlua_dump_stack(lua_State *L); +/* Create new thread and keep track of it. */ +lua_State *dlua_script_new_thread(struct dlua_script *script); + +/* Close thread. */ +void dlua_script_close_thread(struct dlua_script *script, lua_State **_L); + +/* initialize/free script's thread table */ +void dlua_init_thread_table(struct dlua_script *script); +void dlua_free_thread_table(struct dlua_script *script); + #endif diff --git a/src/lib-lua/dlua-script.c b/src/lib-lua/dlua-script.c index 8753c2f8fc..7935061125 100644 --- a/src/lib-lua/dlua-script.c +++ b/src/lib-lua/dlua-script.c @@ -190,6 +190,8 @@ static struct dlua_script *dlua_create_script(const char *name, script->event = event_create(event_parent); event_add_category(script->event, &event_category_lua); + dlua_init_thread_table(script); + return script; } @@ -304,7 +306,12 @@ static void dlua_script_destroy(struct dlua_script *script) { dlua_call_deinit_function(script); + /* close all threads */ + dlua_free_thread_table(script); + + /* close base lua */ lua_close(script->L); + /* remove from list */ DLLIST_REMOVE(&dlua_scripts, script); diff --git a/src/lib-lua/dlua-thread.c b/src/lib-lua/dlua-thread.c new file mode 100644 index 0000000000..84ac488a1a --- /dev/null +++ b/src/lib-lua/dlua-thread.c @@ -0,0 +1,125 @@ +/* Copyright (c) 2021 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "dlua-script-private.h" + +/* + * dlua support for threads + * + * The following code keeps a table in (global) registry. This table is + * indexed by the thread objects, each mapping to another table. That is: + * + * registry[thread] = {} -- threads table + * + * This acts as a reference to the thread object, preventing it from being + * garbage collected. + * + * The table is allocated during struct dlua_script's creation and is freed + * during the scripts destruction. Any lua threads created using + * dlua_script_new_thread() will automatically get added to this table. + */ + +/* the registry entry with a table with all the lua threads */ +#define LUA_THREAD_REGISTRY_KEY "DLUA_THREADS" + +void dlua_init_thread_table(struct dlua_script *script) +{ + lua_newtable(script->L); + lua_setfield(script->L, LUA_REGISTRYINDEX, LUA_THREAD_REGISTRY_KEY); + + /* + * Note that we are *not* adding the main lua state since it is not + * a thread. + */ +} + +static void warn_about_leaked_threads(struct dlua_script *script) +{ + lua_State *L = script->L; + + lua_getfield(L, LUA_REGISTRYINDEX, LUA_THREAD_REGISTRY_KEY); + + i_assert(lua_type(L, -1) == LUA_TTABLE); + + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + /* stack: table, thread, per-thread table */ + + /* check the key */ + if (lua_type(L, -2) != LUA_TTHREAD) { + e_error(script->event, "Unexpected %s key in thread table", + lua_typename(L, lua_type(L, -2))); + } else { + e_error(script->event, "Lua thread %p leaked", lua_tothread(L, -2)); + } + + /* check the value */ + if (lua_type(L, -1) != LUA_TTABLE) { + e_error(script->event, "Unexpected %s value in thread table", + lua_typename(L, lua_type(L, -1))); + } + + /* pop the value for lua_next() */ + lua_pop(L, 1); + } + + lua_pop(L, 1); +} + +void dlua_free_thread_table(struct dlua_script *script) +{ + /* all threads should have been closed by now */ + warn_about_leaked_threads(script); + + /* set the thread table to nil - letting GC clean everything up */ + lua_pushnil(script->L); + lua_setfield(script->L, LUA_REGISTRYINDEX, LUA_THREAD_REGISTRY_KEY); +} + +lua_State *dlua_script_new_thread(struct dlua_script *script) +{ + lua_State *thread; + + /* get the threads table */ + lua_getfield(script->L, LUA_REGISTRYINDEX, LUA_THREAD_REGISTRY_KEY); + + /* allocate a new thread */ + thread = lua_newthread(script->L); + i_assert(thread != NULL); + + /* allocate new per-thread table */ + lua_newtable(script->L); + + /* stack: threads-table, thread, per-thread-table (top) */ + + /* threads-table[thread] = per-thread-table */ + lua_settable(script->L, -3); + + return thread; +} + +void dlua_script_close_thread(struct dlua_script *script, lua_State **_L) +{ + if (*_L == NULL) + return; + + /* get the threads table */ + lua_getfield(*_L, LUA_REGISTRYINDEX, LUA_THREAD_REGISTRY_KEY); + + /* push the thread to destroy */ + i_assert(lua_pushthread(*_L) != 1); + + lua_pushnil(*_L); + + /* stack: threads-table, thread, nil (top) */ + + /* + * threads-table[thread] = nil + * + * This assignment removes the reference to the thread saving it + * from GC. + */ + lua_settable(*_L, -3); + + *_L = NULL; +}