manually revert 4b3043cc and 141e6fb8181; add detailed explanatory comment
[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 "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)(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 lm (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 lm (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                 vec.buf[0]->valid = true;
172                 return vec.buf[0];
173         }
174
175         /* calling thread has not registered, so just allocate a new request on
176          * the heap. the lack of registration implies that realtime constraints
177          * are not at work.
178          */
179
180         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: allocated normal heap request of type %2, caller %3\n", event_loop_name(), rt, pthread_name()));
181
182         RequestObject* req = new RequestObject;
183         req->type = rt;
184
185         return req;
186 }
187
188 template <typename RequestObject> void
189 AbstractUI<RequestObject>::handle_ui_requests ()
190 {
191         RequestBufferMapIterator i;
192         RequestBufferVector vec;
193
194         /* check all registered per-thread buffers first */
195
196         request_buffer_map_lock.lock ();
197
198         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 check %2 request buffers for requests\n", event_loop_name(), request_buffers.size()));
199
200         for (i = request_buffers.begin(); i != request_buffers.end(); ++i) {
201
202                 while (true) {
203
204                         /* we must process requests 1 by 1 because
205                          * the request may run a recursive main
206                          * event loop that will itself call
207                          * handle_ui_requests. when we return
208                          * from the request handler, we cannot
209                          * expect that the state of queued requests
210                          * is even remotely consistent with
211                          * the condition before we called it.
212                          */
213
214                         i->second->get_read_vector (&vec);
215
216                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 reading requests from RB[%2] @ %5, requests = %3 + %4\n",
217                                                                              event_loop_name(), std::distance (request_buffers.begin(), i), vec.len[0], vec.len[1], i->second));
218
219                         if (vec.len[0] == 0) {
220                                 break;
221                         } else {
222                                 if (vec.buf[0]->valid) {
223                                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: valid request, unlocking before calling\n", event_loop_name()));
224                                         request_buffer_map_lock.unlock ();
225                                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: valid request, calling ::do_request()\n", event_loop_name()));
226                                         do_request (vec.buf[0]);
227                                         request_buffer_map_lock.lock ();
228                                         if (vec.buf[0]->invalidation) {
229                                                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: removing invalidation record for that request\n", event_loop_name()));
230                                                 vec.buf[0]->invalidation->requests.remove (vec.buf[0]);
231                                         } else {
232                                                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: no invalidation record for that request\n", event_loop_name()));
233                                         }
234                                 } else {
235                                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, "invalid request, ignoring\n");
236                                 }
237                                 i->second->increment_read_ptr (1);
238                         }
239                 }
240         }
241
242         /* clean up any dead request buffers (their thread has exited) */
243
244         for (i = request_buffers.begin(); i != request_buffers.end(); ) {
245                 if ((*i).second->dead) {
246                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 deleting dead per-thread request buffer for %3 @ %4\n",
247                                                                              event_loop_name(), pthread_name(), i->second));
248                         cerr << event_loop_name() << " noticed that a buffer was dead\n";
249                         delete (*i).second;
250                         RequestBufferMapIterator tmp = i;
251                         ++tmp;
252                         request_buffers.erase (i);
253                         i = tmp;
254                 } else {
255                         ++i;
256                 }
257         }
258
259         request_buffer_map_lock.unlock ();
260
261         /* and now, the generic request buffer. same rules as above apply */
262
263         Glib::Threads::Mutex::Lock lm (request_list_lock);
264
265         while (!request_list.empty()) {
266                 RequestObject* req = request_list.front ();
267                 request_list.pop_front ();
268
269                 /* We need to use this lock, because its the one
270                  * returned by slot_invalidation_mutex() and protects
271                  * against request invalidation.
272                  */
273
274                 request_buffer_map_lock.lock ();
275                 if (!req->valid) {
276                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 handling invalid heap request, type %3, deleting\n", event_loop_name(), pthread_name(), req->type));
277                         delete req;
278                         request_buffer_map_lock.unlock ();
279                         continue;
280                 }
281
282                 /* we're about to execute this request, so its
283                  * too late for any invalidation. mark
284                  * the request as "done" before we start.
285                  */
286
287                 if (req->invalidation) {
288                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 remove request from its invalidation list\n", event_loop_name(), pthread_name()));
289
290                         /* after this call, if the object referenced by the
291                          * invalidation record is deleted, it will no longer
292                          * try to mark the request as invalid.
293                          */
294
295                         req->invalidation->requests.remove (req);
296                 }
297
298                 /* at this point, an object involved in a functor could be
299                  * deleted before we actually execute the functor. so there is
300                  * a race condition that makes the invalidation architecture
301                  * somewhat pointless.
302                  *
303                  * really, we should only allow functors containing shared_ptr
304                  * references to objects to enter into the request queue.
305                  */
306
307                 request_buffer_map_lock.unlock ();
308
309                 /* unlock the request lock while we execute the request, so
310                  * that we don't needlessly block other threads (note: not RT
311                  * threads since they have their own queue) from making requests.
312                  */
313
314                 lm.release ();
315
316                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 execute request type %3\n", event_loop_name(), pthread_name(), req->type));
317
318                 /* and lets do it ... this is a virtual call so that each
319                  * specific type of UI can have its own set of requests without
320                  * some kind of central request type registration logic
321                  */
322
323                 do_request (req);
324
325                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 delete heap request type %3\n", event_loop_name(), pthread_name(), req->type));
326                 delete req;
327
328                 /* re-acquire the list lock so that we check again */
329
330                 lm.acquire();
331         }
332 }
333
334 template <typename RequestObject> void
335 AbstractUI<RequestObject>::send_request (RequestObject *req)
336 {
337         /* This is called to ask a given UI to carry out a request. It may be
338          * called from the same thread that runs the UI's event loop (see the
339          * caller_is_self() case below), or from any other thread.
340          */
341
342         if (base_instance() == 0) {
343                 return; /* XXX is this the right thing to do ? */
344         }
345
346         if (caller_is_self ()) {
347                 /* the thread that runs this UI's event loop is sending itself
348                    a request: we dispatch it immediately and inline.
349                 */
350                 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 direct dispatch of request type %3\n", event_loop_name(), pthread_name(), req->type));
351                 do_request (req);
352                 delete req;
353         } else {
354
355                 /* If called from a different thread, we first check to see if
356                  * the calling thread is registered with this UI. If so, there
357                  * is a per-thread ringbuffer of requests that ::get_request()
358                  * just set up a new request in. If so, all we need do here is
359                  * to advance the write ptr in that ringbuffer so that the next
360                  * request by this calling thread will use the next slot in
361                  * the ringbuffer. The ringbuffer has
362                  * single-reader/single-writer semantics because the calling
363                  * thread is the only writer, and the UI event loop is the only
364                  * reader.
365                  */
366
367                 RequestBuffer* rbuf = per_thread_request_buffer.get ();
368
369                 if (rbuf != 0) {
370                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 send per-thread request type %3 using ringbuffer @ %4\n", event_loop_name(), pthread_name(), req->type, rbuf));
371                         rbuf->increment_write_ptr (1);
372                 } else {
373                         /* no per-thread buffer, so just use a list with a lock so that it remains
374                            single-reader/single-writer semantics
375                         */
376                         DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 send heap request type %3\n", event_loop_name(), pthread_name(), req->type));
377                         Glib::Threads::Mutex::Lock lm (request_list_lock);
378                         request_list.push_back (req);
379                 }
380
381                 /* send the UI event loop thread a wakeup so that it will look
382                    at the per-thread and generic request lists.
383                 */
384
385                 signal_new_request ();
386         }
387 }
388
389 template<typename RequestObject> void
390 AbstractUI<RequestObject>::call_slot (InvalidationRecord* invalidation, const boost::function<void()>& f)
391 {
392         if (caller_is_self()) {
393                 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));
394                 f ();
395                 return;
396         }
397
398         RequestObject *req = get_request (BaseUI::CallSlot);
399
400         if (req == 0) {
401                 return;
402         }
403
404         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));
405
406         /* copy semantics: copy the functor into the request object */
407
408         req->the_slot = f;
409
410         /* the invalidation record is an object which will carry out
411          * invalidation of any requests associated with it when it is
412          * destroyed. it can be null. if its not null, associate this
413          * request with the invalidation record. this allows us to
414          * "cancel" requests submitted to the UI because they involved
415          * a functor that uses an object that is being deleted.
416          */
417
418         req->invalidation = invalidation;
419
420         if (invalidation) {
421                 invalidation->requests.push_back (req);
422                 invalidation->event_loop = this;
423         }
424
425         send_request (req);
426 }
427
428 template<typename RequestObject> void*
429 AbstractUI<RequestObject>::request_buffer_factory (uint32_t num_requests)
430 {
431         RequestBuffer*  mcr = new RequestBuffer (num_requests);
432         per_thread_request_buffer.set (mcr);
433         return mcr;
434 }