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