8e5b9ceb07681a192f93b3a8d8d3622d9b01c33e
[ardour.git] / libs / pbd / pbd / abstract_ui.cc
1 #include <unistd.h>
2
3 #include <pbd/abstract_ui.h>
4 #include <pbd/pthread_utils.h>
5 #include <pbd/failed_constructor.h>
6
7 #include "i18n.h"
8
9 using namespace std;
10
11 template <typename RequestObject>
12 AbstractUI<RequestObject>::AbstractUI (string name, bool with_signal_pipes)
13         : BaseUI (name, with_signal_pipes)
14 {
15         if (pthread_key_create (&thread_request_buffer_key, 0)) {
16                 cerr << _("cannot create thread request buffer key") << endl;
17                 throw failed_constructor();
18         }
19
20         PBD::ThreadCreatedWithRequestSize.connect (mem_fun (*this, &AbstractUI<RequestObject>::register_thread_with_request_count));
21 }
22
23 template <typename RequestObject> void
24 AbstractUI<RequestObject>::register_thread (pthread_t thread_id, string name)
25 {
26         register_thread_with_request_count (thread_id, name, 256);
27 }
28
29 template <typename RequestObject> void
30 AbstractUI<RequestObject>::register_thread_with_request_count (pthread_t thread_id, string thread_name, uint32_t num_requests)
31 {
32         RequestBuffer* b = new RequestBuffer (num_requests);
33
34         {
35         Glib::Mutex::Lock lm (request_buffer_map_lock);
36                 request_buffers[thread_id] = b;
37         }
38
39         pthread_setspecific (thread_request_buffer_key, b);
40 }
41
42 template <typename RequestObject> RequestObject*
43 AbstractUI<RequestObject>::get_request (RequestType rt)
44 {
45         RequestBuffer* rbuf = static_cast<RequestBuffer*>(pthread_getspecific (thread_request_buffer_key));
46         
47         if (rbuf == 0) {
48                 /* Cannot happen, but if it does we can't use the error reporting mechanism */
49                 cerr << _("programming error: ")
50                      << string_compose ("no %1-UI request buffer found for thread %2", name(), pthread_name())
51                      << endl;
52                 abort ();
53         }
54         
55         RequestBufferVector vec;
56         vec.buf[0] = 0;
57         vec.buf[1] = 0;
58         
59         rbuf->get_write_vector (&vec);
60
61         if (vec.len[0] == 0) {
62                 if (vec.len[1] == 0) {
63                         cerr << string_compose ("no space in %1-UI request buffer for thread %2", name(), pthread_name())
64                              << endl;
65                         return 0;
66                 } else {
67                         vec.buf[1]->type = rt;
68                         return vec.buf[1];
69                 }
70         } else {
71                 vec.buf[0]->type = rt;
72                 return vec.buf[0];
73         }
74 }
75
76 template <typename RequestObject> void
77 AbstractUI<RequestObject>::handle_ui_requests ()
78 {
79         RequestBufferMapIterator i;
80
81         request_buffer_map_lock.lock ();
82
83         for (i = request_buffers.begin(); i != request_buffers.end(); ++i) {
84
85                 RequestBufferVector vec;
86
87                 while (true) {
88
89                         /* we must process requests 1 by 1 because
90                            the request may run a recursive main
91                            event loop that will itself call
92                            handle_ui_requests. when we return
93                            from the request handler, we cannot
94                            expect that the state of queued requests
95                            is even remotely consistent with
96                            the condition before we called it.
97                         */
98
99                         i->second->get_read_vector (&vec);
100
101                         if (vec.len[0] == 0) {
102                                 break;
103                         } else {
104                                 request_buffer_map_lock.unlock ();
105                                 do_request (vec.buf[0]);
106                                 request_buffer_map_lock.lock ();
107                                 i->second->increment_read_ptr (1);
108                         } 
109                 }
110         }
111
112         request_buffer_map_lock.unlock ();
113 }
114
115 template <typename RequestObject> void
116 AbstractUI<RequestObject>::send_request (RequestObject *req)
117 {
118         if (base_instance() == 0) {
119                 return; /* XXX is this the right thing to do ? */
120         }
121         
122         if (caller_is_ui_thread()) {
123                 // cerr << "GUI thread sent request " << req << " type = " << req->type << endl;
124                 do_request (req);
125         } else {        
126                 RequestBuffer* rbuf = static_cast<RequestBuffer*> (pthread_getspecific (thread_request_buffer_key));
127
128                 if (rbuf == 0) {
129                         /* can't use the error system to report this, because this
130                            thread isn't registered!
131                         */
132                         cerr << _("programming error: ")
133                              << string_compose ("AbstractUI::send_request() called from %1 (%2), but no request buffer exists for that thread", name(), pthread_name())
134                              << endl;
135                         abort ();
136                 }
137                 
138                 // cerr << "thread " << pthread_self() << " sent request " << req << " type = " << req->type << endl;
139
140                 rbuf->increment_write_ptr (1);
141
142                 if (signal_pipe[1] >= 0) {
143                         const char c = 0;
144                         write (signal_pipe[1], &c, 1);
145                 }
146         }
147 }
148