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.
23 #include "pbd/stacktrace.h"
24 #include "pbd/abstract_ui.h"
25 #include "pbd/pthread_utils.h"
26 #include "pbd/failed_constructor.h"
27 #include "pbd/debug.h"
32 #include <ardourext/misc.h> // Needed for 'DECLARE_DEFAULT_COMPARISONS'. Objects in an STL container can be
33 // searched and sorted. Thus, when instantiating the container, MSVC complains
34 // if the type of object being contained has no appropriate comparison operators
35 // defined (specifically, if operators '<' and '==' are undefined). This seems
36 // to be the case with ptw32 'pthread_t' which is a simple struct.
37 DECLARE_DEFAULT_COMPARISONS(ptw32_handle_t)
42 template<typename RequestBuffer> void
43 cleanup_request_buffer (void* ptr)
45 RequestBuffer* rb = (RequestBuffer*) ptr;
47 /* this is called when the thread for which this request buffer was
48 * allocated dies. That could be before or after the end of the UI
49 * event loop for which this request buffer provides communication.
51 * We are not modifying the UI's thread/buffer map, just marking it
52 * dead. If the UI is currently processing the buffers and misses
53 * this "dead" signal, it will find it the next time it receives
54 * a request. If the UI has finished processing requests, then
55 * we will leak this buffer object.
57 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("thread \"%1\" exits: marking request buffer as dead @ %2\n", pthread_name(), rb));
62 Glib::Threads::Private<typename AbstractUI<R>::RequestBuffer> AbstractUI<R>::per_thread_request_buffer (cleanup_request_buffer<AbstractUI<R>::RequestBuffer>);
64 template <typename RequestObject>
65 AbstractUI<RequestObject>::AbstractUI (const string& name)
68 void (AbstractUI<RequestObject>::*pmf)(pthread_t,string,uint32_t) = &AbstractUI<RequestObject>::register_thread;
70 /* better to make this connect a handler that runs in the UI event loop but the syntax seems hard, and
71 register_thread() is thread safe anyway.
74 PBD::ThreadCreatedWithRequestSize.connect_same_thread (new_thread_connection, boost::bind (pmf, this, _1, _2, _3));
76 /* find pre-registerer threads */
78 vector<EventLoop::ThreadBufferMapping> tbm = EventLoop::get_request_buffers_for_target_thread (event_loop_name());
81 Glib::Threads::Mutex::Lock rbml (request_buffer_map_lock);
82 for (vector<EventLoop::ThreadBufferMapping>::iterator t = tbm.begin(); t != tbm.end(); ++t) {
83 request_buffers[t->emitting_thread] = static_cast<RequestBuffer*> (t->request_buffer);
88 template <typename RequestObject>
89 AbstractUI<RequestObject>::~AbstractUI ()
91 for (RequestBufferMapIterator i = request_buffers.begin(); i != request_buffers.end(); ++i) {
92 if ((*i).second->dead) {
93 EventLoop::remove_request_buffer_from_map ((*i).second);
99 template <typename RequestObject> void
100 AbstractUI<RequestObject>::register_thread (pthread_t thread_id, string thread_name, uint32_t num_requests)
102 /* the calling thread wants to register with the thread that runs this
103 * UI's event loop, so that it will have its own per-thread queue of
104 * requests. this means that when it makes a request to this UI it can
105 * do so in a realtime-safe manner (no locks).
108 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("in %1 (thread name %4), %2 (%5) wants to register with UIs\n", event_loop_name(), thread_name, pthread_name(), DEBUG_THREAD_SELF));
110 /* the per_thread_request_buffer is a thread-private variable.
111 See pthreads documentation for more on these, but the key
112 thing is that it is a variable that as unique value for
113 each thread, guaranteed. Note that the thread in question
114 is the caller of this function, which is assumed to be the
115 thread from which signals will be emitted that this UI's
116 event loop will catch.
119 RequestBuffer* b = per_thread_request_buffer.get();
123 /* create a new request queue/ringbuffer */
125 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("create new request buffer for %1 in %2\n", thread_name, event_loop_name()));
127 b = new RequestBuffer (num_requests); // XXX leaks
128 /* set this thread's per_thread_request_buffer to this new
129 queue/ringbuffer. remember that only this thread will
130 get this queue when it calls per_thread_request_buffer.get()
132 the second argument is a function that will be called
133 when the thread exits, and ensures that the buffer is marked
134 dead. it will then be deleted during a call to handle_ui_requests()
137 per_thread_request_buffer.set (b);
139 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 : %2 is already registered\n", event_loop_name(), thread_name));
143 /* add the new request queue (ringbuffer) to our map
144 so that we can iterate over it when the time is right.
145 This step is not RT-safe, but is assumed to be called
146 only at thread initialization time, not repeatedly,
147 and so this is of little consequence.
149 Glib::Threads::Mutex::Lock rbml (request_buffer_map_lock);
150 request_buffers[thread_id] = b;
155 template <typename RequestObject> RequestObject*
156 AbstractUI<RequestObject>::get_request (RequestType rt)
158 RequestBuffer* rbuf = per_thread_request_buffer.get ();
159 RequestBufferVector vec;
161 /* see comments in ::register_thread() above for an explanation of
162 the per_thread_request_buffer variable
167 /* the calling thread has registered with this UI and therefore
168 * we have a per-thread request queue/ringbuffer. use it. this
169 * "allocation" of a request is RT-safe.
172 rbuf->get_write_vector (&vec);
174 if (vec.len[0] == 0) {
175 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: no space in per thread pool for request of type %2\n", event_loop_name(), rt));
179 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: allocated per-thread request of type %2, caller %3\n", event_loop_name(), rt, pthread_name()));
181 vec.buf[0]->type = rt;
185 /* calling thread has not registered, so just allocate a new request on
186 * the heap. the lack of registration implies that realtime constraints
190 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: allocated normal heap request of type %2, caller %3\n", event_loop_name(), rt, pthread_name()));
192 RequestObject* req = new RequestObject;
198 template <typename RequestObject> void
199 AbstractUI<RequestObject>::handle_ui_requests ()
201 RequestBufferMapIterator i;
202 RequestBufferVector vec;
204 /* check all registered per-thread buffers first */
205 Glib::Threads::Mutex::Lock rbml (request_buffer_map_lock);
207 /* clean up any dead invalidation records (object was deleted) */
210 for (std::list<InvalidationRecord*>::iterator r = trash.begin(); r != trash.end();) {
211 if (!(*r)->in_use ()) {
212 assert (!(*r)->valid ());
213 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 drop invalidation trash %2\n", event_loop_name(), *r));
214 std::list<InvalidationRecord*>::iterator tmp = r;
224 if (trash.size() > 0) {
225 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 items in trash: %2\n", event_loop_name(), trash.size()));
229 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 check %2 request buffers for requests\n", event_loop_name(), request_buffers.size()));
231 for (i = request_buffers.begin(); i != request_buffers.end(); ++i) {
233 while (!(*i).second->dead) {
235 /* we must process requests 1 by 1 because
236 * the request may run a recursive main
237 * event loop that will itself call
238 * handle_ui_requests. when we return
239 * from the request handler, we cannot
240 * expect that the state of queued requests
241 * is even remotely consistent with
242 * the condition before we called it.
245 i->second->get_read_vector (&vec);
247 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 reading requests from RB[%2] @ %5, requests = %3 + %4\n",
248 event_loop_name(), std::distance (request_buffers.begin(), i), vec.len[0], vec.len[1], i->second));
250 if (vec.len[0] == 0) {
253 if (vec.buf[0]->invalidation && !vec.buf[0]->invalidation->valid ()) {
254 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: skipping invalidated request\n", event_loop_name()));
258 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: valid request, unlocking before calling\n", event_loop_name()));
261 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: valid request, calling ::do_request()\n", event_loop_name()));
262 do_request (vec.buf[0]);
265 /* if the request was CallSlot, then we need to ensure that we reset the functor in the request, in case it
266 * held a shared_ptr<>. Failure to do so can lead to dangling references to objects passed to PBD::Signals.
268 * Note that this method (::handle_ui_requests()) is by definition called from the event loop thread, so
269 * caller_is_self() is true, which means that the execution of the functor has definitely happened after
270 * do_request() returns and we no longer need the functor for any reason.
273 if (vec.buf[0]->type == CallSlot) {
274 vec.buf[0]->the_slot = 0;
278 if (vec.buf[0]->invalidation) {
279 vec.buf[0]->invalidation->unref ();
281 vec.buf[0]->invalidation = NULL;
282 i->second->increment_read_ptr (1);
287 assert (rbml.locked ());
288 for (i = request_buffers.begin(); i != request_buffers.end(); ) {
289 if ((*i).second->dead) {
290 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 deleting dead per-thread request buffer for %3 @ %4 (%5 requests)\n", event_loop_name(), pthread_name(), i->second, (*i).second->read_space()));
291 RequestBufferMapIterator tmp = i;
293 /* remove it from the EventLoop static map of all request buffers */
294 EventLoop::remove_request_buffer_from_map ((*i).second);
297 * Deleting the ringbuffer destroys all RequestObjects
298 * and thereby drops any InvalidationRecord references of
299 * requests that have not been processed.
302 /* remove it from this thread's list of request buffers */
303 request_buffers.erase (i);
310 /* and now, the generic request buffer. same rules as above apply */
312 while (!request_list.empty()) {
313 assert (rbml.locked ());
314 RequestObject* req = request_list.front ();
315 request_list.pop_front ();
317 /* we're about to execute this request, so its
318 * too late for any invalidation. mark
319 * the request as "done" before we start.
322 if (req->invalidation && !req->invalidation->valid()) {
323 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 handling invalid heap request, type %3, deleting\n", event_loop_name(), pthread_name(), req->type));
328 /* at this point, an object involved in a functor could be
329 * deleted before we actually execute the functor. so there is
330 * a race condition that makes the invalidation architecture
331 * somewhat pointless.
333 * really, we should only allow functors containing shared_ptr
334 * references to objects to enter into the request queue.
337 /* unlock the request lock while we execute the request, so
338 * that we don't needlessly block other threads (note: not RT
339 * threads since they have their own queue) from making requests.
342 /* also the request may destroy the object itself resulting in a direct
343 * path to EventLoop::invalidate_request () from here
344 * which takes the lock */
348 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 execute request type %3\n", event_loop_name(), pthread_name(), req->type));
350 /* and lets do it ... this is a virtual call so that each
351 * specific type of UI can have its own set of requests without
352 * some kind of central request type registration logic
357 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 delete heap request type %3\n", event_loop_name(), pthread_name(), req->type));
360 /* re-acquire the list lock so that we check again */
368 template <typename RequestObject> void
369 AbstractUI<RequestObject>::send_request (RequestObject *req)
371 /* This is called to ask a given UI to carry out a request. It may be
372 * called from the same thread that runs the UI's event loop (see the
373 * caller_is_self() case below), or from any other thread.
376 if (base_instance() == 0) {
378 return; /* XXX is this the right thing to do ? */
381 if (caller_is_self ()) {
382 /* the thread that runs this UI's event loop is sending itself
383 a request: we dispatch it immediately and inline.
385 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 direct dispatch of request type %3\n", event_loop_name(), pthread_name(), req->type));
390 /* If called from a different thread, we first check to see if
391 * the calling thread is registered with this UI. If so, there
392 * is a per-thread ringbuffer of requests that ::get_request()
393 * just set up a new request in. If so, all we need do here is
394 * to advance the write ptr in that ringbuffer so that the next
395 * request by this calling thread will use the next slot in
396 * the ringbuffer. The ringbuffer has
397 * single-reader/single-writer semantics because the calling
398 * thread is the only writer, and the UI event loop is the only
402 RequestBuffer* rbuf = per_thread_request_buffer.get ();
405 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 send per-thread request type %3 using ringbuffer @ %4 IR: %5\n", event_loop_name(), pthread_name(), req->type, rbuf, req->invalidation));
406 rbuf->increment_write_ptr (1);
408 /* no per-thread buffer, so just use a list with a lock so that it remains
409 * single-reader/single-writer semantics
411 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 send heap request type %3 IR %4\n", event_loop_name(), pthread_name(), req->type, req->invalidation));
412 Glib::Threads::Mutex::Lock lm (request_buffer_map_lock);
413 request_list.push_back (req);
416 /* send the UI event loop thread a wakeup so that it will look
417 at the per-thread and generic request lists.
420 signal_new_request ();
424 template<typename RequestObject> void
425 AbstractUI<RequestObject>::call_slot (InvalidationRecord* invalidation, const boost::function<void()>& f)
427 if (caller_is_self()) {
428 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 direct dispatch of call slot via functor @ %3, invalidation %4\n", event_loop_name(), pthread_name(), &f, invalidation));
433 /* object destruction may race with realtime signal emission.
435 * There may be a concurrent event-loop in progress of deleting
436 * the slot-object. That's perfectly fine. But we need to mark
437 * the invalidation record itself as being used by the request.
439 * The IR needs to be kept around until the last signal using
440 * it is disconnected and then it can be deleted in the event-loop
444 if (!invalidation->valid()) {
445 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 ignoring call-slot using functor @ %3, dead invalidation %4\n", event_loop_name(), pthread_name(), &f, invalidation));
448 invalidation->ref ();
449 invalidation->event_loop = this;
452 RequestObject *req = get_request (BaseUI::CallSlot);
456 invalidation->unref ();
461 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 queue call-slot using functor @ %3, invalidation %4\n", event_loop_name(), pthread_name(), &f, invalidation));
463 /* copy semantics: copy the functor into the request object */
467 /* the invalidation record is an object which will carry out
468 * invalidation of any requests associated with it when it is
469 * destroyed. it can be null. if its not null, associate this
470 * request with the invalidation record. this allows us to
471 * "cancel" requests submitted to the UI because they involved
472 * a functor that uses an object that is being deleted.
475 req->invalidation = invalidation;
480 template<typename RequestObject> void*
481 AbstractUI<RequestObject>::request_buffer_factory (uint32_t num_requests)
483 RequestBuffer* mcr = new RequestBuffer (num_requests); // XXX leaks
484 per_thread_request_buffer.set (mcr);