2013-01-16 18:15:38 +00:00
/*
Copyright ( C ) 2012 Paul Davis
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
2008-06-02 21:41:35 +00:00
# include <unistd.h>
2009-12-22 20:21:43 +00:00
# include <iostream>
2008-06-02 21:41:35 +00:00
2009-12-09 03:05:14 +00:00
# include "pbd/stacktrace.h"
2009-02-25 18:26:51 +00:00
# include "pbd/abstract_ui.h"
# include "pbd/pthread_utils.h"
# include "pbd/failed_constructor.h"
2012-04-24 16:45:38 +00:00
# include "pbd/debug.h"
2008-06-02 21:41:35 +00:00
# include "i18n.h"
2013-07-16 18:00:49 +01:00
# ifdef COMPILER_MSVC
# include <ardourext/misc.h> // Needed for 'DECLARE_DEFAULT_COMPARISONS'. Objects in an STL container can be
// searched and sorted. Thus, when instantiating the container, MSVC complains
// if the type of object being contained has no appropriate comparison operators
// defined (specifically, if operators '<' and '==' are undefined). This seems
// to be the case with ptw32 'pthread_t' which is a simple struct.
2013-12-01 14:26:08 +00:00
DECLARE_DEFAULT_COMPARISONS ( ptw32_handle_t )
2013-07-16 18:00:49 +01:00
# endif
2008-06-02 21:41:35 +00:00
using namespace std ;
2011-02-07 18:21:50 +00:00
template < typename RequestBuffer > void
cleanup_request_buffer ( void * ptr )
{
RequestBuffer * rb = ( RequestBuffer * ) ptr ;
2012-04-24 16:45:38 +00:00
2014-05-27 23:00:43 -04:00
/* this is called when the thread for which this request buffer was
* allocated dies . That could be before or after the end of the UI
2014-04-18 04:21:54 -05:00
* event loop for which this request buffer provides communication .
2012-04-24 16:45:38 +00:00
*
2014-05-27 23:00:43 -04:00
* We are not modifying the UI ' s thread / buffer map , just marking it
* dead . If the UI is currently processing the buffers and misses
* this " dead " signal , it will find it the next time it receives
* a request . If the UI has finished processing requests , then
* we will leak this buffer object .
2012-04-24 16:45:38 +00:00
*/
2014-05-27 23:00:43 -04:00
rb - > dead = true ;
2011-02-07 18:21:50 +00:00
}
2012-07-25 17:48:55 +00:00
template < typename R >
Glib : : Threads : : Private < typename AbstractUI < R > : : RequestBuffer > AbstractUI < R > : : per_thread_request_buffer ( cleanup_request_buffer < AbstractUI < R > : : RequestBuffer > ) ;
2008-06-02 21:41:35 +00:00
template < typename RequestObject >
2009-12-09 03:05:14 +00:00
AbstractUI < RequestObject > : : AbstractUI ( const string & name )
: BaseUI ( name )
2008-06-02 21:41:35 +00:00
{
2009-12-22 20:21:43 +00:00
void ( AbstractUI < RequestObject > : : * pmf ) ( string , pthread_t , string , uint32_t ) = & AbstractUI < RequestObject > : : register_thread ;
/* better to make this connect a handler that runs in the UI event loop but the syntax seems hard, and
register_thread ( ) is thread safe anyway .
*/
PBD : : ThreadCreatedWithRequestSize . connect_same_thread ( new_thread_connection , boost : : bind ( pmf , this , _1 , _2 , _3 , _4 ) ) ;
2008-06-02 21:41:35 +00:00
}
template < typename RequestObject > void
2009-12-20 16:50:41 +00:00
AbstractUI < RequestObject > : : register_thread ( string target_gui , pthread_t thread_id , string /*thread name*/ , uint32_t num_requests )
2008-06-02 21:41:35 +00:00
{
2012-04-24 16:45:38 +00:00
/* the calling thread wants to register with the thread that runs this
* UI ' s event loop , so that it will have its own per - thread queue of
* requests . this means that when it makes a request to this UI it can
* do so in a realtime - safe manner ( no locks ) .
*/
2009-12-09 03:05:14 +00:00
if ( target_gui ! = name ( ) ) {
2012-04-24 16:45:38 +00:00
/* this UI is not the UI that the calling thread is trying to
register with
*/
2009-12-09 03:05:14 +00:00
return ;
}
2008-06-02 21:41:35 +00:00
2012-04-24 16:45:38 +00:00
/* the per_thread_request_buffer is a thread-private variable.
See pthreads documentation for more on these , but the key
thing is that it is a variable that as unique value for
each thread , guaranteed .
*/
2011-02-07 18:21:50 +00:00
RequestBuffer * b = per_thread_request_buffer . get ( ) ;
if ( b ) {
/* thread already registered with this UI
*/
return ;
}
2012-04-24 16:45:38 +00:00
/* create a new request queue/ringbuffer */
2011-02-07 18:21:50 +00:00
b = new RequestBuffer ( num_requests , * this ) ;
2008-06-02 21:41:35 +00:00
{
2012-04-24 16:45:38 +00:00
/* add the new request queue (ringbuffer) to our map
so that we can iterate over it when the time is right .
This step is not RT - safe , but is assumed to be called
only at thread initialization time , not repeatedly ,
and so this is of little consequence .
*/
2012-07-25 17:48:55 +00:00
Glib : : Threads : : Mutex : : Lock lm ( request_buffer_map_lock ) ;
2008-06-02 21:41:35 +00:00
request_buffers [ thread_id ] = b ;
}
2012-04-24 16:45:38 +00:00
/* set this thread's per_thread_request_buffer to this new
queue / ringbuffer . remember that only this thread will
get this queue when it calls per_thread_request_buffer . get ( )
the second argument is a function that will be called
when the thread exits , and ensures that the buffer is marked
dead . it will then be deleted during a call to handle_ui_requests ( )
*/
2012-07-25 17:48:55 +00:00
per_thread_request_buffer . set ( b ) ;
2008-06-02 21:41:35 +00:00
}
template < typename RequestObject > RequestObject *
AbstractUI < RequestObject > : : get_request ( RequestType rt )
{
2009-12-09 03:05:14 +00:00
RequestBuffer * rbuf = per_thread_request_buffer . get ( ) ;
2008-06-02 21:41:35 +00:00
RequestBufferVector vec ;
2012-04-24 16:45:38 +00:00
/* see comments in ::register_thread() above for an explanation of
the per_thread_request_buffer variable
*/
2009-12-09 03:05:14 +00:00
if ( rbuf ! = 0 ) {
2012-04-24 16:45:38 +00:00
/* the calling thread has registered with this UI and therefore
* we have a per - thread request queue / ringbuffer . use it . this
* " allocation " of a request is RT - safe .
*/
2009-12-09 03:05:14 +00:00
rbuf - > get_write_vector ( & vec ) ;
if ( vec . len [ 0 ] = = 0 ) {
2012-04-24 16:45:38 +00:00
DEBUG_TRACE ( PBD : : DEBUG : : AbstractUI , string_compose ( " %1: no space in per thread pool for request of type %2 \n " , name ( ) , rt ) ) ;
2008-06-02 21:41:35 +00:00
return 0 ;
}
2009-12-09 03:05:14 +00:00
2013-07-11 12:01:17 -04:00
DEBUG_TRACE ( PBD : : DEBUG : : AbstractUI , string_compose ( " %1: allocated per-thread request of type %2, caller %3 \n " , name ( ) , rt , pthread_name ( ) ) ) ;
2012-04-24 16:45:38 +00:00
2008-06-02 21:41:35 +00:00
vec . buf [ 0 ] - > type = rt ;
2010-03-30 15:18:43 +00:00
vec . buf [ 0 ] - > valid = true ;
2008-06-02 21:41:35 +00:00
return vec . buf [ 0 ] ;
}
2009-12-09 03:05:14 +00:00
2012-04-24 16:45:38 +00:00
/* calling thread has not registered, so just allocate a new request on
* the heap . the lack of registration implies that realtime constraints
* are not at work .
*/
2013-07-11 12:01:17 -04:00
DEBUG_TRACE ( PBD : : DEBUG : : AbstractUI , string_compose ( " %1: allocated normal heap request of type %2, caller %3 \n " , name ( ) , rt , pthread_name ( ) ) ) ;
2012-04-24 16:45:38 +00:00
2009-12-09 03:05:14 +00:00
RequestObject * req = new RequestObject ;
req - > type = rt ;
2010-03-30 15:18:43 +00:00
2009-12-09 03:05:14 +00:00
return req ;
2008-06-02 21:41:35 +00:00
}
template < typename RequestObject > void
AbstractUI < RequestObject > : : handle_ui_requests ( )
{
RequestBufferMapIterator i ;
2009-12-09 03:05:14 +00:00
RequestBufferVector vec ;
2012-04-24 16:45:38 +00:00
/* check all registered per-thread buffers first */
2008-06-02 21:41:35 +00:00
request_buffer_map_lock . lock ( ) ;
for ( i = request_buffers . begin ( ) ; i ! = request_buffers . end ( ) ; + + i ) {
2011-02-07 18:21:50 +00:00
while ( true ) {
/* we must process requests 1 by 1 because
the request may run a recursive main
event loop that will itself call
handle_ui_requests . when we return
from the request handler , we cannot
expect that the state of queued requests
is even remotely consistent with
the condition before we called it .
*/
i - > second - > get_read_vector ( & vec ) ;
if ( vec . len [ 0 ] = = 0 ) {
break ;
} else {
2010-03-30 15:18:43 +00:00
if ( vec . buf [ 0 ] - > valid ) {
request_buffer_map_lock . unlock ( ) ;
do_request ( vec . buf [ 0 ] ) ;
request_buffer_map_lock . lock ( ) ;
if ( vec . buf [ 0 ] - > invalidation ) {
2010-04-03 00:42:39 +00:00
vec . buf [ 0 ] - > invalidation - > requests . remove ( vec . buf [ 0 ] ) ;
2010-03-30 15:18:43 +00:00
}
2014-06-15 13:04:47 +02:00
delete vec . buf [ 0 ] ;
2010-03-30 15:18:43 +00:00
i - > second - > increment_read_ptr ( 1 ) ;
}
2011-02-07 18:21:50 +00:00
}
}
}
/* clean up any dead request buffers (their thread has exited) */
for ( i = request_buffers . begin ( ) ; i ! = request_buffers . end ( ) ; ) {
if ( ( * i ) . second - > dead ) {
2012-04-24 16:45:38 +00:00
DEBUG_TRACE ( PBD : : DEBUG : : AbstractUI , string_compose ( " %1/%2 deleting dead per-thread request buffer for %3 @ %4 \n " ,
2013-07-11 12:01:17 -04:00
name ( ) , pthread_name ( ) , i - > second ) ) ;
2011-02-07 18:21:50 +00:00
delete ( * i ) . second ;
RequestBufferMapIterator tmp = i ;
+ + tmp ;
request_buffers . erase ( i ) ;
i = tmp ;
} else {
+ + i ;
}
}
2008-06-02 21:41:35 +00:00
request_buffer_map_lock . unlock ( ) ;
2009-12-09 03:05:14 +00:00
/* and now, the generic request buffer. same rules as above apply */
2012-07-25 17:48:55 +00:00
Glib : : Threads : : Mutex : : Lock lm ( request_list_lock ) ;
2009-12-09 03:05:14 +00:00
while ( ! request_list . empty ( ) ) {
RequestObject * req = request_list . front ( ) ;
request_list . pop_front ( ) ;
2010-03-30 15:18:43 +00:00
/* We need to use this lock, because its the one
returned by slot_invalidation_mutex ( ) and protects
2012-04-24 16:45:38 +00:00
against request invalidation .
2010-03-30 15:18:43 +00:00
*/
request_buffer_map_lock . lock ( ) ;
if ( ! req - > valid ) {
2013-07-11 12:01:17 -04:00
DEBUG_TRACE ( PBD : : DEBUG : : AbstractUI , string_compose ( " %1/%2 handling invalid heap request, type %3, deleting \n " , name ( ) , pthread_name ( ) , req - > type ) ) ;
2010-03-30 15:18:43 +00:00
delete req ;
request_buffer_map_lock . unlock ( ) ;
continue ;
}
/* we're about to execute this request, so its
too late for any invalidation . mark
the request as " done " before we start .
*/
if ( req - > invalidation ) {
2013-07-11 12:01:17 -04:00
DEBUG_TRACE ( PBD : : DEBUG : : AbstractUI , string_compose ( " %1/%2 remove request from its invalidation list \n " , name ( ) , pthread_name ( ) ) ) ;
2012-04-24 16:45:38 +00:00
/* after this call, if the object referenced by the
* invalidation record is deleted , it will no longer
* try to mark the request as invalid .
*/
2010-04-03 00:42:39 +00:00
req - > invalidation - > requests . remove ( req ) ;
2010-03-30 15:18:43 +00:00
}
2012-04-24 16:45:38 +00:00
/* at this point, an object involved in a functor could be
* deleted before we actually execute the functor . so there is
* a race condition that makes the invalidation architecture
* somewhat pointless .
*
* really , we should only allow functors containing shared_ptr
* references to objects to enter into the request queue .
*/
2010-03-30 15:18:43 +00:00
request_buffer_map_lock . unlock ( ) ;
2012-04-24 16:45:38 +00:00
/* unlock the request lock while we execute the request, so
* that we don ' t needlessly block other threads ( note : not RT
* threads since they have their own queue ) from making requests .
*/
2010-03-30 15:18:43 +00:00
2009-12-09 03:05:14 +00:00
lm . release ( ) ;
2013-07-11 12:01:17 -04:00
DEBUG_TRACE ( PBD : : DEBUG : : AbstractUI , string_compose ( " %1/%2 execute request type %3 \n " , name ( ) , pthread_name ( ) , req - > type ) ) ;
2012-04-24 16:45:38 +00:00
/* and lets do it ... this is a virtual call so that each
* specific type of UI can have its own set of requests without
* some kind of central request type registration logic
*/
2009-12-09 03:05:14 +00:00
do_request ( req ) ;
2013-07-11 12:01:17 -04:00
DEBUG_TRACE ( PBD : : DEBUG : : AbstractUI , string_compose ( " %1/%2 delete heap request type %3 \n " , name ( ) , pthread_name ( ) , req - > type ) ) ;
2009-12-09 03:05:14 +00:00
delete req ;
2012-04-24 16:45:38 +00:00
/* re-acquire the list lock so that we check again */
2009-12-09 03:05:14 +00:00
lm . acquire ( ) ;
}
2008-06-02 21:41:35 +00:00
}
template < typename RequestObject > void
AbstractUI < RequestObject > : : send_request ( RequestObject * req )
{
2012-04-24 16:45:38 +00:00
/* This is called to ask a given UI to carry out a request. It may be
* called from the same thread that runs the UI ' s event loop ( see the
* caller_is_self ( ) case below ) , or from any other thread .
*/
2008-06-02 21:41:35 +00:00
if ( base_instance ( ) = = 0 ) {
return ; /* XXX is this the right thing to do ? */
}
2009-12-09 03:05:14 +00:00
if ( caller_is_self ( ) ) {
2012-04-24 16:45:38 +00:00
/* the thread that runs this UI's event loop is sending itself
a request : we dispatch it immediately and inline .
*/
2013-07-11 12:01:17 -04:00
DEBUG_TRACE ( PBD : : DEBUG : : AbstractUI , string_compose ( " %1/%2 direct dispatch of request type %3 \n " , name ( ) , pthread_name ( ) , req - > type ) ) ;
2008-06-02 21:41:35 +00:00
do_request ( req ) ;
2014-06-15 13:04:47 +02:00
delete req ;
2008-06-02 21:41:35 +00:00
} else {
2012-04-24 16:45:38 +00:00
/* If called from a different thread, we first check to see if
* the calling thread is registered with this UI . If so , there
* is a per - thread ringbuffer of requests that : : get_request ( )
* just set up a new request in . If so , all we need do here is
* to advance the write ptr in that ringbuffer so that the next
* request by this calling thread will use the next slot in
* the ringbuffer . The ringbuffer has
* single - reader / single - writer semantics because the calling
* thread is the only writer , and the UI event loop is the only
* reader .
*/
2009-12-09 03:05:14 +00:00
RequestBuffer * rbuf = per_thread_request_buffer . get ( ) ;
2008-06-02 21:41:35 +00:00
2009-12-09 03:05:14 +00:00
if ( rbuf ! = 0 ) {
2013-07-11 12:01:17 -04:00
DEBUG_TRACE ( PBD : : DEBUG : : AbstractUI , string_compose ( " %1/%2 send per-thread request type %3 \n " , name ( ) , pthread_name ( ) , req - > type ) ) ;
2009-12-09 03:05:14 +00:00
rbuf - > increment_write_ptr ( 1 ) ;
} else {
/* no per-thread buffer, so just use a list with a lock so that it remains
single - reader / single - writer semantics
2008-06-02 21:41:35 +00:00
*/
2013-07-11 12:01:17 -04:00
DEBUG_TRACE ( PBD : : DEBUG : : AbstractUI , string_compose ( " %1/%2 send heap request type %3 \n " , name ( ) , pthread_name ( ) , req - > type ) ) ;
2012-07-25 17:48:55 +00:00
Glib : : Threads : : Mutex : : Lock lm ( request_list_lock ) ;
2009-12-09 03:05:14 +00:00
request_list . push_back ( req ) ;
2008-06-02 21:41:35 +00:00
}
2012-04-24 16:45:38 +00:00
/* send the UI event loop thread a wakeup so that it will look
at the per - thread and generic request lists .
*/
2013-07-11 12:52:46 -04:00
signal_new_request ( ) ;
2008-06-02 21:41:35 +00:00
}
}
2009-12-09 03:05:14 +00:00
template < typename RequestObject > void
2010-03-30 15:18:43 +00:00
AbstractUI < RequestObject > : : call_slot ( InvalidationRecord * invalidation , const boost : : function < void ( ) > & f )
2009-12-09 03:05:14 +00:00
{
if ( caller_is_self ( ) ) {
2013-07-11 12:01:17 -04:00
DEBUG_TRACE ( PBD : : DEBUG : : AbstractUI , string_compose ( " %1/%2 direct dispatch of call slot via functor @ %3, invalidation %4 \n " , name ( ) , pthread_name ( ) , & f , invalidation ) ) ;
2009-12-11 23:30:48 +00:00
f ( ) ;
2009-12-09 03:05:14 +00:00
return ;
}
RequestObject * req = get_request ( BaseUI : : CallSlot ) ;
if ( req = = 0 ) {
return ;
}
2013-07-11 12:01:17 -04:00
DEBUG_TRACE ( PBD : : DEBUG : : AbstractUI , string_compose ( " %1/%2 queue call-slot using functor @ %3, invalidation %4 \n " , name ( ) , pthread_name ( ) , & f , invalidation ) ) ;
2012-04-24 16:45:38 +00:00
/* copy semantics: copy the functor into the request object */
2009-12-11 23:30:48 +00:00
req - > the_slot = f ;
2012-04-24 16:45:38 +00:00
/* the invalidation record is an object which will carry out
* invalidation of any requests associated with it when it is
* destroyed . it can be null . if its not null , associate this
* request with the invalidation record . this allows us to
* " cancel " requests submitted to the UI because they involved
* a functor that uses an object that is being deleted .
*/
2010-03-30 15:18:43 +00:00
req - > invalidation = invalidation ;
if ( invalidation ) {
2010-04-03 00:42:39 +00:00
invalidation - > requests . push_back ( req ) ;
2010-03-30 15:18:43 +00:00
invalidation - > event_loop = this ;
}
2009-12-09 03:05:14 +00:00
send_request ( req ) ;
}