enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / libs / pbd / event_loop.cc
index 11177610030e9a694d6711961715bfac1e10f1ca..ea3f7a46afb5ae2b347967169c8ace80e69afc6a 100644 (file)
 
 */
 
+#include <cstring>
+
+#include <pthread.h>
+
 #include "pbd/compose.h"
 #include "pbd/debug.h"
 #include "pbd/event_loop.h"
 #include "pbd/error.h"
 #include "pbd/stacktrace.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace PBD;
 using namespace std;
@@ -143,7 +147,7 @@ EventLoop::pre_register (const string& emitting_thread_name, uint32_t num_reques
         */
 
        ThreadBufferMapping mapping;
-       Glib::Threads::RWLock::ReaderLock lm (thread_buffer_requests_lock);
+       Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock);
 
        for (RequestBufferSuppliers::iterator trs = request_buffer_suppliers.begin(); trs != request_buffer_suppliers.end(); ++trs) {
 
@@ -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 */
+               const string key = string_compose ("%1/%2", emitting_thread_name, mapping.target_thread_name);
 
-               string key = emitting_thread_name;
-               key += '/';
-               key +=  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;
+               }
        }
 }