2 Copyright (C) 2012 Paul Davis
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.
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.
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.
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"
32 #include <ardourext/misc.h> // Needed for 'DECLARE_DEFAULT_COMPARISONS'. Objects in an STL container can be
33 // searched and sorted. Thus, when instantiating the container, MSVC complains
34 // if the type of object being contained has no appropriate comparison operators
35 // defined (specifically, if operators '<' and '==' are undefined). This seems
36 // to be the case with ptw32 'pthread_t' which is a simple struct.
37 DECLARE_DEFAULT_COMPARISONS(ptw32_handle_t)
42 template<typename RequestBuffer> void
43 cleanup_request_buffer (void* ptr)
45 RequestBuffer* rb = (RequestBuffer*) ptr;
47 /* this is called when the thread for which this request buffer was
48 * allocated dies. That could be before or after the end of the UI
49 * event loop for which this request buffer provides communication.
51 * We are not modifying the UI's thread/buffer map, just marking it
52 * dead. If the UI is currently processing the buffers and misses
53 * this "dead" signal, it will find it the next time it receives
54 * a request. If the UI has finished processing requests, then
55 * we will leak this buffer object.
57 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("thread \"%1\" exits: marking request buffer as dead @ %2\n", pthread_name(), rb));
62 Glib::Threads::Private<typename AbstractUI<R>::RequestBuffer> AbstractUI<R>::per_thread_request_buffer (cleanup_request_buffer<AbstractUI<R>::RequestBuffer>);
64 template <typename RequestObject>
65 AbstractUI<RequestObject>::AbstractUI (const string& name)
68 void (AbstractUI<RequestObject>::*pmf)(pthread_t,string,uint32_t) = &AbstractUI<RequestObject>::register_thread;
70 /* better to make this connect a handler that runs in the UI event loop but the syntax seems hard, and
71 register_thread() is thread safe anyway.
74 PBD::ThreadCreatedWithRequestSize.connect_same_thread (new_thread_connection, boost::bind (pmf, this, _1, _2, _3));
76 /* find pre-registerer threads */
78 vector<EventLoop::ThreadBufferMapping> tbm = EventLoop::get_request_buffers_for_target_thread (event_loop_name());
81 Glib::Threads::Mutex::Lock lm (request_buffer_map_lock);
82 for (vector<EventLoop::ThreadBufferMapping>::iterator t = tbm.begin(); t != tbm.end(); ++t) {
83 request_buffers[t->emitting_thread] = static_cast<RequestBuffer*> (t->request_buffer);
88 template <typename RequestObject> void
89 AbstractUI<RequestObject>::register_thread (pthread_t thread_id, string thread_name, uint32_t num_requests)
91 /* the calling thread wants to register with the thread that runs this
92 * UI's event loop, so that it will have its own per-thread queue of
93 * requests. this means that when it makes a request to this UI it can
94 * do so in a realtime-safe manner (no locks).
97 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("in %1 (thread name %4), %2 (%5) wants to register with UIs\n", event_loop_name(), thread_name, pthread_name(), DEBUG_THREAD_SELF));
99 /* the per_thread_request_buffer is a thread-private variable.
100 See pthreads documentation for more on these, but the key
101 thing is that it is a variable that as unique value for
102 each thread, guaranteed. Note that the thread in question
103 is the caller of this function, which is assumed to be the
104 thread from which signals will be emitted that this UI's
105 event loop will catch.
108 RequestBuffer* b = per_thread_request_buffer.get();
112 /* create a new request queue/ringbuffer */
114 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("create new request buffer for %1 in %2\n", thread_name, event_loop_name()));
116 b = new RequestBuffer (num_requests);
117 /* set this thread's per_thread_request_buffer to this new
118 queue/ringbuffer. remember that only this thread will
119 get this queue when it calls per_thread_request_buffer.get()
121 the second argument is a function that will be called
122 when the thread exits, and ensures that the buffer is marked
123 dead. it will then be deleted during a call to handle_ui_requests()
126 per_thread_request_buffer.set (b);
128 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 : %2 is already registered\n", event_loop_name(), thread_name));
132 /* add the new request queue (ringbuffer) to our map
133 so that we can iterate over it when the time is right.
134 This step is not RT-safe, but is assumed to be called
135 only at thread initialization time, not repeatedly,
136 and so this is of little consequence.
138 Glib::Threads::Mutex::Lock lm (request_buffer_map_lock);
139 request_buffers[thread_id] = b;
144 template <typename RequestObject> RequestObject*
145 AbstractUI<RequestObject>::get_request (RequestType rt)
147 RequestBuffer* rbuf = per_thread_request_buffer.get ();
148 RequestBufferVector vec;
150 /* see comments in ::register_thread() above for an explanation of
151 the per_thread_request_buffer variable
156 /* the calling thread has registered with this UI and therefore
157 * we have a per-thread request queue/ringbuffer. use it. this
158 * "allocation" of a request is RT-safe.
161 rbuf->get_write_vector (&vec);
163 if (vec.len[0] == 0) {
164 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: no space in per thread pool for request of type %2\n", event_loop_name(), rt));
168 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: allocated per-thread request of type %2, caller %3\n", event_loop_name(), rt, pthread_name()));
170 vec.buf[0]->type = rt;
171 vec.buf[0]->valid = true;
175 /* calling thread has not registered, so just allocate a new request on
176 * the heap. the lack of registration implies that realtime constraints
180 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: allocated normal heap request of type %2, caller %3\n", event_loop_name(), rt, pthread_name()));
182 RequestObject* req = new RequestObject;
188 template <typename RequestObject> void
189 AbstractUI<RequestObject>::handle_ui_requests ()
191 RequestBufferMapIterator i;
192 RequestBufferVector vec;
194 /* check all registered per-thread buffers first */
196 request_buffer_map_lock.lock ();
198 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 check %2 request buffers for requests\n", event_loop_name(), request_buffers.size()));
200 for (i = request_buffers.begin(); i != request_buffers.end(); ++i) {
204 /* we must process requests 1 by 1 because
205 * the request may run a recursive main
206 * event loop that will itself call
207 * handle_ui_requests. when we return
208 * from the request handler, we cannot
209 * expect that the state of queued requests
210 * is even remotely consistent with
211 * the condition before we called it.
214 i->second->get_read_vector (&vec);
216 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1 reading requests from RB[%2] @ %5, requests = %3 + %4\n",
217 event_loop_name(), std::distance (request_buffers.begin(), i), vec.len[0], vec.len[1], i->second));
219 if (vec.len[0] == 0) {
222 if (vec.buf[0]->valid) {
223 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: valid request, unlocking before calling\n", event_loop_name()));
224 request_buffer_map_lock.unlock ();
225 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: valid request, calling ::do_request()\n", event_loop_name()));
226 do_request (vec.buf[0]);
227 request_buffer_map_lock.lock ();
228 if (vec.buf[0]->invalidation) {
229 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: removing invalidation record for that request\n", event_loop_name()));
230 vec.buf[0]->invalidation->requests.remove (vec.buf[0]);
232 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1: no invalidation record for that request\n", event_loop_name()));
235 DEBUG_TRACE (PBD::DEBUG::AbstractUI, "invalid request, ignoring\n");
237 i->second->increment_read_ptr (1);
242 /* clean up any dead request buffers (their thread has exited) */
244 for (i = request_buffers.begin(); i != request_buffers.end(); ) {
245 if ((*i).second->dead) {
246 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 deleting dead per-thread request buffer for %3 @ %4\n",
247 event_loop_name(), pthread_name(), i->second));
248 cerr << event_loop_name() << " noticed that a buffer was dead\n";
249 /* remove it from the EventLoop static map of all request buffers */
250 EventLoop::remove_request_buffer_from_map ((*i).second);
253 RequestBufferMapIterator tmp = i;
255 /* remove it from this thread's list of request buffers */
256 request_buffers.erase (i);
263 request_buffer_map_lock.unlock ();
265 /* and now, the generic request buffer. same rules as above apply */
267 Glib::Threads::Mutex::Lock lm (request_list_lock);
269 while (!request_list.empty()) {
270 RequestObject* req = request_list.front ();
271 request_list.pop_front ();
273 /* We need to use this lock, because its the one
274 * returned by slot_invalidation_mutex() and protects
275 * against request invalidation.
278 request_buffer_map_lock.lock ();
280 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 handling invalid heap request, type %3, deleting\n", event_loop_name(), pthread_name(), req->type));
282 request_buffer_map_lock.unlock ();
286 /* we're about to execute this request, so its
287 * too late for any invalidation. mark
288 * the request as "done" before we start.
291 if (req->invalidation) {
292 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 remove request from its invalidation list\n", event_loop_name(), pthread_name()));
294 /* after this call, if the object referenced by the
295 * invalidation record is deleted, it will no longer
296 * try to mark the request as invalid.
299 req->invalidation->requests.remove (req);
302 /* at this point, an object involved in a functor could be
303 * deleted before we actually execute the functor. so there is
304 * a race condition that makes the invalidation architecture
305 * somewhat pointless.
307 * really, we should only allow functors containing shared_ptr
308 * references to objects to enter into the request queue.
311 request_buffer_map_lock.unlock ();
313 /* unlock the request lock while we execute the request, so
314 * that we don't needlessly block other threads (note: not RT
315 * threads since they have their own queue) from making requests.
320 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 execute request type %3\n", event_loop_name(), pthread_name(), req->type));
322 /* and lets do it ... this is a virtual call so that each
323 * specific type of UI can have its own set of requests without
324 * some kind of central request type registration logic
329 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 delete heap request type %3\n", event_loop_name(), pthread_name(), req->type));
332 /* re-acquire the list lock so that we check again */
338 template <typename RequestObject> void
339 AbstractUI<RequestObject>::send_request (RequestObject *req)
341 /* This is called to ask a given UI to carry out a request. It may be
342 * called from the same thread that runs the UI's event loop (see the
343 * caller_is_self() case below), or from any other thread.
346 if (base_instance() == 0) {
347 return; /* XXX is this the right thing to do ? */
350 if (caller_is_self ()) {
351 /* the thread that runs this UI's event loop is sending itself
352 a request: we dispatch it immediately and inline.
354 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 direct dispatch of request type %3\n", event_loop_name(), pthread_name(), req->type));
359 /* If called from a different thread, we first check to see if
360 * the calling thread is registered with this UI. If so, there
361 * is a per-thread ringbuffer of requests that ::get_request()
362 * just set up a new request in. If so, all we need do here is
363 * to advance the write ptr in that ringbuffer so that the next
364 * request by this calling thread will use the next slot in
365 * the ringbuffer. The ringbuffer has
366 * single-reader/single-writer semantics because the calling
367 * thread is the only writer, and the UI event loop is the only
371 RequestBuffer* rbuf = per_thread_request_buffer.get ();
374 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 send per-thread request type %3 using ringbuffer @ %4\n", event_loop_name(), pthread_name(), req->type, rbuf));
375 rbuf->increment_write_ptr (1);
377 /* no per-thread buffer, so just use a list with a lock so that it remains
378 single-reader/single-writer semantics
380 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 send heap request type %3\n", event_loop_name(), pthread_name(), req->type));
381 Glib::Threads::Mutex::Lock lm (request_list_lock);
382 request_list.push_back (req);
385 /* send the UI event loop thread a wakeup so that it will look
386 at the per-thread and generic request lists.
389 signal_new_request ();
393 template<typename RequestObject> void
394 AbstractUI<RequestObject>::call_slot (InvalidationRecord* invalidation, const boost::function<void()>& f)
396 if (caller_is_self()) {
397 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 direct dispatch of call slot via functor @ %3, invalidation %4\n", event_loop_name(), pthread_name(), &f, invalidation));
402 RequestObject *req = get_request (BaseUI::CallSlot);
408 DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 queue call-slot using functor @ %3, invalidation %4\n", event_loop_name(), pthread_name(), &f, invalidation));
410 /* copy semantics: copy the functor into the request object */
414 /* the invalidation record is an object which will carry out
415 * invalidation of any requests associated with it when it is
416 * destroyed. it can be null. if its not null, associate this
417 * request with the invalidation record. this allows us to
418 * "cancel" requests submitted to the UI because they involved
419 * a functor that uses an object that is being deleted.
422 req->invalidation = invalidation;
425 invalidation->requests.push_back (req);
426 invalidation->event_loop = this;
432 template<typename RequestObject> void*
433 AbstractUI<RequestObject>::request_buffer_factory (uint32_t num_requests)
435 RequestBuffer* mcr = new RequestBuffer (num_requests);
436 per_thread_request_buffer.set (mcr);