Fix binding of automation list undo records to MIDI sources. Should fix the remainde...
[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
9 #include "i18n.h"
10
11 using namespace std;
12
13 static void do_not_delete_the_request_buffer (void*) { }
14
15 template<typename R>
16 Glib::StaticPrivate<typename AbstractUI<R>::RequestBuffer> AbstractUI<R>::per_thread_request_buffer;
17
18 template <typename RequestObject>
19 AbstractUI<RequestObject>::AbstractUI (const string& name)
20         : BaseUI (name)
21 {
22         void (AbstractUI<RequestObject>::*pmf)(string,pthread_t,string,uint32_t) = &AbstractUI<RequestObject>::register_thread;
23
24         /* better to make this connect a handler that runs in the UI event loop but the syntax seems hard, and 
25            register_thread() is thread safe anyway.
26         */
27
28         PBD::ThreadCreatedWithRequestSize.connect_same_thread (new_thread_connection, boost::bind (pmf, this, _1, _2, _3, _4));
29 }
30
31 template <typename RequestObject> void
32 AbstractUI<RequestObject>::register_thread (string target_gui, pthread_t thread_id, string /*thread name*/, uint32_t num_requests)
33 {
34         if (target_gui != name()) {
35                 return;
36         }
37
38         RequestBuffer* b = new RequestBuffer (num_requests);
39
40         {
41                 Glib::Mutex::Lock lm (request_buffer_map_lock);
42                 request_buffers[thread_id] = b;
43         }
44
45         per_thread_request_buffer.set (b, do_not_delete_the_request_buffer);
46 }
47
48 template <typename RequestObject> RequestObject*
49 AbstractUI<RequestObject>::get_request (RequestType rt)
50 {
51         RequestBuffer* rbuf = per_thread_request_buffer.get ();
52         RequestBufferVector vec;
53
54         if (rbuf != 0) {
55                 /* we have a per-thread FIFO, use it */
56
57                 rbuf->get_write_vector (&vec);
58
59                 if (vec.len[0] == 0) {
60                         return 0;
61                 }
62
63                 vec.buf[0]->type = rt;
64                 vec.buf[0]->valid = true;
65                 return vec.buf[0];
66         }
67
68         RequestObject* req = new RequestObject;
69         req->type = rt;
70
71         return req;
72 }
73
74 template <typename RequestObject> void
75 AbstractUI<RequestObject>::handle_ui_requests ()
76 {
77         RequestBufferMapIterator i;
78         RequestBufferVector vec;
79
80         /* per-thread buffers first */
81
82         request_buffer_map_lock.lock ();
83
84         for (i = request_buffers.begin(); i != request_buffers.end(); ++i) {
85
86                 while (true) {
87
88                         /* we must process requests 1 by 1 because
89                            the request may run a recursive main
90                            event loop that will itself call
91                            handle_ui_requests. when we return
92                            from the request handler, we cannot
93                            expect that the state of queued requests
94                            is even remotely consistent with
95                            the condition before we called it.
96                         */
97
98                         i->second->get_read_vector (&vec);
99
100                         if (vec.len[0] == 0) {
101                                 break;
102                         } else {
103                                 if (vec.buf[0]->valid) {
104                                         request_buffer_map_lock.unlock ();
105                                         do_request (vec.buf[0]);
106                                         request_buffer_map_lock.lock ();
107                                         if (vec.buf[0]->invalidation) {
108                                                 vec.buf[0]->invalidation->requests.remove (vec.buf[0]);
109                                         }
110                                         i->second->increment_read_ptr (1);
111                                 }
112                         } 
113                 }
114         }
115
116         request_buffer_map_lock.unlock ();
117
118         /* and now, the generic request buffer. same rules as above apply */
119
120         Glib::Mutex::Lock lm (request_list_lock);
121
122         while (!request_list.empty()) {
123                 RequestObject* req = request_list.front ();
124                 request_list.pop_front ();
125
126                 /* We need to use this lock, because its the one
127                    returned by slot_invalidation_mutex() and protects
128                    against request invalidation.
129                 */
130
131                 request_buffer_map_lock.lock ();
132                 if (!req->valid) {
133                         delete req;
134                         request_buffer_map_lock.unlock ();
135                         continue;
136                 }
137
138                 /* we're about to execute this request, so its
139                    too late for any invalidation. mark
140                    the request as "done" before we start.
141                 */
142
143                 if (req->invalidation) {
144                         req->invalidation->requests.remove (req);
145                 }
146
147                 request_buffer_map_lock.unlock ();
148
149                 lm.release ();
150
151                 do_request (req);
152
153                 delete req;
154
155                 lm.acquire();
156         }
157 }
158
159 template <typename RequestObject> void
160 AbstractUI<RequestObject>::send_request (RequestObject *req)
161 {
162         if (base_instance() == 0) {
163                 return; /* XXX is this the right thing to do ? */
164         }
165
166         if (caller_is_self ()) {
167                 do_request (req);
168         } else {        
169                 RequestBuffer* rbuf = per_thread_request_buffer.get ();
170
171                 if (rbuf != 0) {
172                         rbuf->increment_write_ptr (1);
173                 } else {
174                         /* no per-thread buffer, so just use a list with a lock so that it remains
175                            single-reader/single-writer semantics
176                         */
177                         Glib::Mutex::Lock lm (request_list_lock);
178                         request_list.push_back (req);
179                 }
180
181                 request_channel.wakeup ();
182         }
183 }
184
185 template<typename RequestObject> void
186 AbstractUI<RequestObject>::call_slot (InvalidationRecord* invalidation, const boost::function<void()>& f)
187 {
188         if (caller_is_self()) {
189                 f ();
190                 return;
191         }
192
193         RequestObject *req = get_request (BaseUI::CallSlot);
194         
195         if (req == 0) {
196                 return;
197         }
198
199         req->the_slot = f;
200         req->invalidation = invalidation;
201
202         if (invalidation) {
203                 invalidation->requests.push_back (req);
204                 invalidation->event_loop = this;
205         }
206
207         send_request (req);
208 }       
209