From 3b134cc43965c1c196734be7a162da7cddeeafc8 Mon Sep 17 00:00:00 2001 From: cbdev Date: Thu, 1 Jul 2021 22:34:24 +0200 Subject: Factor out explicit frontend API --- midimonster.c | 426 ++-------------------------------------------------------- 1 file changed, 13 insertions(+), 413 deletions(-) (limited to 'midimonster.c') diff --git a/midimonster.c b/midimonster.c index b73eeff..5e415e6 100644 --- a/midimonster.c +++ b/midimonster.c @@ -1,50 +1,15 @@ #include #include -#include -#include -#include #ifndef _WIN32 - #include #define MM_API __attribute__((visibility("default"))) #else #define MM_API __attribute__((dllexport)) #endif -#define BACKEND_NAME "core" -#define MM_SWAP_LIMIT 20 + +#define BACKEND_NAME "cli" #include "midimonster.h" +#include "core/core.h" #include "core/config.h" -#include "core/backend.h" -#include "core/plugin.h" - -/* Core-internal structures */ -typedef struct /*_event_collection*/ { - size_t alloc; - size_t n; - channel** channel; - channel_value* value; -} event_collection; - -typedef struct /*_mm_channel_mapping*/ { - channel* from; - size_t destinations; - channel** to; -} channel_mapping; - -static struct { - //routing_hash is set up for 256 buckets - size_t entries[256]; - channel_mapping* map[256]; - - event_collection pool[2]; - event_collection* events; -} routing = { - .events = routing.pool -}; - -static size_t fds = 0; -static managed_fd* fd = NULL; -static volatile sig_atomic_t fd_set_dirty = 1; -static uint64_t global_timestamp = 0; volatile static sig_atomic_t shutdown_requested = 0; @@ -52,198 +17,6 @@ static void signal_handler(int signum){ shutdown_requested = 1; } -static size_t routing_hash(channel* key){ - uint64_t repr = (uint64_t) key; - //return 8bit hash for 256 buckets, not ideal but it works - return (repr ^ (repr >> 8) ^ (repr >> 16) ^ (repr >> 24) ^ (repr >> 32)) & 0xFF; -} - -MM_API uint64_t mm_timestamp(){ - return global_timestamp; -} - -static void update_timestamp(){ - #ifdef _WIN32 - global_timestamp = GetTickCount(); - #else - struct timespec current; - if(clock_gettime(CLOCK_MONOTONIC_COARSE, ¤t)){ - fprintf(stderr, "Failed to update global timestamp, time-based processing for some backends may be impaired: %s\n", strerror(errno)); - return; - } - - global_timestamp = current.tv_sec * 1000 + current.tv_nsec / 1000000; - #endif -} - -int mm_map_channel(channel* from, channel* to){ - size_t u, m, bucket = routing_hash(from); - - //find existing source mapping - for(u = 0; u < routing.entries[bucket]; u++){ - if(routing.map[bucket][u].from == from){ - break; - } - } - - //create new entry - if(u == routing.entries[bucket]){ - routing.map[bucket] = realloc(routing.map[bucket], (routing.entries[bucket] + 1) * sizeof(channel_mapping)); - if(!routing.map[bucket]){ - routing.entries[bucket] = 0; - fprintf(stderr, "Failed to allocate memory\n"); - return 1; - } - - memset(routing.map[bucket] + routing.entries[bucket], 0, sizeof(channel_mapping)); - routing.entries[bucket]++; - routing.map[bucket][u].from = from; - } - - //check whether the target is already mapped - for(m = 0; m < routing.map[bucket][u].destinations; m++){ - if(routing.map[bucket][u].to[m] == to){ - return 0; - } - } - - //add a mapping target - routing.map[bucket][u].to = realloc(routing.map[bucket][u].to, (routing.map[bucket][u].destinations + 1) * sizeof(channel*)); - if(!routing.map[bucket][u].to){ - fprintf(stderr, "Failed to allocate memory\n"); - routing.map[bucket][u].destinations = 0; - return 1; - } - - routing.map[bucket][u].to[routing.map[bucket][u].destinations] = to; - routing.map[bucket][u].destinations++; - return 0; -} - -static void routing_cleanup(){ - size_t u, n; - - for(u = 0; u < sizeof(routing.map) / sizeof(routing.map[0]); u++){ - for(n = 0; n < routing.entries[u]; n++){ - free(routing.map[u][n].to); - } - free(routing.map[u]); - routing.map[u] = NULL; - routing.entries[u] = 0; - } - - for(u = 0; u < sizeof(routing.pool) / sizeof(routing.pool[0]); u++){ - free(routing.pool[u].channel); - free(routing.pool[u].value); - routing.pool[u].alloc = 0; - } -} - -MM_API int mm_manage_fd(int new_fd, char* back, int manage, void* impl){ - backend* b = backend_match(back); - size_t u; - - if(!b){ - fprintf(stderr, "Unknown backend %s registered for managed fd\n", back); - return 1; - } - - //find exact match - for(u = 0; u < fds; u++){ - if(fd[u].fd == new_fd && fd[u].backend == b){ - fd[u].impl = impl; - if(!manage){ - fd[u].fd = -1; - fd[u].backend = NULL; - fd[u].impl = NULL; - fd_set_dirty = 1; - } - return 0; - } - } - - if(!manage){ - return 0; - } - - //find free slot - for(u = 0; u < fds; u++){ - if(fd[u].fd < 0){ - break; - } - } - //if necessary expand - if(u == fds){ - fd = realloc(fd, (fds + 1) * sizeof(managed_fd)); - if(!fd){ - fprintf(stderr, "Failed to allocate memory\n"); - return 1; - } - fds++; - } - - //store new fd - fd[u].fd = new_fd; - fd[u].backend = b; - fd[u].impl = impl; - fd_set_dirty = 1; - return 0; -} - -static void fds_free(){ - size_t u; - for(u = 0; u < fds; u++){ - if(fd[u].fd >= 0){ - close(fd[u].fd); - fd[u].fd = -1; - } - } - free(fd); - fds = 0; - fd = NULL; -} - -MM_API int mm_channel_event(channel* c, channel_value v){ - size_t u, p, bucket = routing_hash(c); - - //find mapped channels - for(u = 0; u < routing.entries[bucket]; u++){ - if(routing.map[bucket][u].from == c){ - break; - } - } - - if(u == routing.entries[bucket]){ - //target-only channel - return 0; - } - - //resize event structures to fit additional events - if(routing.events->n + routing.map[bucket][u].destinations >= routing.events->alloc){ - routing.events->channel = realloc(routing.events->channel, (routing.events->alloc + routing.map[bucket][u].destinations) * sizeof(channel*)); - routing.events->value = realloc(routing.events->value, (routing.events->alloc + routing.map[bucket][u].destinations) * sizeof(channel_value)); - - if(!routing.events->channel || !routing.events->value){ - fprintf(stderr, "Failed to allocate memory\n"); - routing.events->alloc = 0; - routing.events->n = 0; - return 1; - } - - routing.events->alloc += routing.map[bucket][u].destinations; - } - - //enqueue channel events - //FIXME this might lead to one channel being mentioned multiple times in an apply call - memcpy(routing.events->channel + routing.events->n, routing.map[bucket][u].to, routing.map[bucket][u].destinations * sizeof(channel*)); - for(p = 0; p < routing.map[bucket][u].destinations; p++){ - routing.events->value[routing.events->n + p] = v; - } - - routing.events->n += routing.map[bucket][u].destinations; - return 0; -} - static void version(){ printf("MIDIMonster %s\n", MIDIMONSTER_VERSION); } @@ -255,36 +28,9 @@ static int usage(char* fn){ return EXIT_FAILURE; } -static fd_set fds_collect(int* max_fd){ - size_t u = 0; - fd_set rv_fds; - - if(max_fd){ - *max_fd = -1; - } - - DBGPF("Building selector set from %" PRIsize_t " FDs registered to core", fds); - FD_ZERO(&rv_fds); - for(u = 0; u < fds; u++){ - if(fd[u].fd >= 0){ - FD_SET(fd[u].fd, &rv_fds); - if(max_fd){ - *max_fd = max(*max_fd, fd[u].fd); - } - } - } - - return rv_fds; -} static int platform_initialize(){ #ifdef _WIN32 - WSADATA wsa; - WORD version = MAKEWORD(2, 2); - if(WSAStartup(version, &wsa)){ - return 1; - } - unsigned error_mode = SetErrorMode(0); SetErrorMode(error_mode | SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); #endif @@ -341,131 +87,9 @@ static int args_parse(int argc, char** argv, char** cfg_file){ return 0; } -static int core_process(size_t nfds, managed_fd* signaled_fds){ - event_collection* secondary = NULL; - size_t u, swaps = 0; - - //run backend processing, collect events - DBGPF("%" PRIsize_t " backend FDs signaled", nfds); - if(backends_handle(nfds, signaled_fds)){ - return 1; - } - - //limit number of collector swaps per iteration to prevent complete deadlock - while(routing.events->n && swaps < MM_SWAP_LIMIT){ - //swap primary and secondary event collectors - DBGPF("Swapping event collectors, %" PRIsize_t " events in primary", routing.events->n); - for(u = 0; u < sizeof(routing.pool) / sizeof(routing.pool[0]); u++){ - if(routing.events != routing.pool + u){ - secondary = routing.events; - routing.events = routing.pool + u; - break; - } - } - - //push collected events to target backends - if(secondary->n && backends_notify(secondary->n, secondary->channel, secondary->value)){ - fprintf(stderr, "Backends failed to handle output\n"); - return 1; - } - - //reset the event count - secondary->n = 0; - } - - if(swaps == MM_SWAP_LIMIT){ - LOG("Iteration swap limit hit, a backend may be configured to route events in an infinite loop"); - } - - return 0; -} - -static int core_loop(){ - fd_set all_fds, read_fds; - managed_fd* signaled_fds = NULL; - struct timeval tv; - int error, maxfd = -1; - size_t n, u; - #ifdef _WIN32 - char* error_message = NULL; - #else - struct timespec ts; - #endif - - FD_ZERO(&all_fds); - - //process events - while(!shutdown_requested){ - //rebuild fd set if necessary - if(fd_set_dirty || !signaled_fds){ - all_fds = fds_collect(&maxfd); - signaled_fds = realloc(signaled_fds, fds * sizeof(managed_fd)); - if(!signaled_fds){ - fprintf(stderr, "Failed to allocate memory\n"); - return 1; - } - fd_set_dirty = 0; - } - - //wait for & translate events - read_fds = all_fds; - tv = backend_timeout(); - - //check whether there are any fds active, windows does not like select() without descriptors - if(maxfd >= 0){ - error = select(maxfd + 1, &read_fds, NULL, NULL, &tv); - if(error < 0){ - #ifndef _WIN32 - fprintf(stderr, "select failed: %s\n", strerror(errno)); - #else - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, WSAGetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &error_message, 0, NULL); - fprintf(stderr, "select failed: %s\n", error_message); - LocalFree(error_message); - error_message = NULL; - #endif - free(signaled_fds); - return 1; - } - } - else{ - DBGPF("No descriptors, sleeping for %zu msec", tv.tv_sec * 1000 + tv.tv_usec / 1000); - #ifdef _WIN32 - Sleep(tv.tv_sec * 1000 + tv.tv_usec / 1000); - #else - ts.tv_sec = tv.tv_sec; - ts.tv_nsec = tv.tv_usec * 1000; - nanosleep(&ts, NULL); - #endif - } - - //update this iteration's timestamp - update_timestamp(); - - //find all signaled fds - n = 0; - for(u = 0; u < fds; u++){ - if(fd[u].fd >= 0 && FD_ISSET(fd[u].fd, &read_fds)){ - signaled_fds[n] = fd[u]; - n++; - } - } - - //fetch and process events - if(core_process(n, signaled_fds)){ - free(signaled_fds); - return 1; - } - } - - free(signaled_fds); - return 0; -} - int main(int argc, char** argv){ int rv = EXIT_FAILURE; char* cfg_file = DEFAULT_CFG; - size_t u, n = 0, max = 0; //parse commandline arguments if(args_parse(argc, argv, &cfg_file)){ @@ -479,57 +103,33 @@ int main(int argc, char** argv){ } //initialize backends - if(plugins_load(PLUGINS)){ - fprintf(stderr, "Failed to initialize a backend\n"); + if(core_initialize()){ goto bail; } //read config if(config_read(cfg_file)){ fprintf(stderr, "Failed to parse master configuration file %s\n", cfg_file); - backends_stop(); - routing_cleanup(); - fds_free(); - plugins_close(); - config_free(); + core_shutdown(); return (usage(argv[0]) | platform_shutdown()); } - //load an initial timestamp - update_timestamp(); - - //start backends - if(backends_start()){ + //start core + if(core_start()){ goto bail; } signal(SIGINT, signal_handler); - //count and report mappings - for(u = 0; u < sizeof(routing.map) / sizeof(routing.map[0]); u++){ - n += routing.entries[u]; - max = max(max, routing.entries[u]); - } - LOGPF("Routing %" PRIsize_t " sources, largest bucket has %" PRIsize_t " entries", - n, max); - - if(!fds){ - fprintf(stderr, "No descriptors registered for multiplexing\n"); - } - //run the core loop - if(!core_loop()){ - rv = EXIT_SUCCESS; + while(!shutdown_requested){ + if(core_iteration()){ + goto bail; + } } + rv = EXIT_SUCCESS; bail: - //free all data - backends_stop(); - routing_cleanup(); - fds_free(); - plugins_close(); - config_free(); - platform_shutdown(); - + core_shutdown(); return rv; } -- cgit v1.2.3