d3d3c2e8b10c82c758c53f905f6d1c243e32e6fb
[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
20 #include <unistd.h>
21 #include <iostream>
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 "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
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)(string,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, _4));
75 }
76
77 template <typename RequestObject> void
78 AbstractUI<RequestObject>::register_thread (string target_gui, pthread_t thread_id, string thread_name, uint32_t num_requests)
79 {
80         /* the calling thread wants to register with the thread that runs this
81          * UI's event loop, so that it will have its own per-thread queue of
82          * requests. this means that when it makes a request to this UI it can
83          * do so in a realtime-safe manner (no locks).
84          */
85
86         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("in %1 (thread name %4), %2 (%5) wants to register with %3\n", event_loop_name(), thread_name, target_gui, pthread_name(), DEBUG_THREAD_SELF));
87
88         if (target_gui != event_loop_name()) {
89                 /* this UI is not the UI that the calling thread is trying to
90                    register with
91                 */
92                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 : not the registration target\n", event_loop_name()));
93                 return;
94         }
95
96         /* the per_thread_request_buffer is a thread-private variable.
97            See pthreads documentation for more on these, but the key
98            thing is that it is a variable that as unique value for
99            each thread, guaranteed.
100         */
101
102         RequestBuffer* b = per_thread_request_buffer.get();
103
104         if (b) {
105                 /* thread already registered with this UI
106                 */
107                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 : %2 is already registered\n", event_loop_name(), thread_name));
108                 return;
109         }
110
111         /* create a new request queue/ringbuffer */
112
113         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("create new request buffer for %1 in %2\n", thread_name, event_loop_name()));
114
115         b = new RequestBuffer (num_requests, *this);
116
117         {
118                 /* add the new request queue (ringbuffer) to our map
119                    so that we can iterate over it when the time is right.
120                    This step is not RT-safe, but is assumed to be called
121                    only at thread initialization time, not repeatedly,
122                    and so this is of little consequence.
123                 */
124                 Glib::Threads::Mutex::Lock lm (request_buffer_map_lock);
125                 request_buffers[thread_id] = b;
126         }
127
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 }
139
140 template <typename RequestObject> RequestObject*
141 AbstractUI<RequestObject>::get_request (RequestType rt)
142 {
143         RequestBuffer* rbuf = per_thread_request_buffer.get ();
144         RequestBufferVector vec;
145
146         /* see comments in ::register_thread() above for an explanation of
147            the per_thread_request_buffer variable
148         */
149
150         if (rbuf != 0) {
151
152                 /* the calling thread has registered with this UI and therefore
153                  * we have a per-thread request queue/ringbuffer. use it. this
154                  * "allocation" of a request is RT-safe.
155                  */
156
157                 rbuf->get_write_vector (&vec);
158
159                 if (vec.len[0] == 0) {
160                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: no space in per thread pool for request of type %2\n", event_loop_name(), rt));
161                         return 0;
162                 }
163
164                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: allocated per-thread request of type %2, caller %3\n", event_loop_name(), rt, pthread_name()));
165
166                 vec.buf[0]->type = rt;
167                 vec.buf[0]->valid = true;
168                 return vec.buf[0];
169         }
170
171         /* calling thread has not registered, so just allocate a new request on
172          * the heap. the lack of registration implies that realtime constraints
173          * are not at work.
174          */
175
176         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: allocated normal heap request of type %2, caller %3\n", event_loop_name(), rt, pthread_name()));
177
178         RequestObject* req = new RequestObject;
179         req->type = rt;
180
181         return req;
182 }
183
184 template <typename RequestObject> void
185 AbstractUI<RequestObject>::handle_ui_requests ()
186 {
187         RequestBufferMapIterator i;
188         RequestBufferVector vec;
189
190         /* check all registered per-thread buffers first */
191
192         request_buffer_map_lock.lock ();
193
194         for (i = request_buffers.begin(); i != request_buffers.end(); ++i) {
195
196                 while (true) {
197
198                         /* we must process requests 1 by 1 because
199                          * the request may run a recursive main
200                          * event loop that will itself call
201                          * handle_ui_requests. when we return
202                          * from the request handler, we cannot
203                          * expect that the state of queued requests
204                          * is even remotely consistent with
205                          * the condition before we called it.
206                          */
207
208                         i->second->get_read_vector (&vec);
209
210                         if (vec.len[0] == 0) {
211                                 break;
212                         } else {
213                                 if (vec.buf[0]->valid) {
214                                         request_buffer_map_lock.unlock ();
215                                         do_request (vec.buf[0]);
216                                         request_buffer_map_lock.lock ();
217                                         if (vec.buf[0]->invalidation) {
218                                                 vec.buf[0]->invalidation->requests.remove (vec.buf[0]);
219                                         }
220                                 }
221                                 i->second->increment_read_ptr (1);
222                         }
223                 }
224         }
225
226         /* clean up any dead request buffers (their thread has exited) */
227
228         for (i = request_buffers.begin(); i != request_buffers.end(); ) {
229                 if ((*i).second->dead) {
230                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 deleting dead per-thread request buffer for %3 @ %4\n",
231                                                                              event_loop_name(), pthread_name(), i->second));
232                         delete (*i).second;
233                         RequestBufferMapIterator tmp = i;
234                         ++tmp;
235                         request_buffers.erase (i);
236                         i = tmp;
237                 } else {
238                         ++i;
239                 }
240         }
241
242         request_buffer_map_lock.unlock ();
243
244         /* and now, the generic request buffer. same rules as above apply */
245
246         Glib::Threads::Mutex::Lock lm (request_list_lock);
247
248         while (!request_list.empty()) {
249                 RequestObject* req = request_list.front ();
250                 request_list.pop_front ();
251
252                 /* We need to use this lock, because its the one
253                  * returned by slot_invalidation_mutex() and protects
254                  * against request invalidation.
255                  */
256
257                 request_buffer_map_lock.lock ();
258                 if (!req->valid) {
259                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 handling invalid heap request, type %3, deleting\n", event_loop_name(), pthread_name(), req->type));
260                         delete req;
261                         request_buffer_map_lock.unlock ();
262                         continue;
263                 }
264
265                 /* we're about to execute this request, so its
266                  * too late for any invalidation. mark
267                  * the request as "done" before we start.
268                  */
269
270                 if (req->invalidation) {
271                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 remove request from its invalidation list\n", event_loop_name(), pthread_name()));
272
273                         /* after this call, if the object referenced by the
274                          * invalidation record is deleted, it will no longer
275                          * try to mark the request as invalid.
276                          */
277
278                         req->invalidation->requests.remove (req);
279                 }
280
281                 /* at this point, an object involved in a functor could be
282                  * deleted before we actually execute the functor. so there is
283                  * a race condition that makes the invalidation architecture
284                  * somewhat pointless.
285                  *
286                  * really, we should only allow functors containing shared_ptr
287                  * references to objects to enter into the request queue.
288                  */
289
290                 request_buffer_map_lock.unlock ();
291
292                 /* unlock the request lock while we execute the request, so
293                  * that we don't needlessly block other threads (note: not RT
294                  * threads since they have their own queue) from making requests.
295                  */
296
297                 lm.release ();
298
299                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 execute request type %3\n", event_loop_name(), pthread_name(), req->type));
300
301                 /* and lets do it ... this is a virtual call so that each
302                  * specific type of UI can have its own set of requests without
303                  * some kind of central request type registration logic
304                  */
305
306                 do_request (req);
307
308                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 delete heap request type %3\n", event_loop_name(), pthread_name(), req->type));
309                 delete req;
310
311                 /* re-acquire the list lock so that we check again */
312
313                 lm.acquire();
314         }
315 }
316
317 template <typename RequestObject> void
318 AbstractUI<RequestObject>::send_request (RequestObject *req)
319 {
320         /* This is called to ask a given UI to carry out a request. It may be
321          * called from the same thread that runs the UI's event loop (see the
322          * caller_is_self() case below), or from any other thread.
323          */
324
325         if (base_instance() == 0) {
326                 return; /* XXX is this the right thing to do ? */
327         }
328
329         if (caller_is_self ()) {
330                 /* the thread that runs this UI's event loop is sending itself
331                    a request: we dispatch it immediately and inline.
332                 */
333                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 direct dispatch of request type %3\n", event_loop_name(), pthread_name(), req->type));
334                 do_request (req);
335                 delete req;
336         } else {
337
338                 /* If called from a different thread, we first check to see if
339                  * the calling thread is registered with this UI. If so, there
340                  * is a per-thread ringbuffer of requests that ::get_request()
341                  * just set up a new request in. If so, all we need do here is
342                  * to advance the write ptr in that ringbuffer so that the next
343                  * request by this calling thread will use the next slot in
344                  * the ringbuffer. The ringbuffer has
345                  * single-reader/single-writer semantics because the calling
346                  * thread is the only writer, and the UI event loop is the only
347                  * reader.
348                  */
349
350                 RequestBuffer* rbuf = per_thread_request_buffer.get ();
351
352                 if (rbuf != 0) {
353                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 send per-thread request type %3\n", event_loop_name(), pthread_name(), req->type));
354                         rbuf->increment_write_ptr (1);
355                 } else {
356                         /* no per-thread buffer, so just use a list with a lock so that it remains
357                            single-reader/single-writer semantics
358                         */
359                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 send heap request type %3\n", event_loop_name(), pthread_name(), req->type));
360                         Glib::Threads::Mutex::Lock lm (request_list_lock);
361                         request_list.push_back (req);
362                 }
363
364                 /* send the UI event loop thread a wakeup so that it will look
365                    at the per-thread and generic request lists.
366                 */
367
368                 signal_new_request ();
369         }
370 }
371
372 template<typename RequestObject> void
373 AbstractUI<RequestObject>::call_slot (InvalidationRecord* invalidation, const boost::function<void()>& f)
374 {
375         if (caller_is_self()) {
376                 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));
377                 f ();
378                 return;
379         }
380
381         RequestObject *req = get_request (BaseUI::CallSlot);
382
383         if (req == 0) {
384                 return;
385         }
386
387         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));
388
389         /* copy semantics: copy the functor into the request object */
390
391         req->the_slot = f;
392
393         /* the invalidation record is an object which will carry out
394          * invalidation of any requests associated with it when it is
395          * destroyed. it can be null. if its not null, associate this
396          * request with the invalidation record. this allows us to
397          * "cancel" requests submitted to the UI because they involved
398          * a functor that uses an object that is being deleted.
399          */
400
401         req->invalidation = invalidation;
402
403         if (invalidation) {
404                 invalidation->requests.push_back (req);
405                 invalidation->event_loop = this;
406         }
407
408         send_request (req);
409 }