Use XMLNode::set_property API in PBD::Undo class
[ardour.git] / libs / pbd / pbd / abstract_ui.cc
1 /*
2     Copyright (C) 2012 Paul Davis
3
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.
8
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.
13
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.
17 */
18
19 #include <unistd.h>
20 #include <iostream>
21 #include <algorithm>
22
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"
28
29 #include "pbd/i18n.h"
30
31 #ifdef COMPILER_MSVC
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)
38 #endif
39
40 using namespace std;
41
42 template<typename RequestBuffer> void
43 cleanup_request_buffer (void* ptr)
44 {
45         RequestBuffer* rb = (RequestBuffer*) ptr;
46
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.
50          *
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.
56          */
57         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("thread \"%1\" exits: marking request buffer as dead @ %2\n", pthread_name(), rb));
58         rb->dead = true;
59 }
60
61 template<typename R>
62 Glib::Threads::Private<typename AbstractUI<R>::RequestBuffer> AbstractUI<R>::per_thread_request_buffer (cleanup_request_buffer<AbstractUI<R>::RequestBuffer>);
63
64 template <typename RequestObject>
65 AbstractUI<RequestObject>::AbstractUI (const string& name)
66         : BaseUI (name)
67 {
68         void (AbstractUI<RequestObject>::*pmf)(pthread_t,string,uint32_t) = &AbstractUI<RequestObject>::register_thread;
69
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.
72         */
73
74         PBD::ThreadCreatedWithRequestSize.connect_same_thread (new_thread_connection, boost::bind (pmf, this, _1, _2, _3));
75
76         /* find pre-registerer threads */
77
78         vector<EventLoop::ThreadBufferMapping> tbm = EventLoop::get_request_buffers_for_target_thread (event_loop_name());
79
80         {
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);
84                 }
85         }
86 }
87
88 template <typename RequestObject>
89 AbstractUI<RequestObject>::~AbstractUI ()
90 {
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);
94                         delete (*i).second;
95                 }
96         }
97 }
98
99 template <typename RequestObject> void
100 AbstractUI<RequestObject>::register_thread (pthread_t thread_id, string thread_name, uint32_t num_requests)
101 {
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).
106          */
107
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));
109
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.
117         */
118
119         RequestBuffer* b = per_thread_request_buffer.get();
120
121         if (!b) {
122
123                 /* create a new request queue/ringbuffer */
124
125                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("create new request buffer for %1 in %2\n", thread_name, event_loop_name()));
126
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()
131
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()
135                 */
136
137                 per_thread_request_buffer.set (b);
138         } else {
139                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 : %2 is already registered\n", event_loop_name(), thread_name));
140         }
141
142         {
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.
148                 */
149                 Glib::Threads::Mutex::Lock rbml (request_buffer_map_lock);
150                 request_buffers[thread_id] = b;
151         }
152
153 }
154
155 template <typename RequestObject> RequestObject*
156 AbstractUI<RequestObject>::get_request (RequestType rt)
157 {
158         RequestBuffer* rbuf = per_thread_request_buffer.get ();
159         RequestBufferVector vec;
160
161         /* see comments in ::register_thread() above for an explanation of
162            the per_thread_request_buffer variable
163         */
164
165         if (rbuf != 0) {
166
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.
170                  */
171
172                 rbuf->get_write_vector (&vec);
173
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));
176                         return 0;
177                 }
178
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()));
180
181                 vec.buf[0]->type = rt;
182                 return vec.buf[0];
183         }
184
185         /* calling thread has not registered, so just allocate a new request on
186          * the heap. the lack of registration implies that realtime constraints
187          * are not at work.
188          */
189
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()));
191
192         RequestObject* req = new RequestObject;
193         req->type = rt;
194
195         return req;
196 }
197
198 template <typename RequestObject> void
199 AbstractUI<RequestObject>::handle_ui_requests ()
200 {
201         RequestBufferMapIterator i;
202         RequestBufferVector vec;
203
204         /* check all registered per-thread buffers first */
205         Glib::Threads::Mutex::Lock rbml (request_buffer_map_lock);
206
207         /* clean up any dead invalidation records (object was deleted) */
208         trash.sort();
209         trash.unique();
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;
215                         ++tmp;
216                         delete *r;
217                         trash.erase (r);
218                         r = tmp;
219                 } else {
220                         ++r;
221                 }
222         }
223 #ifndef NDEBUG
224         if (trash.size() > 0) {
225                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 items in trash: %2\n", event_loop_name(), trash.size()));
226         }
227 #endif
228
229         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 check %2 request buffers for requests\n", event_loop_name(), request_buffers.size()));
230
231         for (i = request_buffers.begin(); i != request_buffers.end(); ++i) {
232
233                 while (!(*i).second->dead) {
234
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.
243                          */
244
245                         i->second->get_read_vector (&vec);
246
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));
249
250                         if (vec.len[0] == 0) {
251                                 break;
252                         } else {
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()));
255                                         rbml.release ();
256                                 } else {
257
258                                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: valid request, unlocking before calling\n", event_loop_name()));
259                                         rbml.release ();
260
261                                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: valid request, calling ::do_request()\n", event_loop_name()));
262                                         do_request (vec.buf[0]);
263                                 }
264
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.
267                                  *
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.
271                                  */
272
273                                 if (vec.buf[0]->type == CallSlot) {
274                                         vec.buf[0]->the_slot = 0;
275                                 }
276
277                                 rbml.acquire ();
278                                 if (vec.buf[0]->invalidation) {
279                                         vec.buf[0]->invalidation->unref ();
280                                 }
281                                 vec.buf[0]->invalidation = NULL;
282                                 i->second->increment_read_ptr (1);
283                         }
284                 }
285         }
286
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;
292                         ++tmp;
293                         /* remove it from the EventLoop static map of all request buffers */
294                         EventLoop::remove_request_buffer_from_map ((*i).second);
295                         /* delete it
296                          *
297                          * Deleting the ringbuffer destroys all RequestObjects
298                          * and thereby drops any InvalidationRecord references of
299                          * requests that have not been processed.
300                          */
301                         delete (*i).second;
302                         /* remove it from this thread's list of request buffers */
303                         request_buffers.erase (i);
304                         i = tmp;
305                 } else {
306                         ++i;
307                 }
308         }
309
310         /* and now, the generic request buffer. same rules as above apply */
311
312         while (!request_list.empty()) {
313                 assert (rbml.locked ());
314                 RequestObject* req = request_list.front ();
315                 request_list.pop_front ();
316
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.
320                  */
321
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));
324                         delete req;
325                         continue;
326                 }
327
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.
332                  *
333                  * really, we should only allow functors containing shared_ptr
334                  * references to objects to enter into the request queue.
335                  */
336
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.
340                  */
341
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 */
345
346                 rbml.release ();
347
348                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 execute request type %3\n", event_loop_name(), pthread_name(), req->type));
349
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
353                  */
354
355                 do_request (req);
356
357                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 delete heap request type %3\n", event_loop_name(), pthread_name(), req->type));
358                 delete req;
359
360                 /* re-acquire the list lock so that we check again */
361
362                 rbml.acquire();
363         }
364
365         rbml.release ();
366 }
367
368 template <typename RequestObject> void
369 AbstractUI<RequestObject>::send_request (RequestObject *req)
370 {
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.
374          */
375
376         if (base_instance() == 0) {
377                 delete req;
378                 return; /* XXX is this the right thing to do ? */
379         }
380
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.
384                 */
385                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 direct dispatch of request type %3\n", event_loop_name(), pthread_name(), req->type));
386                 do_request (req);
387                 delete req;
388         } else {
389
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
399                  * reader.
400                  */
401
402                 RequestBuffer* rbuf = per_thread_request_buffer.get ();
403
404                 if (rbuf != 0) {
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);
407                 } else {
408                         /* no per-thread buffer, so just use a list with a lock so that it remains
409                          * single-reader/single-writer semantics
410                          */
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);
414                 }
415
416                 /* send the UI event loop thread a wakeup so that it will look
417                    at the per-thread and generic request lists.
418                 */
419
420                 signal_new_request ();
421         }
422 }
423
424 template<typename RequestObject> void
425 AbstractUI<RequestObject>::call_slot (InvalidationRecord* invalidation, const boost::function<void()>& f)
426 {
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));
429                 f ();
430                 return;
431         }
432
433         /* object destruction may race with realtime signal emission.
434          *
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.
438          *
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
441          * (GUI thread).
442          */
443         if (invalidation) {
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));
446                         return;
447                 }
448                 invalidation->ref ();
449                 invalidation->event_loop = this;
450         }
451
452         RequestObject *req = get_request (BaseUI::CallSlot);
453
454         if (req == 0) {
455                 if (invalidation) {
456                         invalidation->unref ();
457                 }
458                 return;
459         }
460
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));
462
463         /* copy semantics: copy the functor into the request object */
464
465         req->the_slot = f;
466
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.
473          */
474
475         req->invalidation = invalidation;
476
477         send_request (req);
478 }
479
480 template<typename RequestObject> void*
481 AbstractUI<RequestObject>::request_buffer_factory (uint32_t num_requests)
482 {
483         RequestBuffer*  mcr = new RequestBuffer (num_requests); // XXX leaks
484         per_thread_request_buffer.set (mcr);
485         return mcr;
486 }