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