From d757a9c63371fcf5f9aa832d58ed4b8bdf634909 Mon Sep 17 00:00:00 2001 From: cbdev Date: Tue, 10 Mar 2020 20:42:55 +0100 Subject: Restructure core loop, add check for zero-descriptor case --- TODO | 1 + 1 file changed, 1 insertion(+) (limited to 'TODO') diff --git a/TODO b/TODO index 900cc1b..1ea91f4 100644 --- a/TODO +++ b/TODO @@ -7,3 +7,4 @@ 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 +only call _handle and _interval on backends with instances -- cgit v1.2.3 From 05cdf563fcc4c7835ec422fa5d7ee86b68a9f1df Mon Sep 17 00:00:00 2001 From: cbdev Date: Sat, 14 Mar 2020 21:45:09 +0100 Subject: Restructure backend and instance registry --- TODO | 1 - backend.c | 268 +++++++++++++++++++++++++++++------------------------- backend.h | 4 +- backends/artnet.c | 5 - backends/sacn.c | 5 - config.c | 2 +- 6 files changed, 147 insertions(+), 138 deletions(-) (limited to 'TODO') diff --git a/TODO b/TODO index 1ea91f4..900cc1b 100644 --- a/TODO +++ b/TODO @@ -7,4 +7,3 @@ 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 -only call _handle and _interval on backends with instances diff --git a/backend.c b/backend.c index ef8f66a..65cbd7d 100644 --- a/backend.c +++ b/backend.c @@ -8,10 +8,15 @@ #include "midimonster.h" #include "backend.h" -static size_t nbackends = 0; -static backend* backends = NULL; -static size_t ninstances = 0; -static instance** instances = NULL; +static struct { + size_t n; + backend* backends; + instance*** instances; +} registry = { + .n = 0 +}; + +//TODO move channel store into registry static size_t nchannels = 0; static channel** channels = NULL; @@ -20,11 +25,11 @@ int backends_handle(size_t nfds, managed_fd* fds){ int rv = 0; managed_fd xchg; - for(u = 0; u < nbackends && !rv; u++){ + for(u = 0; u < registry.n && !rv; u++){ n = 0; for(p = 0; p < nfds; p++){ - if(fds[p].backend == backends + u){ + if(fds[p].backend == registry.backends + u){ xchg = fds[n]; fds[n] = fds[p]; fds[p] = xchg; @@ -32,10 +37,13 @@ int backends_handle(size_t nfds, managed_fd* fds){ } } - DBGPF("Notifying backend %s of %lu waiting FDs\n", backends[u].name, n); - rv |= backends[u].process(n, fds); - if(rv){ - fprintf(stderr, "Backend %s failed to handle input\n", backends[u].name); + //handle if there is data ready or the backend has active instances for polling + if(n || registry.instances[u]){ + DBGPF("Notifying backend %s of %" PRIsize_t " waiting FDs\n", registry.backends[u].name, n); + rv |= registry.backends[u].process(n, fds); + if(rv){ + fprintf(stderr, "Backend %s failed to handle input\n", registry.backends[u].name); + } } } return rv; @@ -45,28 +53,29 @@ int backends_notify(size_t nev, channel** c, channel_value* v){ size_t u, p, n; int rv = 0; channel_value xval; - channel* xchnl; - - //TODO eliminate duplicates - for(u = 0; u < ninstances && !rv; u++){ - n = 0; + channel* xchnl = NULL; - for(p = 0; p < nev; p++){ - if(c[p]->instance == instances[u]){ - xval = v[n]; - xchnl = c[n]; + for(u = 0; u < nev && !rv; u++){ + //sort for this instance + n = u + 1; + for(p = u + 1; p < nev; p++){ + if(c[p]->instance == c[u]->instance){ + xval = v[p]; + xchnl = c[p]; - v[n] = v[p]; - c[n] = c[p]; + v[p] = v[n]; + c[p] = c[n]; - v[p] = xval; - c[p] = xchnl; + v[n] = xval; + c[n] = xchnl; n++; } } - DBGPF("Calling handler for instance %s with %lu events\n", instances[u]->name, n); - rv |= instances[u]->backend->handle(instances[u], n, c, v); + //TODO eliminate duplicates + DBGPF("Calling handler for instance %s with %" PRIsize_t " events\n", c[u]->instance->name, n - u); + rv |= c[u]->instance->backend->handle(c[u]->instance, n - u, c + u, v + u); + u = n; } return 0; @@ -76,17 +85,17 @@ MM_API channel* mm_channel(instance* inst, uint64_t ident, uint8_t create){ size_t u; for(u = 0; u < nchannels; u++){ if(channels[u]->instance == inst && channels[u]->ident == ident){ - DBGPF("Requested channel %lu on instance %s already exists, reusing\n", ident, inst->name); + DBGPF("Requested channel %" PRIu64 " on instance %s already exists, reusing\n", ident, inst->name); return channels[u]; } } if(!create){ - DBGPF("Requested unknown channel %lu on instance %s\n", ident, inst->name); + DBGPF("Requested unknown channel %" PRIu64 " on instance %s\n", ident, inst->name); return NULL; } - DBGPF("Creating previously unknown channel %lu on instance %s\n", ident, inst->name); + DBGPF("Creating previously unknown channel %" PRIu64 " on instance %s\n", ident, inst->name); channel** new_chan = realloc(channels, (nchannels + 1) * sizeof(channel*)); if(!new_chan){ fprintf(stderr, "Failed to allocate memory\n"); @@ -106,34 +115,46 @@ MM_API channel* mm_channel(instance* inst, uint64_t ident, uint8_t create){ return channels[nchannels++]; } -instance* mm_instance(){ - instance** new_inst = realloc(instances, (ninstances + 1) * sizeof(instance*)); - if(!new_inst){ - //TODO free - fprintf(stderr, "Failed to allocate memory\n"); - ninstances = 0; - return NULL; - } - instances = new_inst; - instances[ninstances] = calloc(1, sizeof(instance)); - if(!instances[ninstances]){ - fprintf(stderr, "Failed to allocate memory\n"); - return NULL; +instance* mm_instance(backend* b){ + size_t u = 0, n = 0; + + for(u = 0; u < registry.n; u++){ + if(registry.backends + u == b){ + //count existing instances + for(n = 0; registry.instances[u] && registry.instances[u][n]; n++){ + } + + //extend + registry.instances[u] = realloc(registry.instances[u], (n + 2) * sizeof(instance*)); + if(!registry.instances[u]){ + fprintf(stderr, "Failed to allocate memory\n"); + return NULL; + } + //sentinel + registry.instances[u][n + 1] = NULL; + registry.instances[u][n] = calloc(1, sizeof(instance)); + if(!registry.instances[u][n]){ + fprintf(stderr, "Failed to allocate memory\n"); + } + registry.instances[u][n]->backend = b; + return registry.instances[u][n]; + } } - return instances[ninstances++]; + //this should never happen + return NULL; } MM_API instance* mm_instance_find(char* name, uint64_t ident){ - size_t u; - backend* b = backend_match(name); - if(!b){ - return NULL; - } - - for(u = 0; u < ninstances; u++){ - if(instances[u]->backend == b && instances[u]->ident == ident){ - return instances[u]; + size_t b = 0; + instance** iter = NULL; + for(b = 0; b < registry.n; b++){ + if(!strcmp(registry.backends[b].name, name)){ + for(iter = registry.instances[b]; iter && *iter; iter++){ + if((*iter)->ident == ident){ + return *iter; + } + } } } @@ -141,55 +162,40 @@ MM_API instance* mm_instance_find(char* name, uint64_t ident){ } MM_API int mm_backend_instances(char* name, size_t* ninst, instance*** inst){ - backend* b = backend_match(name); - size_t n = 0, u; - //count number of affected instances - for(u = 0; u < ninstances; u++){ - if(instances[u]->backend == b){ - n++; - } + size_t b = 0, i = 0; + if(!ninst || !inst){ + return 1; } - *ninst = n; + for(b = 0; b < registry.n; b++){ + if(!strcmp(registry.backends[b].name, name)){ + //count instances + for(i = 0; registry.instances[b] && registry.instances[b][i]; i++){ + } - if(!n){ - *inst = NULL; - return 0; - } + *ninst = i; + if(!i){ + *inst = NULL; + return 0; + } - *inst = calloc(n, sizeof(instance*)); - if(!*inst){ - fprintf(stderr, "Failed to allocate memory\n"); - return 1; - } + *inst = calloc(i, sizeof(instance*)); + if(!*inst){ + fprintf(stderr, "Failed to allocate memory\n"); + return 1; + } - n = 0; - for(u = 0; u < ninstances; u++){ - if(instances[u]->backend == b){ - (*inst)[n] = instances[u]; - n++; + memcpy(*inst, registry.instances[b], i * sizeof(instance*)); + return 0; } } - return 0; -} - -void instances_free(){ - size_t u; - for(u = 0; u < ninstances; u++){ - free(instances[u]->name); - instances[u]->name = NULL; - instances[u]->backend = NULL; - free(instances[u]); - instances[u] = NULL; - } - free(instances); - ninstances = 0; + return 1; } void channels_free(){ size_t u; for(u = 0; u < nchannels; u++){ - DBGPF("Destroying channel %lu on instance %s\n", channels[u]->ident, channels[u]->instance->name); + DBGPF("Destroying channel %" PRIu64 " on instance %s\n", channels[u]->ident, channels[u]->instance->name); if(channels[u]->impl && channels[u]->instance->backend->channel_free){ channels[u]->instance->backend->channel_free(channels[u]); } @@ -200,11 +206,12 @@ void channels_free(){ nchannels = 0; } + backend* backend_match(char* name){ size_t u; - for(u = 0; u < nbackends; u++){ - if(!strcmp(backends[u].name, name)){ - return backends + u; + for(u = 0; u < registry.n; u++){ + if(!strcmp(registry.backends[u].name, name)){ + return registry.backends + u; } } return NULL; @@ -212,9 +219,12 @@ backend* backend_match(char* name){ instance* instance_match(char* name){ size_t u; - for(u = 0; u < ninstances; u++){ - if(!strcmp(instances[u]->name, name)){ - return instances[u]; + instance** iter = NULL; + for(u = 0; u < registry.n; u++){ + for(iter = registry.instances[u]; iter && *iter; iter++){ + if(!strcmp(name, (*iter)->name)){ + return *iter; + } } } return NULL; @@ -224,16 +234,17 @@ struct timeval backend_timeout(){ size_t u; uint32_t res, secs = 1, msecs = 0; - for(u = 0; u < nbackends; u++){ - if(backends[u].interval){ - res = backends[u].interval(); + for(u = 0; u < registry.n; u++){ + //only call interval if backend has instances + if(registry.instances[u] && registry.backends[u].interval){ + res = registry.backends[u].interval(); if((res / 1000) < secs){ - DBGPF("Updating interval to %" PRIu32 " msecs by request from %s", res, backends[u].name); + DBGPF("Updating interval to %" PRIu32 " msecs by request from %s", res, registry.backends[u].name); secs = res / 1000; msecs = res % 1000; } else if(res / 1000 == secs && (res % 1000) < msecs){ - DBGPF("Updating interval to %" PRIu32 " msecs by request from %s", res, backends[u].name); + DBGPF("Updating interval to %" PRIu32 " msecs by request from %s", res, registry.backends[u].name); msecs = res % 1000; } } @@ -248,14 +259,16 @@ struct timeval backend_timeout(){ MM_API int mm_backend_register(backend b){ if(!backend_match(b.name)){ - backends = realloc(backends, (nbackends + 1) * sizeof(backend)); - if(!backends){ + registry.backends = realloc(registry.backends, (registry.n + 1) * sizeof(backend)); + registry.instances = realloc(registry.instances, (registry.n + 1) * sizeof(instance**)); + if(!registry.backends || !registry.instances){ fprintf(stderr, "Failed to allocate memory\n"); - nbackends = 0; + registry.n = 0; return 1; } - backends[nbackends] = b; - nbackends++; + registry.backends[registry.n] = b; + registry.instances[registry.n] = NULL; + registry.n++; fprintf(stderr, "Registered backend %s\n", b.name); return 0; @@ -265,29 +278,25 @@ MM_API int mm_backend_register(backend b){ int backends_start(){ int rv = 0, current; - size_t n, u, p; instance** inst = NULL; + size_t n, u; - for(u = 0; u < nbackends; u++){ - //only start backends that have instances - for(p = 0; p < ninstances && instances[p]->backend != backends + u; p++){ - } - - //backend has no instances, skip the start call - if(p == ninstances){ + for(u = 0; u < registry.n; u++){ + //skip backends without instances + if(!registry.instances[u]){ continue; } //fetch list of instances - if(mm_backend_instances(backends[u].name, &n, &inst)){ - fprintf(stderr, "Failed to fetch instance list for initialization of backend %s\n", backends[u].name); + if(mm_backend_instances(registry.backends[u].name, &n, &inst)){ + fprintf(stderr, "Failed to fetch instance list for initialization of backend %s\n", registry.backends[u].name); return 1; } //start the backend - current = backends[u].start(n, inst); + current = registry.backends[u].start(n, inst); if(current){ - fprintf(stderr, "Failed to start backend %s\n", backends[u].name); + fprintf(stderr, "Failed to start backend %s\n", registry.backends[u].name); } //clean up @@ -302,20 +311,33 @@ int backends_stop(){ size_t u, n; instance** inst = NULL; - for(u = 0; u < nbackends; u++){ + //shut down the registry + for(u = 0; u < registry.n; u++){ //fetch list of instances - if(mm_backend_instances(backends[u].name, &n, &inst)){ - fprintf(stderr, "Failed to fetch instance list for shutdown of backend %s\n", backends[u].name); - n = 0; + if(mm_backend_instances(registry.backends[u].name, &n, &inst)){ + fprintf(stderr, "Failed to fetch instance list for shutdown of backend %s\n", registry.backends[u].name); inst = NULL; + n = 0; } - backends[u].shutdown(n, inst); + registry.backends[u].shutdown(n, inst); free(inst); inst = NULL; + + //free instances + for(inst = registry.instances[u]; inst && *inst; inst++){ + free((*inst)->name); + (*inst)->name = NULL; + (*inst)->backend = NULL; + free(*inst); + } + free(registry.instances[u]); + registry.instances[u] = NULL; } - free(backends); - nbackends = 0; + channels_free(); + free(registry.backends); + free(registry.instances); + registry.n = 0; return 0; } diff --git a/backend.h b/backend.h index de9b5dc..6a69508 100644 --- a/backend.h +++ b/backend.h @@ -8,9 +8,7 @@ instance* instance_match(char* name); struct timeval backend_timeout(); int backends_start(); int backends_stop(); -void instances_free(); -void channels_free(); -instance* mm_instance(); +instance* mm_instance(backend* b); /* Backend API */ MM_API channel* mm_channel(instance* inst, uint64_t ident, uint8_t create); diff --git a/backends/artnet.c b/backends/artnet.c index 34fc82d..caab6e0 100644 --- a/backends/artnet.c +++ b/backends/artnet.c @@ -321,11 +321,6 @@ static inline int artnet_process_frame(instance* inst, artnet_pkt* frame){ chan = data->data.channel + MAPPED_CHANNEL(data->data.map[p]); } - if(!chan){ - LOGPF("Active channel %" PRIsize_t " on %s not known to core", p, inst->name); - return 1; - } - if(IS_WIDE(data->data.map[p])){ data->data.map[MAPPED_CHANNEL(data->data.map[p])] &= ~MAP_MARK; wide_val = data->data.in[p] << ((data->data.map[p] & MAP_COARSE) ? 8 : 0); diff --git a/backends/sacn.c b/backends/sacn.c index 495bdf3..bd5c75a 100644 --- a/backends/sacn.c +++ b/backends/sacn.c @@ -436,11 +436,6 @@ static int sacn_process_frame(instance* inst, sacn_frame_root* frame, sacn_frame chan = inst_data->data.channel + MAPPED_CHANNEL(inst_data->data.map[u]); } - if(!chan){ - LOGPF("Active channel %" PRIsize_t " on %s not known to core", u, inst->name); - return 1; - } - //generate value if(IS_WIDE(inst_data->data.map[u])){ inst_data->data.map[MAPPED_CHANNEL(inst_data->data.map[u])] &= ~MAP_MARK; diff --git a/config.c b/config.c index 920e161..aef4f87 100644 --- a/config.c +++ b/config.c @@ -388,7 +388,7 @@ static int config_line(char* line){ return 1; } - current_instance = mm_instance(); + current_instance = mm_instance(current_backend); if(!current_instance){ return 1; } -- cgit v1.2.3 From 2a079f72483aa853d68430883b2281f436512c6b Mon Sep 17 00:00:00 2001 From: cbdev Date: Thu, 26 Mar 2020 22:42:38 +0100 Subject: Implement lua cleanup handlers --- TODO | 3 --- backends/lua.c | 74 +++++++++++++++++++++++++++++++++++++-------------------- backends/lua.h | 1 + backends/lua.md | 14 ++++++++--- 4 files changed, 60 insertions(+), 32 deletions(-) (limited to 'TODO') 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. -- cgit v1.2.3 From bac13064352234acea012de30ee36dd51748b97f Mon Sep 17 00:00:00 2001 From: cbdev Date: Sat, 11 Apr 2020 19:22:57 +0200 Subject: Implement strerror abstraction for Windows --- TODO | 1 - backends/libmmbackend.c | 21 ++++++++++++++++----- backends/libmmbackend.h | 1 + backends/rtpmidi.c | 19 ++++++++++--------- 4 files changed, 27 insertions(+), 15 deletions(-) (limited to 'TODO') diff --git a/TODO b/TODO index 900cc1b..a4a7e32 100644 --- a/TODO +++ b/TODO @@ -5,5 +5,4 @@ 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/libmmbackend.c b/backends/libmmbackend.c index ab96646..1624f56 100644 --- a/backends/libmmbackend.c +++ b/backends/libmmbackend.c @@ -17,6 +17,17 @@ int mmbackend_strdup(char** dest, char* src){ return 0; } +char* mmbackend_sockstrerror(int err_no){ + #ifdef _WIN32 + static char error[2048] = ""; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, WSAGetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), error, sizeof(error), NULL); + return error; + #else + return strerror(err_no); + #endif +} + void mmbackend_parse_hostspec(char* spec, char** host, char** port, char** options){ size_t u = 0; @@ -107,18 +118,18 @@ int mmbackend_socket(char* host, char* port, int socktype, uint8_t listener, uin //set required socket options yes = 1; if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(yes)) < 0){ - LOGPF("Failed to enable SO_REUSEADDR on socket: %s", strerror(errno)); + LOGPF("Failed to enable SO_REUSEADDR on socket: %s", mmbackend_sockstrerror(errno)); } if(mcast){ yes = 1; if(setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (void*)&yes, sizeof(yes)) < 0){ - LOGPF("Failed to enable SO_BROADCAST on socket: %s", strerror(errno)); + LOGPF("Failed to enable SO_BROADCAST on socket: %s", mmbackend_sockstrerror(errno)); } yes = 0; if(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (void*)&yes, sizeof(yes)) < 0){ - LOGPF("Failed to disable IP_MULTICAST_LOOP on socket: %s", strerror(errno)); + LOGPF("Failed to disable IP_MULTICAST_LOOP on socket: %s", mmbackend_sockstrerror(errno)); } } @@ -156,7 +167,7 @@ int mmbackend_socket(char* host, char* port, int socktype, uint8_t listener, uin #else int flags = fcntl(fd, F_GETFL, 0); if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0){ - LOGPF("Failed to set socket nonblocking: %s", strerror(errno)); + LOGPF("Failed to set socket nonblocking: %s", mmbackend_sockstrerror(errno)); close(fd); return -1; } @@ -174,7 +185,7 @@ int mmbackend_send(int fd, uint8_t* data, size_t length){ sent = send(fd, data + total, 1, 0); #endif if(sent < 0){ - LOGPF("Failed to send: %s", strerror(errno)); + LOGPF("Failed to send: %s", mmbackend_sockstrerror(errno)); return 1; } total += sent; diff --git a/backends/libmmbackend.h b/backends/libmmbackend.h index cb211a6..f487ee1 100644 --- a/backends/libmmbackend.h +++ b/backends/libmmbackend.h @@ -21,6 +21,7 @@ /** Convenience functions **/ int mmbackend_strdup(char** dest, char* src); +char* mmbackend_sockstrerror(int errno); /** Networking functions **/ diff --git a/backends/rtpmidi.c b/backends/rtpmidi.c index 95a50d2..581b16c 100644 --- a/backends/rtpmidi.c +++ b/backends/rtpmidi.c @@ -28,7 +28,6 @@ //TODO announce on mdns input //TODO connect to discovered peers //TODO refactor cfg.announces -//TODO windows address discovery //TODO for some reason, the announce packet generates an exception in the wireshark dissector static struct /*_rtpmidi_global*/ { @@ -325,7 +324,7 @@ static int rtpmidi_announce_addrs(){ struct ifaddrs* ifa = NULL, *iter = NULL; if(getifaddrs(&ifa)){ - LOGPF("Failed to get adapter address information: %s", strerror(errno)); + LOGPF("Failed to get adapter address information: %s", mmbackend_sockstrerror(errno)); return 1; } @@ -411,7 +410,7 @@ static int rtpmidi_bind_instance(instance* inst, rtpmidi_instance_data* data, ch } if(getsockname(data->fd, (struct sockaddr*) &sock_addr, &sock_len)){ - LOGPF("Failed to fetch data port information: %s", strerror(errno)); + LOGPF("Failed to fetch data port information: %s", mmbackend_sockstrerror(errno)); return 1; } @@ -1242,7 +1241,7 @@ static int rtpmidi_mdns_announce(rtpmidi_instance_data* data){ offset += sizeof(dns_rr_srv); //rfc2782 (srv) says to not compress `target`, rfc6762 (mdns) 18.14 says to - //we dont do it because i dont want to + //we don't do it because i don't want to snprintf((char*) frame + offset, sizeof(frame) - offset, "%s.local", cfg.mdns_name); if(dns_encode_name((char*) frame + offset, &name)){ LOGPF("Failed to encode name for %s", frame + offset); @@ -1486,11 +1485,15 @@ static int rtpmidi_handle_mdns(){ free(name.name); free(host.name); if(bytes <= 0){ + #ifdef _WIN32 + if(WSAGetLastError() == WSAEWOULDBLOCK){ + #else if(errno == EAGAIN){ + #endif return 0; } - LOGPF("Error reading from mDNS descriptor: %s", strerror(errno)); + LOGPF("Error reading from mDNS descriptor: %s", mmbackend_sockstrerror(errno)); return 1; } @@ -1564,14 +1567,12 @@ static int rtpmidi_start_mdns(){ //join ipv4 multicast group if(setsockopt(cfg.mdns_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (uint8_t*) &mcast_req, sizeof(mcast_req))){ - LOGPF("Failed to join IPv4 multicast group for mDNS: %s", strerror(errno)); - return 1; + LOGPF("Failed to join IPv4 multicast group for mDNS, discovery may be impaired: %s", mmbackend_sockstrerror(errno)); } //join ipv6 multicast group if(setsockopt(cfg.mdns_fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (uint8_t*) &mcast6_req, sizeof(mcast6_req))){ - LOGPF("Failed to join IPv6 multicast group for mDNS: %s", strerror(errno)); - return 1; + LOGPF("Failed to join IPv6 multicast group for mDNS, discovery may be impaired: %s", mmbackend_sockstrerror(errno)); } //register mdns fd to core -- cgit v1.2.3