mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-06 14:54:56 +01:00
libpbd: changes to pre-registration of signal emitting threads
There is no need to preallocate request buffers for these threads - the event loops that require them can allocate them when they discover and register the pre-registered threads. This also means that event loops do not need to register request buffer factories.
This commit is contained in:
parent
ba66381ab0
commit
b0586763ba
7 changed files with 55 additions and 145 deletions
|
|
@ -37,7 +37,7 @@ static void do_not_delete_the_loop_pointer (void*) { }
|
|||
|
||||
Glib::Threads::Private<EventLoop> EventLoop::thread_event_loop (do_not_delete_the_loop_pointer);
|
||||
|
||||
Glib::Threads::RWLock EventLoop::thread_buffer_requests_lock;
|
||||
Glib::Threads::Mutex EventLoop::thread_buffer_requests_lock;
|
||||
EventLoop::ThreadRequestBufferList EventLoop::thread_buffer_requests;
|
||||
EventLoop::RequestBufferSuppliers EventLoop::request_buffer_suppliers;
|
||||
|
||||
|
|
@ -113,16 +113,13 @@ vector<EventLoop::ThreadBufferMapping>
|
|||
EventLoop::get_request_buffers_for_target_thread (const std::string& target_thread)
|
||||
{
|
||||
vector<ThreadBufferMapping> ret;
|
||||
Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock);
|
||||
Glib::Threads::Mutex::Lock lm (thread_buffer_requests_lock);
|
||||
|
||||
DEBUG_TRACE (PBD::DEBUG::EventLoop, string_compose ("%1 look for request buffers via %2\n", pthread_name(), target_thread));
|
||||
|
||||
for (ThreadRequestBufferList::const_iterator x = thread_buffer_requests.begin(); x != thread_buffer_requests.end(); ++x) {
|
||||
|
||||
if (x->second.target_thread_name == target_thread) {
|
||||
DEBUG_TRACE (PBD::DEBUG::EventLoop, string_compose ("for thread \"%1\", request buffer for %2/%3 thread %4\n", target_thread, x->first, x->second.emitting_thread));
|
||||
ret.push_back (x->second);
|
||||
}
|
||||
for (auto const & tbr : thread_buffer_requests) {
|
||||
DEBUG_TRACE (PBD::DEBUG::EventLoop, string_compose ("for thread \"%1\", request buffer for %2 (%3) thread %4\n", target_thread, tbr.emitting_thread, tbr.num_requests));
|
||||
ret.push_back (tbr);
|
||||
}
|
||||
|
||||
DEBUG_TRACE (PBD::DEBUG::EventLoop, string_compose ("for thread \"%1\", found %2 request buffers\n", target_thread, ret.size()));
|
||||
|
|
@ -130,21 +127,6 @@ EventLoop::get_request_buffers_for_target_thread (const std::string& target_thre
|
|||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
EventLoop::register_request_buffer_factory (const string& target_thread_name, void* (*factory)(uint32_t))
|
||||
{
|
||||
|
||||
RequestBufferSupplier trs;
|
||||
trs.name = target_thread_name;
|
||||
trs.factory = factory;
|
||||
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock);
|
||||
request_buffer_suppliers.push_back (trs);
|
||||
}
|
||||
DEBUG_TRACE (PBD::DEBUG::EventLoop, string_compose ("event loop %1 registered a buffer factory for %2\n", pthread_name(), target_thread_name));
|
||||
}
|
||||
|
||||
void
|
||||
EventLoop::pre_register (const string& emitting_thread_name, uint32_t num_requests)
|
||||
{
|
||||
|
|
@ -160,85 +142,56 @@ EventLoop::pre_register (const string& emitting_thread_name, uint32_t num_reques
|
|||
*/
|
||||
|
||||
ThreadBufferMapping mapping;
|
||||
Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock);
|
||||
Glib::Threads::Mutex::Lock lm (thread_buffer_requests_lock);
|
||||
|
||||
for (RequestBufferSuppliers::iterator trs = request_buffer_suppliers.begin(); trs != request_buffer_suppliers.end(); ++trs) {
|
||||
mapping.emitting_thread = pthread_self();
|
||||
mapping.num_requests = num_requests;
|
||||
|
||||
if (!trs->factory) {
|
||||
/* no factory - no request buffer required or expected */
|
||||
continue;
|
||||
}
|
||||
/* now store it where the receiving thread (trs->name) can find
|
||||
it if and when it is created. (Discovery happens in the
|
||||
AbstractUI constructor. Note that if
|
||||
*/
|
||||
|
||||
if (emitting_thread_name == trs->name) {
|
||||
/* no need to register an emitter with itself */
|
||||
continue;
|
||||
}
|
||||
/* management of the thread_request_buffers map works as
|
||||
* follows:
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
mapping.emitting_thread = pthread_self();
|
||||
mapping.target_thread_name = trs->name;
|
||||
|
||||
/* Allocate a suitably sized request buffer. This will set the
|
||||
* thread-local variable that holds a pointer to this request
|
||||
* buffer.
|
||||
*/
|
||||
mapping.request_buffer = trs->factory (num_requests);
|
||||
|
||||
/* now store it where the receiving thread (trs->name) can find
|
||||
it if and when it is created. (Discovery happens in the
|
||||
AbstractUI constructor. Note that if
|
||||
*/
|
||||
|
||||
const string key = string_compose ("%1/%2", emitting_thread_name, mapping.target_thread_name);
|
||||
|
||||
/* management of the thread_request_buffers map works as
|
||||
* follows:
|
||||
*
|
||||
* 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 (key was %4)\n",
|
||||
emitting_thread_name, trs->name, mapping.request_buffer, key));
|
||||
}
|
||||
thread_buffer_requests.push_back (mapping);
|
||||
DEBUG_TRACE (PBD::DEBUG::EventLoop, string_compose ("pre-registered thread \"%1\"\n", emitting_thread_name));
|
||||
}
|
||||
|
||||
void
|
||||
EventLoop::remove_request_buffer_from_map (void* ptr)
|
||||
EventLoop::remove_request_buffer_from_map (pthread_t pth)
|
||||
{
|
||||
Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock);
|
||||
Glib::Threads::Mutex::Lock 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) {
|
||||
if (x->emitting_thread == pth) {
|
||||
thread_buffer_requests.erase (x);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue