2 * Copyright (C) 2009-2018 Paul Davis <paul@linuxaudiosystems.com>
3 * Copyright (C) 2010 Carl Hetherington <carl@carlh.net>
4 * Copyright (C) 2015-2017 Robin Gareus <robin@gareus.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "pbd/compose.h"
26 #include "pbd/debug.h"
27 #include "pbd/event_loop.h"
28 #include "pbd/error.h"
29 #include "pbd/pthread_utils.h"
30 #include "pbd/stacktrace.h"
37 static void do_not_delete_the_loop_pointer (void*) { }
39 Glib::Threads::Private<EventLoop> EventLoop::thread_event_loop (do_not_delete_the_loop_pointer);
41 Glib::Threads::RWLock EventLoop::thread_buffer_requests_lock;
42 EventLoop::ThreadRequestBufferList EventLoop::thread_buffer_requests;
43 EventLoop::RequestBufferSuppliers EventLoop::request_buffer_suppliers;
45 EventLoop::EventLoop (string const& name)
50 EventLoop::~EventLoop ()
54 for (std::list<InvalidationRecord*>::iterator r = trash.begin(); r != trash.end(); ++r) {
55 if (!(*r)->in_use ()) {
63 EventLoop::get_event_loop_for_thread()
65 return thread_event_loop.get ();
69 EventLoop::set_event_loop_for_thread (EventLoop* loop)
71 thread_event_loop.set (loop);
75 EventLoop::invalidate_request (void* data)
77 InvalidationRecord* ir = (InvalidationRecord*) data;
79 /* Some of the requests queued with an EventLoop may involve functors
80 * that make method calls to objects whose lifetime is shorter
81 * than the EventLoop's. We do not want to make those calls if the
82 * object involve has been destroyed. To prevent this, we
83 * provide a way to invalidate those requests when the object is
86 * An object was passed to __invalidator() which added a callback to
87 * EventLoop::invalidate_request() to its "notify when destroyed"
88 * list. __invalidator() returned an InvalidationRecord that has been
89 * to passed to this function as data.
91 * The object is currently being destroyed and so we want to
92 * mark all requests involving this object that are queued with
93 * any EventLoop as invalid.
95 * As of April 2012, we are usign sigc::trackable as the base object
96 * used to queue calls to ::invalidate_request() to be made upon
97 * destruction, via its ::add_destroy_notify_callback() API. This is
98 * not necessarily ideal, but it is very close to precisely what we
99 * want, and many of the objects we want to do this with already
100 * inherit (indirectly) from sigc::trackable.
103 if (ir->event_loop) {
104 DEBUG_TRACE (PBD::DEBUG::EventLoop, string_compose ("%1: invalidating request from %2 (%3) @ %4\n", pthread_name(), ir->event_loop, ir->event_loop->event_loop_name(), ir));
105 Glib::Threads::Mutex::Lock lm (ir->event_loop->slot_invalidation_mutex());
107 ir->event_loop->trash.push_back(ir);
113 vector<EventLoop::ThreadBufferMapping>
114 EventLoop::get_request_buffers_for_target_thread (const std::string& target_thread)
116 vector<ThreadBufferMapping> ret;
117 Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock);
119 for (ThreadRequestBufferList::const_iterator x = thread_buffer_requests.begin();
120 x != thread_buffer_requests.end(); ++x) {
122 if (x->second.target_thread_name == target_thread) {
123 ret.push_back (x->second);
127 DEBUG_TRACE (PBD::DEBUG::EventLoop, string_compose ("for thread \"%1\", found %2 request buffers\n", target_thread, ret.size()));
133 EventLoop::register_request_buffer_factory (const string& target_thread_name,
134 void* (*factory)(uint32_t))
137 RequestBufferSupplier trs;
138 trs.name = target_thread_name;
139 trs.factory = factory;
142 Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock);
143 request_buffer_suppliers.push_back (trs);
148 EventLoop::pre_register (const string& emitting_thread_name, uint32_t num_requests)
150 /* Threads that need to emit signals "towards" other threads, but with
151 RT safe behavior may be created before the receiving threads
152 exist. This makes it impossible for them to use the
153 ThreadCreatedWithRequestSize signal to notify receiving threads of
156 This function creates a request buffer for them to use with
157 the (not yet) created threads, and stores it where the receiving
158 thread can find it later.
161 ThreadBufferMapping mapping;
162 Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock);
164 for (RequestBufferSuppliers::iterator trs = request_buffer_suppliers.begin(); trs != request_buffer_suppliers.end(); ++trs) {
167 /* no factory - no request buffer required or expected */
171 if (emitting_thread_name == trs->name) {
172 /* no need to register an emitter with itself */
176 mapping.emitting_thread = pthread_self();
177 mapping.target_thread_name = trs->name;
179 /* Allocate a suitably sized request buffer. This will set the
180 * thread-local variable that holds a pointer to this request
183 mapping.request_buffer = trs->factory (num_requests);
185 /* now store it where the receiving thread (trs->name) can find
186 it if and when it is created. (Discovery happens in the
187 AbstractUI constructor. Note that if
190 const string key = string_compose ("%1/%2", emitting_thread_name, mapping.target_thread_name);
192 /* management of the thread_request_buffers map works as
195 * when the factory method was called above, the pointer to the
196 * created buffer is set as a thread-local-storage (TLS) value
197 * for this (the emitting) thread.
199 * The TLS value is set up with a destructor that marks the
200 * request buffer as "dead" when the emitting thread exits.
202 * An entry will remain in the map after the thread exits.
204 * The receiving thread may (if it receives requests from other
205 * threads) notice the dead buffer. If it does, it will delete
206 * the request buffer, and call
207 * ::remove_request_buffer_from_map() to get rid of it from the map.
209 * This does mean that the lifetime of the request buffer is
210 * indeterminate: if the receiving thread were to receive no
211 * further requests, the request buffer will live on
212 * forever. But this is OK, because if there are no requests
213 * arriving, the receiving thread is not attempting to use the
214 * request buffer(s) in any way.
216 * Note, however, that *if* an emitting thread is recreated
217 * with the same name (e.g. when a control surface is
218 * enabled/disabled/enabled), then the request buffer for the
219 * new thread will replace the map entry for the key, because
220 * of the matching thread names. This does mean that
221 * potentially the request buffer can leak in this case, but
222 * (a) these buffers are not really that large anyway (b) the
223 * scenario is not particularly common (c) the buffers would
224 * typically last across a session instance if not program
228 thread_buffer_requests[key] = mapping;
229 DEBUG_TRACE (PBD::DEBUG::EventLoop, string_compose ("pre-registered request buffer for \"%1\" to send to \"%2\", buffer @ %3 (key was %4)\n",
230 emitting_thread_name, trs->name, mapping.request_buffer, key));
235 EventLoop::remove_request_buffer_from_map (void* ptr)
237 Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock);
239 for (ThreadRequestBufferList::iterator x = thread_buffer_requests.begin(); x != thread_buffer_requests.end(); ++x) {
240 if (x->second.request_buffer == ptr) {
241 thread_buffer_requests.erase (x);