aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorcbdev <cb@cbcdn.com>2020-03-26 22:42:38 +0100
committercbdev <cb@cbcdn.com>2020-03-26 22:42:38 +0100
commit2a079f72483aa853d68430883b2281f436512c6b (patch)
tree7604e801f66703c522899610cb9e07f956479d53
parent18db3856a3a3e81f3e2050e3f137e6e15103f9a4 (diff)
downloadmidimonster-2a079f72483aa853d68430883b2281f436512c6b.tar.gz
midimonster-2a079f72483aa853d68430883b2281f436512c6b.tar.bz2
midimonster-2a079f72483aa853d68430883b2281f436512c6b.zip
Implement lua cleanup handlers
-rw-r--r--TODO3
-rw-r--r--backends/lua.c74
-rw-r--r--backends/lua.h1
-rw-r--r--backends/lua.md14
4 files changed, 60 insertions, 32 deletions
diff --git a/TODO b/TODO
index 900cc1b..ccad973 100644
--- a/TODO
+++ b/TODO
@@ -1,9 +1,6 @@
keepalive channels per backend?
Note source in channel value struct
-Optimize core channel search (store backend offset)
udp backends may ignore MTU
-mm_managed_fd.impl is not freed currently (and is heaped most of the time anyway) -> documentation
make event collectors threadsafe to stop marshalling data...
collect & check backend API version
windows strerror
-move all connection establishment to _start to be able to hot-stop/start all backends
diff --git a/backends/lua.c b/backends/lua.c
index 968193e..7424f65 100644
--- a/backends/lua.c
+++ b/backends/lua.c
@@ -155,8 +155,19 @@ static void lua_thread_resume(size_t current_thread){
lua_settable(thread[current_thread].thread, LUA_REGISTRYINDEX);
}
-static int lua_callback_thread(lua_State* interpreter){
+static instance* lua_fetch_instance(lua_State* interpreter){
instance* inst = NULL;
+
+ //get instance pointer from registry
+ lua_pushstring(interpreter, LUA_REGISTRY_KEY);
+ lua_gettable(interpreter, LUA_REGISTRYINDEX);
+ inst = (instance*) lua_touserdata(interpreter, -1);
+ lua_pop(interpreter, 1);
+ return inst;
+}
+
+static int lua_callback_thread(lua_State* interpreter){
+ instance* inst = lua_fetch_instance(interpreter);
size_t u = threads;
if(lua_gettop(interpreter) != 1){
LOGPF("Thread function called with %d arguments, expected function", lua_gettop(interpreter));
@@ -165,11 +176,6 @@ static int lua_callback_thread(lua_State* interpreter){
luaL_checktype(interpreter, 1, LUA_TFUNCTION);
- //get instance pointer from registry
- lua_pushstring(interpreter, LUA_REGISTRY_KEY);
- lua_gettable(interpreter, LUA_REGISTRYINDEX);
- inst = (instance*) lua_touserdata(interpreter, -1);
-
//make space for a new thread
thread = realloc(thread, (threads + 1) * sizeof(lua_thread));
if(!thread){
@@ -223,20 +229,14 @@ static int lua_callback_output(lua_State* interpreter){
size_t n = 0;
channel_value val;
const char* channel_name = NULL;
- instance* inst = NULL;
- lua_instance_data* data = NULL;
+ instance* inst = lua_fetch_instance(interpreter);
+ lua_instance_data* data = (lua_instance_data*) inst->impl;
if(lua_gettop(interpreter) != 2){
LOGPF("Output function called with %d arguments, expected 2 (string, number)", lua_gettop(interpreter));
return 0;
}
- //get instance pointer from registry
- lua_pushstring(interpreter, LUA_REGISTRY_KEY);
- lua_gettable(interpreter, LUA_REGISTRYINDEX);
- inst = (instance*) lua_touserdata(interpreter, -1);
- data = (lua_instance_data*) inst->impl;
-
//fetch function parameters
channel_name = lua_tostring(interpreter, 1);
val.normalised = clamp(luaL_checknumber(interpreter, 2), 1.0, 0.0);
@@ -264,6 +264,28 @@ static int lua_callback_output(lua_State* interpreter){
return 0;
}
+static int lua_callback_cleanup_handler(lua_State* interpreter){
+ instance* inst = lua_fetch_instance(interpreter);
+ lua_instance_data* data = (lua_instance_data*) inst->impl;
+ int current_handler = data->cleanup_handler;
+
+ if(lua_gettop(interpreter) != 1){
+ LOGPF("Cleanup handler function called with %d arguments, expected 1 (function)", lua_gettop(interpreter));
+ return 0;
+ }
+
+ luaL_checktype(interpreter, 1, LUA_TFUNCTION);
+
+ data->cleanup_handler = luaL_ref(interpreter, LUA_REGISTRYINDEX);
+ if(current_handler == LUA_NOREF){
+ lua_pushnil(interpreter);
+ return 1;
+ }
+ lua_rawgeti(interpreter, LUA_REGISTRYINDEX, current_handler);
+ luaL_unref(interpreter, LUA_REGISTRYINDEX, current_handler);
+ return 1;
+}
+
static int lua_callback_interval(lua_State* interpreter){
size_t n = 0;
uint64_t interval = 0;
@@ -274,10 +296,6 @@ static int lua_callback_interval(lua_State* interpreter){
return 0;
}
- //get instance pointer from registry
- lua_pushstring(interpreter, LUA_REGISTRY_KEY);
- lua_gettable(interpreter, LUA_REGISTRYINDEX);
-
//fetch and round the interval
interval = luaL_checkinteger(interpreter, 2);
if(interval % 10 < 5){
@@ -341,21 +359,15 @@ static int lua_callback_interval(lua_State* interpreter){
static int lua_callback_value(lua_State* interpreter, uint8_t input){
size_t n = 0;
- instance* inst = NULL;
- lua_instance_data* data = NULL;
const char* channel_name = NULL;
+ instance* inst = lua_fetch_instance(interpreter);
+ lua_instance_data* data = (lua_instance_data*) inst->impl;
if(lua_gettop(interpreter) != 1){
LOGPF("get_value function called with %d arguments, expected 1 (string)", lua_gettop(interpreter));
return 0;
}
- //get instance pointer from registry
- lua_pushstring(interpreter, LUA_REGISTRY_KEY);
- lua_gettable(interpreter, LUA_REGISTRYINDEX);
- inst = (instance*) lua_touserdata(interpreter, -1);
- data = (lua_instance_data*) inst->impl;
-
//fetch argument
channel_name = lua_tostring(interpreter, 1);
@@ -425,6 +437,7 @@ static int lua_instance(instance* inst){
//load the interpreter
data->interpreter = luaL_newstate();
+ data->cleanup_handler = LUA_NOREF;
if(!data->interpreter){
LOG("Failed to initialize interpreter");
free(data);
@@ -441,6 +454,7 @@ static int lua_instance(instance* inst){
lua_register(data->interpreter, "timestamp", lua_callback_timestamp);
lua_register(data->interpreter, "thread", lua_callback_thread);
lua_register(data->interpreter, "sleep", lua_callback_sleep);
+ lua_register(data->interpreter, "cleanup_handler", lua_callback_cleanup_handler);
//store instance pointer to the lua state
lua_pushstring(data->interpreter, LUA_REGISTRY_KEY);
@@ -578,6 +592,7 @@ static int lua_resolve_symbol(lua_State* interpreter, char* symbol){
|| !strcmp(symbol, "output_value")
|| !strcmp(symbol, "input_channel")
|| !strcmp(symbol, "timestamp")
+ || !strcmp(symbol, "cleanup_handler")
|| !strcmp(symbol, "interval")){
return LUA_NOREF;
}
@@ -639,6 +654,13 @@ static int lua_shutdown(size_t n, instance** inst){
for(u = 0; u < n; u++){
data = (lua_instance_data*) inst[u]->impl;
+
+ //call cleanup function if one is registered
+ if(data->cleanup_handler != LUA_NOREF){
+ lua_rawgeti(data->interpreter, LUA_REGISTRYINDEX, data->cleanup_handler);
+ lua_pcall(data->interpreter, 0, 0, 0);
+ }
+
//stop the interpreter
lua_close(data->interpreter);
//cleanup channel data
diff --git a/backends/lua.h b/backends/lua.h
index 4583dfe..5587bf9 100644
--- a/backends/lua.h
+++ b/backends/lua.h
@@ -35,6 +35,7 @@ typedef struct /*_lua_instance_data*/ {
lua_channel_data* channel;
lua_State* interpreter;
+ int cleanup_handler;
char* default_handler;
} lua_instance_data;
diff --git a/backends/lua.md b/backends/lua.md
index 05509b6..30d7580 100644
--- a/backends/lua.md
+++ b/backends/lua.md
@@ -13,14 +13,15 @@ The following functions are provided within the Lua interpreter for interaction
| Function | Usage example | Description |
|-------------------------------|-------------------------------|---------------------------------------|
-| `output(string, number)` | `output("foo", 0.75)` | Output a value event to a channel |
+| `output(string, number)` | `output("foo", 0.75)` | Output a value event to a channel on this instance |
| `interval(function, number)` | `interval(update, 100)` | Register a function to be called periodically. Intervals are milliseconds (rounded to the nearest 10 ms). Calling `interval` on a Lua function multiple times updates the interval. Specifying `0` as interval stops periodic calls to the function |
-| `input_value(string)` | `input_value("foo")` | Get the last input value on a channel |
-| `output_value(string)` | `output_value("bar")` | Get the last output value on a channel |
+| `input_value(string)` | `input_value("foo")` | Get the last input value on a channel on this instance |
+| `output_value(string)` | `output_value("bar")` | Get the last output value on a channel on this instance |
| `input_channel()` | `print(input_channel())` | Returns the name of the input channel whose handler function is currently running or `nil` if in an `interval`'ed function (or the initial parse step) |
| `timestamp()` | `print(timestamp())` | Returns the core timestamp for this iteration with millisecond resolution. This is not a performance timer, but intended for timeouting, etc |
| `thread(function)` | `thread(run_show)` | Run a function as a Lua thread (see below) |
| `sleep(number)` | `sleep(100)` | Suspend current thread for time specified in milliseconds |
+| `cleanup_handler(function)` | | Register a function to be called when the instance is destroyed (on MIDIMonster shutdown). One cleanup handler can be registered per instance. Calling this function when the instance already has a cleanup handler registered replaces the handler, returning the old one. |
Example script:
```lua
@@ -43,8 +44,12 @@ function run_show()
end
end
+function save_values()
+end
+
interval(toggle, 1000)
thread(run_show)
+cleanup_handler(save_values)
```
Input values range between 0.0 and 1.0, output values are clamped to the same range.
@@ -86,6 +91,9 @@ be called.
Output values will not trigger corresponding input event handlers unless the channel is mapped
back in the MIDIMonster configuration. This is intentional.
+Output events generated from cleanup handlers called during shutdown will not be routed, as the core
+routing facility has already shut down at this point. There are no plans to change this behaviour.
+
To build (and run) the `lua` backend on Windows, a compiled version of the Lua 5.3 library is required.
For various reasons (legal, separations of concern, not wanting to ship binary data in the repository),
the MIDIMonster project can not provide this file within this repository.