2 Copyright (C) 2012 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "pbd/compose.h"
25 #include "pbd/debug.h"
26 #include "pbd/event_loop.h"
27 #include "pbd/error.h"
28 #include "pbd/stacktrace.h"
35 static void do_not_delete_the_loop_pointer (void*) { }
37 Glib::Threads::Private<EventLoop> EventLoop::thread_event_loop (do_not_delete_the_loop_pointer);
39 Glib::Threads::RWLock EventLoop::thread_buffer_requests_lock;
40 EventLoop::ThreadRequestBufferList EventLoop::thread_buffer_requests;
41 EventLoop::RequestBufferSuppliers EventLoop::request_buffer_suppliers;
43 EventLoop::EventLoop (string const& name)
49 EventLoop::get_event_loop_for_thread()
51 return thread_event_loop.get ();
55 EventLoop::set_event_loop_for_thread (EventLoop* loop)
57 thread_event_loop.set (loop);
61 EventLoop::invalidate_request (void* data)
63 InvalidationRecord* ir = (InvalidationRecord*) data;
65 /* Some of the requests queued with an EventLoop may involve functors
66 * that make method calls to objects whose lifetime is shorter
67 * than the EventLoop's. We do not want to make those calls if the
68 * object involve has been destroyed. To prevent this, we
69 * provide a way to invalidate those requests when the object is
72 * An object was passed to __invalidator() which added a callback to
73 * EventLoop::invalidate_request() to its "notify when destroyed"
74 * list. __invalidator() returned an InvalidationRecord that has been
75 * to passed to this function as data.
77 * The object is currently being destroyed and so we want to
78 * mark all requests involving this object that are queued with
79 * any EventLoop as invalid.
81 * As of April 2012, we are usign sigc::trackable as the base object
82 * used to queue calls to ::invalidate_request() to be made upon
83 * destruction, via its ::add_destroy_notify_callback() API. This is
84 * not necessarily ideal, but it is very close to precisely what we
85 * want, and many of the objects we want to do this with already
86 * inherit (indirectly) from sigc::trackable.
90 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: EventLoop::invalidate_request %2\n", ir->event_loop, ir));
91 Glib::Threads::Mutex::Lock lm (ir->event_loop->slot_invalidation_mutex());
93 ir->event_loop->trash.push_back(ir);
99 vector<EventLoop::ThreadBufferMapping>
100 EventLoop::get_request_buffers_for_target_thread (const std::string& target_thread)
102 vector<ThreadBufferMapping> ret;
103 Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock);
105 for (ThreadRequestBufferList::const_iterator x = thread_buffer_requests.begin();
106 x != thread_buffer_requests.end(); ++x) {
108 if (x->second.target_thread_name == target_thread) {
109 ret.push_back (x->second);
113 DEBUG_TRACE (PBD::DEBUG::EventLoop, string_compose ("for thread \"%1\", found %2 request buffers\n", target_thread, ret.size()));
119 EventLoop::register_request_buffer_factory (const string& target_thread_name,
120 void* (*factory)(uint32_t))
123 RequestBufferSupplier trs;
124 trs.name = target_thread_name;
125 trs.factory = factory;
128 Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock);
129 request_buffer_suppliers.push_back (trs);
134 EventLoop::pre_register (const string& emitting_thread_name, uint32_t num_requests)
136 /* Threads that need to emit signals "towards" other threads, but with
137 RT safe behavior may be created before the receiving threads
138 exist. This makes it impossible for them to use the
139 ThreadCreatedWithRequestSize signal to notify receiving threads of
142 This function creates a request buffer for them to use with
143 the (not yet) created threads, and stores it where the receiving
144 thread can find it later.
147 ThreadBufferMapping mapping;
148 Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock);
150 for (RequestBufferSuppliers::iterator trs = request_buffer_suppliers.begin(); trs != request_buffer_suppliers.end(); ++trs) {
153 /* no factory - no request buffer required or expected */
157 if (emitting_thread_name == trs->name) {
158 /* no need to register an emitter with itself */
162 mapping.emitting_thread = pthread_self();
163 mapping.target_thread_name = trs->name;
165 /* Allocate a suitably sized request buffer. This will set the
166 * thread-local variable that holds a pointer to this request
169 mapping.request_buffer = trs->factory (num_requests);
171 /* now store it where the receiving thread (trs->name) can find
172 it if and when it is created. (Discovery happens in the
173 AbstractUI constructor. Note that if
176 const string key = string_compose ("%1/%2", emitting_thread_name, mapping.target_thread_name);
178 /* management of the thread_request_buffers map works as
181 * when the factory method was called above, the pointer to the
182 * created buffer is set as a thread-local-storage (TLS) value
183 * for this (the emitting) thread.
185 * The TLS value is set up with a destructor that marks the
186 * request buffer as "dead" when the emitting thread exits.
188 * An entry will remain in the map after the thread exits.
190 * The receiving thread may (if it receives requests from other
191 * threads) notice the dead buffer. If it does, it will delete
192 * the request buffer, and call
193 * ::remove_request_buffer_from_map() to get rid of it from the map.
195 * This does mean that the lifetime of the request buffer is
196 * indeterminate: if the receiving thread were to receive no
197 * further requests, the request buffer will live on
198 * forever. But this is OK, because if there are no requests
199 * arriving, the receiving thread is not attempting to use the
200 * request buffer(s) in any way.
202 * Note, however, that *if* an emitting thread is recreated
203 * with the same name (e.g. when a control surface is
204 * enabled/disabled/enabled), then the request buffer for the
205 * new thread will replace the map entry for the key, because
206 * of the matching thread names. This does mean that
207 * potentially the request buffer can leak in this case, but
208 * (a) these buffers are not really that large anyway (b) the
209 * scenario is not particularly common (c) the buffers would
210 * typically last across a session instance if not program
214 thread_buffer_requests[key] = mapping;
215 DEBUG_TRACE (PBD::DEBUG::EventLoop, string_compose ("pre-registered request buffer for \"%1\" to send to \"%2\", buffer @ %3 (key was %4)\n",
216 emitting_thread_name, trs->name, mapping.request_buffer, key));
221 EventLoop::remove_request_buffer_from_map (void* ptr)
223 Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock);
225 for (ThreadRequestBufferList::iterator x = thread_buffer_requests.begin(); x != thread_buffer_requests.end(); ++x) {
226 if (x->second.request_buffer == ptr) {
227 thread_buffer_requests.erase (x);