C++98 compatible iterator erase
[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> void
89 AbstractUI<RequestObject>::register_thread (pthread_t thread_id, string thread_name, uint32_t num_requests)
90 {
91         /* the calling thread wants to register with the thread that runs this
92          * UI's event loop, so that it will have its own per-thread queue of
93          * requests. this means that when it makes a request to this UI it can
94          * do so in a realtime-safe manner (no locks).
95          */
96
97         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));
98
99         /* the per_thread_request_buffer is a thread-private variable.
100            See pthreads documentation for more on these, but the key
101            thing is that it is a variable that as unique value for
102            each thread, guaranteed. Note that the thread in question
103            is the caller of this function, which is assumed to be the
104            thread from which signals will be emitted that this UI's
105            event loop will catch.
106         */
107
108         RequestBuffer* b = per_thread_request_buffer.get();
109
110         if (!b) {
111
112                 /* create a new request queue/ringbuffer */
113
114                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("create new request buffer for %1 in %2\n", thread_name, event_loop_name()));
115
116                 b = new RequestBuffer (num_requests);
117                 /* set this thread's per_thread_request_buffer to this new
118                    queue/ringbuffer. remember that only this thread will
119                    get this queue when it calls per_thread_request_buffer.get()
120
121                    the second argument is a function that will be called
122                    when the thread exits, and ensures that the buffer is marked
123                    dead. it will then be deleted during a call to handle_ui_requests()
124                 */
125
126                 per_thread_request_buffer.set (b);
127         } else {
128                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 : %2 is already registered\n", event_loop_name(), thread_name));
129         }
130
131         {
132                 /* add the new request queue (ringbuffer) to our map
133                    so that we can iterate over it when the time is right.
134                    This step is not RT-safe, but is assumed to be called
135                    only at thread initialization time, not repeatedly,
136                    and so this is of little consequence.
137                 */
138                 Glib::Threads::Mutex::Lock rbml (request_buffer_map_lock);
139                 request_buffers[thread_id] = b;
140         }
141
142 }
143
144 template <typename RequestObject> RequestObject*
145 AbstractUI<RequestObject>::get_request (RequestType rt)
146 {
147         RequestBuffer* rbuf = per_thread_request_buffer.get ();
148         RequestBufferVector vec;
149
150         /* see comments in ::register_thread() above for an explanation of
151            the per_thread_request_buffer variable
152         */
153
154         if (rbuf != 0) {
155
156                 /* the calling thread has registered with this UI and therefore
157                  * we have a per-thread request queue/ringbuffer. use it. this
158                  * "allocation" of a request is RT-safe.
159                  */
160
161                 rbuf->get_write_vector (&vec);
162
163                 if (vec.len[0] == 0) {
164                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: no space in per thread pool for request of type %2\n", event_loop_name(), rt));
165                         return 0;
166                 }
167
168                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: allocated per-thread request of type %2, caller %3\n", event_loop_name(), rt, pthread_name()));
169
170                 vec.buf[0]->type = rt;
171                 return vec.buf[0];
172         }
173
174         /* calling thread has not registered, so just allocate a new request on
175          * the heap. the lack of registration implies that realtime constraints
176          * are not at work.
177          */
178
179         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: allocated normal heap request of type %2, caller %3\n", event_loop_name(), rt, pthread_name()));
180
181         RequestObject* req = new RequestObject;
182         req->type = rt;
183
184         return req;
185 }
186
187 template <typename RequestObject> void
188 AbstractUI<RequestObject>::handle_ui_requests ()
189 {
190         RequestBufferMapIterator i;
191         RequestBufferVector vec;
192
193         /* check all registered per-thread buffers first */
194         Glib::Threads::Mutex::Lock rbml (request_buffer_map_lock);
195
196         /* clean up any dead invalidation records (object was deleted) */
197         trash.sort();
198         trash.unique();
199         for (std::list<InvalidationRecord*>::const_iterator r = trash.begin(); r != trash.end();) {
200                 if (!(*r)->in_use ()) {
201                         assert (!(*r)->valid ());
202                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 drop invalidation trash %2\n", event_loop_name(), *r));
203                         std::list<InvalidationRecord*>::const_iterator tmp = r;
204                         ++tmp;
205                         delete *r;
206                         r = tmp;
207                 } else {
208                         ++r;
209                 }
210         }
211 #ifndef NDEBUG
212         if (trash.size() > 0) {
213                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 items in trash: %2\n", event_loop_name(), trash.size()));
214         }
215 #endif
216
217         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 check %2 request buffers for requests\n", event_loop_name(), request_buffers.size()));
218
219         for (i = request_buffers.begin(); i != request_buffers.end(); ++i) {
220
221                 while (!(*i).second->dead) {
222
223                         /* we must process requests 1 by 1 because
224                          * the request may run a recursive main
225                          * event loop that will itself call
226                          * handle_ui_requests. when we return
227                          * from the request handler, we cannot
228                          * expect that the state of queued requests
229                          * is even remotely consistent with
230                          * the condition before we called it.
231                          */
232
233                         i->second->get_read_vector (&vec);
234
235                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 reading requests from RB[%2] @ %5, requests = %3 + %4\n",
236                                                 event_loop_name(), std::distance (request_buffers.begin(), i), vec.len[0], vec.len[1], i->second));
237
238                         if (vec.len[0] == 0) {
239                                 break;
240                         } else {
241                                 if (vec.buf[0]->invalidation && !vec.buf[0]->invalidation->valid ()) {
242                                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: skipping invalidated request\n", event_loop_name()));
243                                         rbml.release ();
244                                 } else {
245
246                                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: valid request, unlocking before calling\n", event_loop_name()));
247                                         rbml.release ();
248
249                                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: valid request, calling ::do_request()\n", event_loop_name()));
250                                         do_request (vec.buf[0]);
251                                 }
252
253                                 /* if the request was CallSlot, then we need to ensure that we reset the functor in the request, in case it
254                                  * held a shared_ptr<>. Failure to do so can lead to dangling references to objects passed to PBD::Signals.
255                                  *
256                                  * Note that this method (::handle_ui_requests()) is by definition called from the event loop thread, so
257                                  * caller_is_self() is true, which means that the execution of the functor has definitely happened after
258                                  * do_request() returns and we no longer need the functor for any reason.
259                                  */
260
261                                 if (vec.buf[0]->type == CallSlot) {
262                                         vec.buf[0]->the_slot = 0;
263                                 }
264
265                                 rbml.acquire ();
266                                 if (vec.buf[0]->invalidation) {
267                                         vec.buf[0]->invalidation->unref ();
268                                 }
269                                 i->second->increment_read_ptr (1);
270                         }
271                 }
272         }
273
274         assert (rbml.locked ());
275         for (i = request_buffers.begin(); i != request_buffers.end(); ) {
276                 if ((*i).second->dead) {
277                         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()));
278                         RequestBufferMapIterator tmp = i;
279                         ++tmp;
280                         /* remove it from the EventLoop static map of all request buffers */
281                         EventLoop::remove_request_buffer_from_map ((*i).second);
282                         /* delete it
283                          *
284                          * Deleting the ringbuffer destroys all RequestObjects
285                          * and thereby drops any InvalidationRecord references of
286                          * requests that have not been processed.
287                          */
288                         delete (*i).second;
289                         /* remove it from this thread's list of request buffers */
290                         request_buffers.erase (i);
291                         i = tmp;
292                 } else {
293                         ++i;
294                 }
295         }
296
297         /* and now, the generic request buffer. same rules as above apply */
298
299         while (!request_list.empty()) {
300                 assert (rbml.locked ());
301                 RequestObject* req = request_list.front ();
302                 request_list.pop_front ();
303
304                 /* we're about to execute this request, so its
305                  * too late for any invalidation. mark
306                  * the request as "done" before we start.
307                  */
308
309                 if (req->invalidation && !req->invalidation->valid()) {
310                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 handling invalid heap request, type %3, deleting\n", event_loop_name(), pthread_name(), req->type));
311                         delete req;
312                         continue;
313                 }
314
315                 /* at this point, an object involved in a functor could be
316                  * deleted before we actually execute the functor. so there is
317                  * a race condition that makes the invalidation architecture
318                  * somewhat pointless.
319                  *
320                  * really, we should only allow functors containing shared_ptr
321                  * references to objects to enter into the request queue.
322                  */
323
324                 /* unlock the request lock while we execute the request, so
325                  * that we don't needlessly block other threads (note: not RT
326                  * threads since they have their own queue) from making requests.
327                  */
328
329                 /* also the request may destroy the object itself resulting in a direct
330                  * path to EventLoop::invalidate_request () from here
331                  * which takes the lock */
332
333                 rbml.release ();
334
335                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 execute request type %3\n", event_loop_name(), pthread_name(), req->type));
336
337                 /* and lets do it ... this is a virtual call so that each
338                  * specific type of UI can have its own set of requests without
339                  * some kind of central request type registration logic
340                  */
341
342                 do_request (req);
343
344                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 delete heap request type %3\n", event_loop_name(), pthread_name(), req->type));
345                 delete req;
346
347                 /* re-acquire the list lock so that we check again */
348
349                 rbml.acquire();
350         }
351
352         rbml.release ();
353 }
354
355 template <typename RequestObject> void
356 AbstractUI<RequestObject>::send_request (RequestObject *req)
357 {
358         /* This is called to ask a given UI to carry out a request. It may be
359          * called from the same thread that runs the UI's event loop (see the
360          * caller_is_self() case below), or from any other thread.
361          */
362
363         if (base_instance() == 0) {
364                 delete req;
365                 return; /* XXX is this the right thing to do ? */
366         }
367
368         if (caller_is_self ()) {
369                 /* the thread that runs this UI's event loop is sending itself
370                    a request: we dispatch it immediately and inline.
371                 */
372                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 direct dispatch of request type %3\n", event_loop_name(), pthread_name(), req->type));
373                 do_request (req);
374                 delete req;
375         } else {
376
377                 /* If called from a different thread, we first check to see if
378                  * the calling thread is registered with this UI. If so, there
379                  * is a per-thread ringbuffer of requests that ::get_request()
380                  * just set up a new request in. If so, all we need do here is
381                  * to advance the write ptr in that ringbuffer so that the next
382                  * request by this calling thread will use the next slot in
383                  * the ringbuffer. The ringbuffer has
384                  * single-reader/single-writer semantics because the calling
385                  * thread is the only writer, and the UI event loop is the only
386                  * reader.
387                  */
388
389                 RequestBuffer* rbuf = per_thread_request_buffer.get ();
390
391                 if (rbuf != 0) {
392                         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));
393                         rbuf->increment_write_ptr (1);
394                 } else {
395                         /* no per-thread buffer, so just use a list with a lock so that it remains
396                          * single-reader/single-writer semantics
397                          */
398                         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));
399                         Glib::Threads::Mutex::Lock lm (request_buffer_map_lock);
400                         request_list.push_back (req);
401                 }
402
403                 /* send the UI event loop thread a wakeup so that it will look
404                    at the per-thread and generic request lists.
405                 */
406
407                 signal_new_request ();
408         }
409 }
410
411 template<typename RequestObject> void
412 AbstractUI<RequestObject>::call_slot (InvalidationRecord* invalidation, const boost::function<void()>& f)
413 {
414         if (caller_is_self()) {
415                 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));
416                 f ();
417                 return;
418         }
419
420         if (invalidation) {
421                 Glib::Threads::Mutex::Lock lm (request_buffer_map_lock); //  -- remove this once signal connect/disconnect uses ir->un/ref()
422                 if (!invalidation->valid()) {
423                         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));
424                         return;
425                 }
426                 invalidation->ref ();
427                 assert (invalidation->event_loop == this);
428                 invalidation->event_loop = this; // XXX is this needed,  PBD::signal::connect sets it
429         }
430
431         RequestObject *req = get_request (BaseUI::CallSlot);
432
433         if (req == 0) {
434                 if (invalidation) {
435                         invalidation->unref ();
436                 }
437                 return;
438         }
439
440         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));
441
442         /* copy semantics: copy the functor into the request object */
443
444         req->the_slot = f;
445
446         /* the invalidation record is an object which will carry out
447          * invalidation of any requests associated with it when it is
448          * destroyed. it can be null. if its not null, associate this
449          * request with the invalidation record. this allows us to
450          * "cancel" requests submitted to the UI because they involved
451          * a functor that uses an object that is being deleted.
452          */
453
454         req->invalidation = invalidation;
455
456         send_request (req);
457 }
458
459 template<typename RequestObject> void*
460 AbstractUI<RequestObject>::request_buffer_factory (uint32_t num_requests)
461 {
462         RequestBuffer*  mcr = new RequestBuffer (num_requests); // leaks
463         per_thread_request_buffer.set (mcr);
464         return mcr;
465 }