diff options
Diffstat (limited to 'backend.c')
-rw-r--r-- | backend.c | 341 |
1 files changed, 191 insertions, 150 deletions
@@ -4,26 +4,44 @@ #else #define MM_API __attribute__((dllexport)) #endif +#define BACKEND_NAME "core/be" #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 size_t nchannels = 0; -static channel** channels = NULL; +static struct { + size_t n; + backend* backends; + instance*** instances; +} registry = { + .n = 0 +}; + +//the global channel store was converted from a naive list to a hashmap of lists for performance reasons +static struct { + //channelstore hash is set up for 256 buckets + size_t n[256]; + channel** entry[256]; +} channels = { + .n = { + 0 + } +}; + +static size_t channelstore_hash(instance* inst, uint64_t ident){ + uint64_t repr = ((uint64_t) inst) ^ ident; + return (repr ^ (repr >> 8) ^ (repr >> 16) ^ (repr >> 24) ^ (repr >> 32)) & 0xFF; +} int backends_handle(size_t nfds, managed_fd* fds){ size_t u, p, n; 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; @@ -31,10 +49,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; @@ -44,166 +65,149 @@ int backends_notify(size_t nev, channel** c, channel_value* v){ size_t u, p, n; int rv = 0; channel_value xval; - channel* xchnl; + channel* xchnl = NULL; - //TODO eliminate duplicates - for(u = 0; u < ninstances && !rv; u++){ - n = 0; + for(u = 0; u < nev && !rv; u = n){ + //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]; - for(p = 0; p < nev; p++){ - if(c[p]->instance == instances[u]){ - xval = v[n]; - xchnl = c[n]; - - 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); } return 0; } 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); - return channels[u]; + size_t u, bucket = channelstore_hash(inst, ident); + for(u = 0; u < channels.n[bucket]; u++){ + if(channels.entry[bucket][u]->instance == inst + && channels.entry[bucket][u]->ident == ident){ + DBGPF("Requested channel %" PRIu64 " on instance %s already exists, reusing (%" PRIsize_t " search steps)\n", ident, inst->name, u); + return channels.entry[bucket][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); - channel** new_chan = realloc(channels, (nchannels + 1) * sizeof(channel*)); - if(!new_chan){ + DBGPF("Creating previously unknown channel %" PRIu64 " on instance %s, bucket %" PRIsize_t "\n", ident, inst->name, bucket); + channels.entry[bucket] = realloc(channels.entry[bucket], (channels.n[bucket] + 1) * sizeof(channel*)); + if(!channels.entry[bucket]){ fprintf(stderr, "Failed to allocate memory\n"); - nchannels = 0; + channels.n[bucket] = 0; return NULL; } - channels = new_chan; - channels[nchannels] = calloc(1, sizeof(channel)); - if(!channels[nchannels]){ + channels.entry[bucket][channels.n[bucket]] = calloc(1, sizeof(channel)); + if(!channels.entry[bucket][channels.n[bucket]]){ fprintf(stderr, "Failed to allocate memory\n"); return NULL; } - channels[nchannels]->instance = inst; - channels[nchannels]->ident = ident; - return channels[nchannels++]; + channels.entry[bucket][channels.n[bucket]]->instance = inst; + channels.entry[bucket][channels.n[bucket]]->ident = ident; + return channels.entry[bucket][(channels.n[bucket]++)]; } -MM_API 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; - return instances[ninstances++]; -} - -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 < registry.n; u++){ + if(registry.backends + u == b){ + //count existing instances + for(n = 0; registry.instances[u] && registry.instances[u][n]; n++){ + } - for(u = 0; u < ninstances; u++){ - if(instances[u]->backend == b && instances[u]->ident == ident){ - return instances[u]; + //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]; } } + //this should never happen return NULL; } -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++; +MM_API instance* mm_instance_find(char* name, uint64_t ident){ + 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; + } + } } } - *ninst = n; - - if(!n){ - *inst = NULL; - return 0; - } + return NULL; +} - *inst = calloc(n, sizeof(instance*)); - if(!*inst){ - fprintf(stderr, "Failed to allocate memory\n"); +MM_API int mm_backend_instances(char* name, size_t* ninst, instance*** inst){ + size_t b = 0, i = 0; + if(!ninst || !inst){ return 1; } - n = 0; - for(u = 0; u < ninstances; u++){ - if(instances[u]->backend == b){ - (*inst)[n] = instances[u]; - n++; - } - } - return 0; -} + 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++){ + } -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; -} + *ninst = i; + if(!i){ + *inst = NULL; + return 0; + } -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); - if(channels[u]->impl){ - channels[u]->instance->backend->channel_free(channels[u]); + *inst = calloc(i, sizeof(instance*)); + if(!*inst){ + fprintf(stderr, "Failed to allocate memory\n"); + return 1; + } + + memcpy(*inst, registry.instances[b], i * sizeof(instance*)); + return 0; } - free(channels[u]); - channels[u] = NULL; } - free(channels); - nchannels = 0; + return 1; } 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; @@ -211,9 +215,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; @@ -223,14 +230,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, 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, registry.backends[u].name); msecs = res % 1000; } } @@ -245,14 +255,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; @@ -262,29 +274,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 @@ -295,24 +303,57 @@ int backends_start(){ return rv; } +static void channels_free(){ + size_t u, p; + for(u = 0; u < sizeof(channels.n) / sizeof(channels.n[0]); u++){ + DBGPF("Cleaning up channel registry bucket %" PRIsize_t " with %" PRIsize_t " channels", u, channels.n[u]); + for(p = 0; p < channels.n[u]; p++){ + DBGPF("Destroying channel %" PRIu64 " on instance %s\n", channels.entry[u][p]->ident, channels.entry[u][p]->instance->name); + //call the channel_free function if the backend supports it + if(channels.entry[u][p]->impl && channels.entry[u][p]->instance->backend->channel_free){ + channels.entry[u][p]->instance->backend->channel_free(channels.entry[u][p]); + } + free(channels.entry[u][p]); + } + free(channels.entry[u]); + channels.entry[u] = NULL; + channels.n[u] = 0; + } +} + int backends_stop(){ size_t u, n; instance** inst = NULL; - for(u = 0; u < nbackends; u++){ + //channels before instances to support proper shutdown procedures + channels_free(); + + //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; + free(registry.backends); + free(registry.instances); + registry.n = 0; return 0; } |