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