X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fpbd%2Fevent_loop.cc;h=3f5eeb0a755d3c9325fa2aa4a5162d0bf1ea8d04;hb=9ab92a67c856e371b292d701f2068207efe79af3;hp=11177610030e9a694d6711961715bfac1e10f1ca;hpb=d3ad5c16d13847dadb7821e6c3f225261d4b04ce;p=ardour.git diff --git a/libs/pbd/event_loop.cc b/libs/pbd/event_loop.cc index 1117761003..3f5eeb0a75 100644 --- a/libs/pbd/event_loop.cc +++ b/libs/pbd/event_loop.cc @@ -17,6 +17,10 @@ */ +#include + +#include + #include "pbd/compose.h" #include "pbd/debug.h" #include "pbd/event_loop.h" @@ -171,24 +175,59 @@ EventLoop::pre_register (const string& emitting_thread_name, uint32_t num_reques AbstractUI constructor. Note that if */ - /* make a key composed of the emitter and receiver thread names */ - - string key = emitting_thread_name; - key += '/'; - key += mapping.target_thread_name; + const string key = string_compose ("%1/%2", emitting_thread_name, mapping.target_thread_name); - /* if the emitting thread was killed and recreated (with the - * same name), this will replace the entry in - * thread_buffer_requests. The old entry will be lazily deleted - * when the target thread finds the request buffer and realizes - * that it is dead. + /* management of the thread_request_buffers map works as + * follows: * - * If the request buffer is replaced before the target thread - * ever finds the dead version, we will leak the old request - * buffer. + * when the factory method was called above, the pointer to the + * created buffer is set as a thread-local-storage (TLS) value + * for this (the emitting) thread. + * + * The TLS value is set up with a destructor that marks the + * request buffer as "dead" when the emitting thread exits. + * + * An entry will remain in the map after the thread exits. + * + * The receiving thread may (if it receives requests from other + * threads) notice the dead buffer. If it does, it will delete + * the request buffer, and call + * ::remove_request_buffer_from_map() to get rid of it from the map. + * + * This does mean that the lifetime of the request buffer is + * indeterminate: if the receiving thread were to receive no + * further requests, the request buffer will live on + * forever. But this is OK, because if there are no requests + * arriving, the receiving thread is not attempting to use the + * request buffer(s) in any way. + * + * Note, however, that *if* an emitting thread is recreated + * with the same name (e.g. when a control surface is + * enabled/disabled/enabled), then the request buffer for the + * new thread will replace the map entry for the key, because + * of the matching thread names. This does mean that + * potentially the request buffer can leak in this case, but + * (a) these buffers are not really that large anyway (b) the + * scenario is not particularly common (c) the buffers would + * typically last across a session instance if not program + * lifetime anyway. */ thread_buffer_requests[key] = mapping; - DEBUG_TRACE (PBD::DEBUG::EventLoop, string_compose ("pre-registered request buffer for \"%1\" to send to \"%2\", buffer @ %3\n", emitting_thread_name, trs->name, mapping.request_buffer)); + DEBUG_TRACE (PBD::DEBUG::EventLoop, string_compose ("pre-registered request buffer for \"%1\" to send to \"%2\", buffer @ %3 (key was %4)\n", + emitting_thread_name, trs->name, mapping.request_buffer, key)); + } +} + +void +EventLoop::remove_request_buffer_from_map (void* ptr) +{ + Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock); + + for (ThreadRequestBufferList::iterator x = thread_buffer_requests.begin(); x != thread_buffer_requests.end(); ++x) { + if (x->second.request_buffer == ptr) { + thread_buffer_requests.erase (x); + break; + } } }