Commit a88c35b8 authored by Karel Koci's avatar Karel Koci 🤘

Implement Lua bindings for cleanup

parent ef01234a
--[[
Copyright 2018, CZ.NIC z.s.p.o. (http://www.nic.cz/)
This file is part of the turris updater.
Updater is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Updater is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Updater. If not, see <http://www.gnu.org/licenses/>.
]]--
-- luacheck: globals cleanup_register cleanup_unregister cleanup_run cleanup_run_handle
--[[ This is array with registered Lua cleanup functions
It contains table with fields 'func' for registered Lua function and 'handle' for
handle returned from C function.
]]
local cleanup_functions = {}
-- We are not removing old fields from table (only setting them to nil) because we
-- would otherwise lost correct index-handle pairing. Because of that we can't use
-- maxn function. We are using this variable instead.
local cleanup_functions_top = 0
function cleanup_register(func)
local handle = cleanup_register_handle(cleanup_functions_top)
cleanup_functions_top = cleanup_functions_top + 1
table.insert(cleanup_functions, cleanup_functions_top, {['func'] = func, ['handle'] = handle})
end
-- This returns true if given function was on stack and removes it from it
local function cleanup_pop_handle(func)
-- First locate top most function
local i = cleanup_functions_top
while i > 0 and (cleanup_functions[i] or {})['func'] ~= func do
i = i - 1
end
if i <= 0 then
return true
end
local handle = cleanup_functions[i]['handle']
cleanup_unregister_handle(handle)
cleanup_functions[i] = nil
return handle == nil
end
function cleanup_unregister(func)
return not cleanup_pop_handle(func)
end
function cleanup_run(func)
if cleanup_pop_handle(func) then
return
end
func()
end
-- This function is called from C and shouldn't be called from Lua it self.
function cleanup_run_handle(index)
cleanup_functions[index]['func']()
cleanup_functions[index] = nil
end
......@@ -417,6 +417,40 @@ static int lua_events_wait(lua_State *L) {
return 0;
}
// Name of lua_cleanup_data meta table
#define CLEANUP_DATA_META "CLEANUP_DATA_META"
struct lua_cleanup_data {
lua_State *L;
int index; // Index in lua table of functions
};
static void lua_cleanup_func(void *vdata) {
struct lua_cleanup_data *data = (struct lua_cleanup_data*)vdata;
lua_State *L = data->L;
int handler = push_err_handler(L);
lua_getglobal(data->L, "cleanup_run_handle");
lua_pushinteger(data->L, data->index);
int result = lua_pcall(L, 1, 0, handler);
ASSERT_MSG(!result, "%s", interpreter_error_result(L));
}
static int lua_cleanup_register_handle(lua_State *L) {
struct lua_cleanup_data *data = lua_newuserdata(L, sizeof *data);
luaL_newmetatable(L, CLEANUP_DATA_META);
lua_setmetatable(L, -2);
data->L = L;
data->index = luaL_checkinteger(L, 1);
cleanup_register(lua_cleanup_func, data);
return 1;
}
static int lua_cleanup_unregister_handle(lua_State *L) {
struct lua_cleanup_data *data = (struct lua_cleanup_data*)luaL_checkudata(L, 1, CLEANUP_DATA_META);
ASSERT(cleanup_unregister_data(lua_cleanup_func, data)); // Lua should never request nonexistent handle from us
return 0;
}
static int lua_mkdtemp(lua_State *L) {
int param_count = lua_gettop(L);
if (param_count > 1)
......@@ -816,6 +850,8 @@ static const struct injected_func injected_funcs[] = {
{ lua_log, "log" },
{ lua_state_log_enabled, "state_log_enabled" },
{ lua_state_dump, "state_dump" },
{ lua_cleanup_register_handle, "cleanup_register_handle" },
{ lua_cleanup_unregister_handle, "cleanup_unregister_handle" },
{ lua_run_command, "run_command" },
{ lua_run_util, "run_util" },
{ lua_download, "download" },
......
......@@ -33,6 +33,44 @@ DIE(msg, msg, ...)::
state_dump(msg)::
Dump the given message into `/tmp/update-state/state`.
Cleanup
-------
It's common to have code that requires cleanup. But ensuring that cleanup is
really called is tedious task. But thanks to atexit function and common DIE
function we can ensure that cleanup functions will be called. We defined function
to register, unregister and call cleanup function.
cleanup_register(function)::
This registers given function as to be called when cleanup is requested.
cleanup_unregister(function)::
This removes previously registered function from list of functions to be called
for cleanup. This is done without calling given function.
cleanup_run(function)::
This runs previously registered function and removes it from list of functions
to be called for cleanup.
Note: This is for simplicity not implemented completely in C but lua functions
handlers is implemented in Lua it self. But we can't have that completely detached
as we have to ensure that order of cleanup holds even if Lua and C calls are mixed
together.
Following functions are defined for internal use of cleanup functionality only.
But they are not documented outside of the code usage so here they are:
cleanup_register_handle(index)::
This function registers new cleanup for Lua. It expect index identifying this
cleanup in global table of cleanup functions. It is used to register lua cleanup
function handle to C cleanups. Exact Lua function is stored in Lua table and
only index identifying given cleanup is passed to C.
cleanup_unregister_handle(index)::
This function unregisters existing cleanup for Lua. It expect index identifying
this cleanup in global table of cleanup functions. It reverse function or
`cleanup_register_handle`.
Asynchronous events
-------------------
......
......@@ -23,6 +23,7 @@ LUA_TESTS := \
transaction \
utils \
syscnf \
cleanup \
uri \
picosat
......@@ -47,7 +48,7 @@ clean-coverage:
rm -rf $(O)/coverage
# Ignore stacktraceplus and dumper, not our creation.
LUA_AUTOLOAD := $(filter-out 01_stacktraceplus 06_dumper,$(patsubst a_%.lua,%,$(notdir $(wildcard $(S)/src/lib/autoload/a_*.lua))))
LUA_AUTOLOAD := $(filter-out 01_stacktraceplus 07_dumper,$(patsubst a_%.lua,%,$(notdir $(wildcard $(S)/src/lib/autoload/a_*.lua))))
TEST_URIINTERNAL := $(abspath $(wildcard $(S)/tests/internal/*))
$(O)/.gen/tests/uriinternal.embedlist: $(S)/src/lib/gen_embed.sh $(S)/src/lib/embed_types.h $(TEST_URIINTERNAL)
......
--[[
Copyright 2018, CZ.NIC z.s.p.o. (http://www.nic.cz/)
This file is part of the turris updater.
Updater is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Updater is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Updater. If not, see <http://www.gnu.org/licenses/>.
]]--
require 'lunit'
module("cleanup", package.seeall, lunit.testcase)
local cleaned = false
local function cleanup_global()
cleaned = true
end
function test_cleanup()
cleanup_register(cleanup_global)
cleaned = false
cleanup_run(cleanup_global)
assert_true(cleaned)
cleaned = false
local function cleanup_local()
cleaned = true
end
cleanup_register(cleanup_local)
cleanup_run(cleanup_local)
assert_true(cleaned)
end
function test_cleanup_not_registered()
cleaned = false
cleanup_run(cleanup_global)
assert_false(cleaned)
end
function test_cleanup_unregister()
local function cleanup_local()
cleaned = true
end
cleaned = false
cleanup_register(cleanup_global)
cleanup_register(cleanup_local)
assert_true(cleanup_unregister(cleanup_global))
assert_false(cleanup_unregister(cleanup_global))
assert_true(cleanup_unregister(cleanup_local))
assert_false(cleanup_unregister(cleanup_local))
assert_false(cleaned);
end
......@@ -10,7 +10,7 @@ globals = {
-- lua51 doesn't contains?
"_M",
-- From interpreter.c
"log", "state_log_enabled", "state_dump", "run_command", "run_util", "download", "events_wait", "mkdtemp", "chdir", "getcwd", "mkdir", "move", "copy", "ls", "stat", "lstat", "sync", "setenv", "md5", "sha256", "md5_file", "sha256_file", "reexec", "uri_internal_get", "system_reboot", "get_updater_version",
"log", "state_log_enabled", "state_dump", "cleanup_register_handle", "cleanup_unregister_handle", "run_command", "run_util", "download", "events_wait", "mkdtemp", "chdir", "getcwd", "mkdir", "move", "copy", "ls", "stat", "lstat", "sync", "setenv", "md5", "sha256", "md5_file", "sha256_file", "reexec", "uri_internal_get", "system_reboot", "get_updater_version",
-- From logging
"ERROR", "WARN", "INFO", "DBG", "TRACE", "DIE", "log_event", "c_pcall_error_handler",
-- Picosat
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment