From ca055614db57386f0e43d65bb10e501bdde699ef Mon Sep 17 00:00:00 2001 From: cbdev Date: Tue, 22 Jun 2021 03:29:08 +0200 Subject: Keep channel registry map in sync when updating identifiers (#76) --- backends/maweb.c | 2 +- core/backend.c | 47 +++++++++++++++++++++++++++++++++++++++++++++-- core/backend.h | 1 + midimonster.c | 4 ++-- midimonster.h | 25 ++++++++++++++++++++----- 5 files changed, 69 insertions(+), 10 deletions(-) diff --git a/backends/maweb.c b/backends/maweb.c index 39ef7a6..8b878b0 100644 --- a/backends/maweb.c +++ b/backends/maweb.c @@ -1111,7 +1111,7 @@ static int maweb_start(size_t n, instance** inst){ //re-set channel identifiers for(p = 0; p < data->channels; p++){ - data->channel[p].chan->ident = p; + mm_channel_update(data->channel[p].chan, p); } //try to connect to any available host diff --git a/core/backend.c b/core/backend.c index 16e095c..8a8588f 100644 --- a/core/backend.c +++ b/core/backend.c @@ -94,9 +94,9 @@ int backends_notify(size_t nev, channel** c, channel_value* v){ MM_API channel* mm_channel(instance* inst, uint64_t ident, uint8_t create){ size_t u, bucket = channelstore_hash(inst, ident); - DBGPF("\tSearching for inst %" PRIu64 " ident %" PRIu64, inst, ident); + DBGPF("\tSearching for inst %" PRIu64 " ident %" PRIu64, (uint64_t) inst, ident); for(u = 0; u < channels.n[bucket]; u++){ - DBGPF("\tBucket %" PRIsize_t " entry %" PRIsize_t " inst %" PRIu64 " ident %" PRIu64, bucket, u, channels.entry[bucket][u]->instance, channels.entry[bucket][u]->ident); + DBGPF("\tBucket %" PRIsize_t " entry %" PRIsize_t " inst %" PRIu64 " ident %" PRIu64, bucket, u, (uint64_t) channels.entry[bucket][u]->instance, channels.entry[bucket][u]->ident); if(channels.entry[bucket][u]->instance == inst && channels.entry[bucket][u]->ident == ident){ DBGPF("Requested channel %" PRIu64 " on instance %s already exists, reusing (bucket %" PRIsize_t ", %" PRIsize_t " search steps)\n", ident, inst->name, bucket, u); @@ -128,6 +128,49 @@ MM_API channel* mm_channel(instance* inst, uint64_t ident, uint8_t create){ return channels.entry[bucket][(channels.n[bucket]++)]; } +MM_API void mm_channel_update(channel* chan, uint64_t ident){ + size_t bucket = channelstore_hash(chan->instance, chan->ident), new_bucket = channelstore_hash(chan->instance, ident); + size_t u; + + DBGPF("Updating identifier for inst %" PRIu64 " ident %" PRIu64 " (bucket %" PRIsize_t " to %" PRIsize_t ") to %" PRIu64, (uint64_t) chan->instance, chan->ident, bucket, new_bucket, ident); + + if(bucket == new_bucket){ + chan->ident = ident; + return; + } + + for(u = 0; u < channels.n[bucket]; u++){ + if(channels.entry[bucket][u]->instance == chan->instance + && channels.entry[bucket][u]->ident == chan->ident){ + break; + } + } + + if(u == channels.n[bucket]){ + DBGPF("Failed to find channel to update in bucket %" PRIsize_t, bucket); + return; + } + + DBGPF("Removing channel from slot %" PRIsize_t " of %" PRIsize_t " of bucket %" PRIsize_t, u, channels.n[bucket], bucket); + //remove channel from old bucket + for(; u < channels.n[bucket] - 1; u++){ + channels.entry[bucket][u] = channels.entry[bucket][u + 1]; + } + + //add to new bucket + channels.entry[new_bucket] = realloc(channels.entry[new_bucket], (channels.n[new_bucket] + 1) * sizeof(channel*)); + if(!channels.entry[new_bucket]){ + fprintf(stderr, "Failed to allocate memory\n"); + channels.n[new_bucket] = 0; + return; + } + + channels.entry[new_bucket][channels.n[new_bucket]] = chan; + chan->ident = ident; + channels.n[bucket]--; + channels.n[new_bucket]++; +} + instance* mm_instance(backend* b){ size_t u = 0, n = 0; diff --git a/core/backend.h b/core/backend.h index 6a69508..46c6c3a 100644 --- a/core/backend.h +++ b/core/backend.h @@ -12,6 +12,7 @@ instance* mm_instance(backend* b); /* Backend API */ MM_API channel* mm_channel(instance* inst, uint64_t ident, uint8_t create); +MM_API void mm_channel_update(channel* chan, uint64_t ident); MM_API instance* mm_instance_find(char* name, uint64_t ident); MM_API int mm_backend_instances(char* name, size_t* ninst, instance*** inst); MM_API int mm_backend_register(backend b); diff --git a/midimonster.c b/midimonster.c index 51fe7ad..5817ac7 100644 --- a/midimonster.c +++ b/midimonster.c @@ -346,7 +346,7 @@ static int core_process(size_t nfds, managed_fd* signaled_fds){ size_t u, swaps = 0; //run backend processing, collect events - DBGPF("%lu backend FDs signaled\n", nfds); + DBGPF("%lu backend FDs signaled", nfds); if(backends_handle(nfds, signaled_fds)){ return 1; } @@ -354,7 +354,7 @@ static int core_process(size_t nfds, managed_fd* signaled_fds){ //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, %lu events in primary\n", routing.events->n); + DBGPF("Swapping event collectors, %lu 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; diff --git a/midimonster.h b/midimonster.h index 9552b7e..89688c4 100644 --- a/midimonster.h +++ b/midimonster.h @@ -227,15 +227,21 @@ MM_API int mm_backend_register(backend b); MM_API instance* mm_instance_find(char* backend, uint64_t ident); /* - * Provides a pointer to a channel structure, pre-filled with the provided - * instance reference and identifier. + * This function is the main interface to the core-provided channel registry. + * This API is just a convenience function. Creating and managing a + * backend-internal channel store is possible (and encouraged for performance + * reasons). + * + * Channels are identified by the (instance, ident) tuple within the registry. + * + * This API provides a pointer to a channel structure, pre-filled with the + * provided instance reference and identifier. * The `create` parameter is a boolean flag indicating whether a channel * matching the `ident` parameter should be created in the global channel store * if none exists yet. If the instance already registered a channel matching * `ident`, a pointer to the existing channel is returned. - * This API is just a convenience function. Creating and managing a - * backend-internal channel store is possible (and encouraged for performance - * reasons). When returning pointers from a backend-local channel store, the + * + * When returning pointers from a backend-local channel store, the * returned pointers must stay valid over the lifetime of the instance and * provide valid `instance` members, as they are used for callbacks. * For each channel with a non-NULL `impl` field registered using @@ -244,6 +250,15 @@ MM_API instance* mm_instance_find(char* backend, uint64_t ident); */ MM_API channel* mm_channel(instance* i, uint64_t ident, uint8_t create); +/* + * When using the core-provided channel registry, the identification + * member of the structure must only be updated using this API. + * The tuple of (instance, ident) is used as key to the backing + * storage of the channel registry, thus the registry must be notified + * of changes. + */ +MM_API void mm_channel_update(channel* c, uint64_t ident); + /* * Register (manage = 1) or unregister (manage = 0) a file descriptor to be * selected on. The backend will be notified when the descriptor becomes ready -- cgit v1.2.3