2009-10-23 19:23:00 -04:00
/*
2019-08-02 22:01:25 -04:00
* Copyright ( C ) 1999 - 2017 Paul Davis < paul @ linuxaudiosystems . com >
* Copyright ( C ) 2009 - 2015 David Robillard < d @ drobilla . net >
* Copyright ( C ) 2010 - 2012 Carl Hetherington < carl @ carlh . net >
* Copyright ( C ) 2014 - 2017 Robin Gareus < robin @ gareus . org >
* Copyright ( C ) 2015 GZharun < grygoriiz @ wavesglobal . com >
*
* 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 . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*/
2009-10-23 19:23:00 -04:00
2009-10-23 20:39:28 -04:00
# include <errno.h>
# include <fcntl.h>
# include <unistd.h>
2013-07-11 14:41:08 -04:00
2024-04-29 15:46:06 -04:00
# ifdef HAVE_IOPRIO
# include <sys/syscall.h>
# endif
2013-08-04 14:03:32 -04:00
# ifndef PLATFORM_WINDOWS
2009-10-23 20:39:28 -04:00
# include <poll.h>
2013-07-11 14:41:08 -04:00
# endif
2009-10-23 20:39:28 -04:00
# include "pbd/error.h"
# include "pbd/pthread_utils.h"
2017-04-10 05:25:00 -04:00
2020-11-23 18:44:32 -05:00
# include "temporal/superclock.h"
2020-11-29 14:49:25 -05:00
# include "temporal/tempo.h"
2020-11-23 18:44:32 -05:00
2022-05-03 17:58:50 -04:00
# include "ardour/auditioner.h"
2009-10-23 19:23:00 -04:00
# include "ardour/butler.h"
2017-04-10 05:25:00 -04:00
# include "ardour/debug.h"
# include "ardour/disk_io.h"
# include "ardour/disk_reader.h"
2009-10-23 20:39:28 -04:00
# include "ardour/io.h"
2024-04-29 18:01:32 -04:00
# include "ardour/io_tasklist.h"
2009-10-23 20:39:28 -04:00
# include "ardour/session.h"
2010-04-21 16:42:22 -04:00
# include "ardour/track.h"
2009-10-23 20:39:28 -04:00
2016-07-14 14:44:52 -04:00
# include "pbd/i18n.h"
2009-10-23 20:39:28 -04:00
using namespace PBD ;
2022-05-03 17:58:50 -04:00
namespace ARDOUR
{
2009-10-23 19:23:00 -04:00
2022-05-03 17:58:50 -04:00
Butler : : Butler ( Session & s )
2009-12-17 13:24:23 -05:00
: SessionHandleRef ( s )
2022-05-03 17:58:50 -04:00
, thread ( )
2014-05-01 12:27:26 -04:00
, have_thread ( false )
2022-05-03 17:58:50 -04:00
, _audio_capture_buffer_size ( 0 )
, _audio_playback_buffer_size ( 0 )
, _midi_buffer_size ( 0 )
, pool_trash ( 16 )
2015-03-01 14:55:39 -05:00
, _xthread ( true )
2009-10-23 19:23:00 -04:00
{
2023-02-17 02:31:21 -05:00
should_do_transport_work . store ( 0 ) ;
2010-04-14 18:06:12 -04:00
SessionEvent : : pool - > set_trash ( & pool_trash ) ;
2010-06-09 13:24:07 -04:00
2021-03-19 01:12:11 -04:00
/* catch future changes to parameters */
Config - > ParameterChanged . connect_same_thread ( * this , boost : : bind ( & Butler : : config_changed , this , _1 ) ) ;
2009-10-23 19:23:00 -04:00
}
2022-05-03 17:58:50 -04:00
Butler : : ~ Butler ( )
2009-10-23 19:23:00 -04:00
{
2009-12-25 16:06:52 -05:00
terminate_thread ( ) ;
2009-10-23 19:23:00 -04:00
}
2015-01-26 15:54:29 -05:00
void
Butler : : map_parameters ( )
{
2022-05-03 17:58:50 -04:00
/* use any current ones that we care about */
boost : : function < void ( std : : string ) > ff ( boost : : bind ( & Butler : : config_changed , this , _1 ) ) ;
Config - > map_parameters ( ff ) ;
2015-01-26 15:54:29 -05:00
}
2010-06-09 13:24:07 -04:00
void
Butler : : config_changed ( std : : string p )
{
2015-03-05 18:48:33 -05:00
if ( p = = " playback-buffer-seconds " ) {
_session . adjust_playback_buffering ( ) ;
2022-05-03 17:58:50 -04:00
if ( Config - > get_buffering_preset ( ) = = Custom ) {
2015-05-12 14:15:07 -04:00
/* size is in Samples, not bytes */
2022-05-03 17:58:50 -04:00
samplecnt_t audio_playback_buffer_size = ( uint32_t ) floor ( Config - > get_audio_playback_buffer_seconds ( ) * _session . sample_rate ( ) ) ;
2021-05-12 13:10:10 -04:00
if ( _audio_playback_buffer_size ! = audio_playback_buffer_size ) {
_audio_playback_buffer_size = audio_playback_buffer_size ;
_session . adjust_playback_buffering ( ) ;
}
2015-05-12 14:15:07 -04:00
}
2015-03-05 18:48:33 -05:00
} else if ( p = = " capture-buffer-seconds " ) {
2022-05-03 17:58:50 -04:00
if ( Config - > get_buffering_preset ( ) = = Custom ) {
2021-05-12 13:10:10 -04:00
/* size is in Samples, not bytes */
2022-05-03 17:58:50 -04:00
samplecnt_t audio_capture_buffer_size = ( uint32_t ) floor ( Config - > get_audio_capture_buffer_seconds ( ) * _session . sample_rate ( ) ) ;
2021-05-12 13:10:10 -04:00
if ( _audio_capture_buffer_size ! = audio_capture_buffer_size ) {
_audio_capture_buffer_size = audio_capture_buffer_size ;
_session . adjust_capture_buffering ( ) ;
}
2015-05-12 14:15:07 -04:00
}
} else if ( p = = " buffering-preset " ) {
2022-05-03 17:58:50 -04:00
DiskIOProcessor : : set_buffering_parameters ( Config - > get_buffering_preset ( ) ) ;
samplecnt_t audio_capture_buffer_size = ( uint32_t ) floor ( Config - > get_audio_capture_buffer_seconds ( ) * _session . sample_rate ( ) ) ;
samplecnt_t audio_playback_buffer_size = ( uint32_t ) floor ( Config - > get_audio_playback_buffer_seconds ( ) * _session . sample_rate ( ) ) ;
2021-05-12 13:10:10 -04:00
if ( _audio_capture_buffer_size ! = audio_capture_buffer_size ) {
_audio_capture_buffer_size = audio_capture_buffer_size ;
_session . adjust_capture_buffering ( ) ;
}
if ( _audio_playback_buffer_size ! = audio_playback_buffer_size ) {
_audio_playback_buffer_size = audio_playback_buffer_size ;
_session . adjust_playback_buffering ( ) ;
}
2015-03-05 18:48:33 -05:00
}
2010-06-09 13:24:07 -04:00
}
2013-07-11 14:35:26 -04:00
int
2022-05-03 17:58:50 -04:00
Butler : : start_thread ( )
2013-07-11 14:35:26 -04:00
{
2015-05-12 23:02:16 -04:00
// set up capture and playback buffering
2022-05-03 17:58:50 -04:00
DiskIOProcessor : : set_buffering_parameters ( Config - > get_buffering_preset ( ) ) ;
2015-10-05 10:17:49 -04:00
2013-07-11 14:35:26 -04:00
/* size is in Samples, not bytes */
2022-05-03 17:58:50 -04:00
const float rate = ( float ) _session . sample_rate ( ) ;
_audio_capture_buffer_size = ( uint32_t ) floor ( Config - > get_audio_capture_buffer_seconds ( ) * rate ) ;
_audio_playback_buffer_size = ( uint32_t ) floor ( Config - > get_audio_playback_buffer_seconds ( ) * rate ) ;
2015-10-04 14:51:05 -04:00
2013-07-11 14:35:26 -04:00
/* size is in bytes
2015-05-12 23:02:16 -04:00
* XXX : AudioEngine needs to tell us the MIDI buffer size
2013-07-11 14:35:26 -04:00
* ( i . e . how many MIDI bytes we might see in a cycle )
*/
2022-05-03 17:58:50 -04:00
_midi_buffer_size = ( uint32_t ) floor ( Config - > get_midi_track_buffer_seconds ( ) * rate ) ;
2013-07-11 14:35:26 -04:00
should_run = false ;
2009-12-08 22:05:14 -05:00
if ( pthread_create_and_store ( " disk butler " , & thread , _thread_work , this ) ) {
2009-10-23 20:39:28 -04:00
error < < _ ( " Session: could not create butler thread " ) < < endmsg ;
return - 1 ;
}
//pthread_detach (thread);
2014-05-01 12:27:26 -04:00
have_thread = true ;
2015-10-04 14:51:05 -04:00
2015-05-12 23:02:16 -04:00
// we are ready to request buffer adjustments
_session . adjust_capture_buffering ( ) ;
_session . adjust_playback_buffering ( ) ;
2015-10-05 10:17:49 -04:00
2009-10-23 20:39:28 -04:00
return 0 ;
}
void
Butler : : terminate_thread ( )
{
2014-05-01 12:27:26 -04:00
if ( have_thread ) {
void * status ;
2022-05-03 17:58:50 -04:00
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " %1: ask butler to quit @ %2 \n " , DEBUG_THREAD_SELF , g_get_monotonic_time ( ) ) ) ;
2014-05-01 12:27:26 -04:00
queue_request ( Request : : Quit ) ;
pthread_join ( thread , & status ) ;
}
2009-10-23 20:39:28 -04:00
}
2022-05-03 17:58:50 -04:00
void *
2009-10-23 20:39:28 -04:00
Butler : : _thread_work ( void * arg )
{
2011-03-02 17:41:46 -05:00
SessionEvent : : create_per_thread_pool ( " butler events " , 4096 ) ;
2009-12-31 14:49:22 -05:00
pthread_set_name ( X_ ( " butler " ) ) ;
2024-04-29 21:46:39 -04:00
/* get thread buffers for RegionFx */
2024-04-29 19:54:33 -04:00
ARDOUR : : ProcessThread * pt = new ProcessThread ( ) ;
pt - > get_buffers ( ) ;
2024-04-29 21:46:39 -04:00
DiskReader : : allocate_working_buffers ( ) ;
2024-04-29 19:54:33 -04:00
void * rv = ( ( Butler * ) arg ) - > thread_work ( ) ;
2024-04-29 21:46:39 -04:00
DiskReader : : free_working_buffers ( ) ;
2024-04-29 19:54:33 -04:00
pt - > drop_buffers ( ) ;
delete pt ;
return rv ;
2009-10-23 20:39:28 -04:00
}
2022-05-03 17:58:50 -04:00
void *
2013-07-11 14:36:43 -04:00
Butler : : thread_work ( )
{
2022-05-03 17:58:50 -04:00
uint32_t err = 0 ;
bool disk_work_outstanding = false ;
2013-07-11 14:36:43 -04:00
RouteList : : iterator i ;
2009-10-23 20:39:28 -04:00
2024-04-29 15:46:06 -04:00
# ifdef HAVE_IOPRIO
// ioprio_set (IOPRIO_WHO_PROCESS, 0 /*calling thread*/, IOPRIO_PRIO_VALUE (IOPRIO_CLASS_RT, 4))
if ( 0 ! = syscall ( SYS_ioprio_set , 1 , 0 , ( 1 < < 13 ) | 4 ) ) {
2024-05-20 15:56:24 -04:00
info < < _ ( " Cannot set I/O Priority for disk read/write thread " ) < < endmsg ;
2024-04-29 15:46:06 -04:00
}
# endif
2013-07-11 14:36:43 -04:00
while ( true ) {
2022-05-03 17:58:50 -04:00
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " %1 butler main loop, disk work outstanding ? %2 @ %3 \n " , DEBUG_THREAD_SELF , disk_work_outstanding , g_get_monotonic_time ( ) ) ) ;
2014-12-09 10:18:29 -05:00
2022-05-03 17:58:50 -04:00
if ( ! disk_work_outstanding ) {
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " %1 butler waits for requests @ %2 \n " , DEBUG_THREAD_SELF , g_get_monotonic_time ( ) ) ) ;
2015-03-01 14:55:39 -05:00
char msg ;
/* empty the pipe of all current requests */
if ( _xthread . receive ( msg , true ) > = 0 ) {
2022-05-03 17:58:50 -04:00
Request : : Type req = ( Request : : Type ) msg ;
2015-03-01 14:55:39 -05:00
switch ( req ) {
2009-10-23 20:39:28 -04:00
case Request : : Run :
2022-05-03 17:58:50 -04:00
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " %1: butler asked to run @ %2 \n " , DEBUG_THREAD_SELF , g_get_monotonic_time ( ) ) ) ;
2009-10-23 20:39:28 -04:00
should_run = true ;
break ;
case Request : : Pause :
2022-05-03 17:58:50 -04:00
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " %1: butler asked to pause @ %2 \n " , DEBUG_THREAD_SELF , g_get_monotonic_time ( ) ) ) ;
2009-10-23 20:39:28 -04:00
should_run = false ;
break ;
case Request : : Quit :
2022-05-03 17:58:50 -04:00
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " %1: butler asked to quit @ %2 \n " , DEBUG_THREAD_SELF , g_get_monotonic_time ( ) ) ) ;
2013-08-15 11:44:47 -04:00
return 0 ;
2022-05-03 17:58:50 -04:00
abort ( ) ; /*NOTREACHED*/
2009-10-23 20:39:28 -04:00
break ;
default :
break ;
}
}
}
2020-11-29 14:49:25 -05:00
Temporal : : TempoMap : : fetch ( ) ;
2015-10-05 10:17:49 -04:00
2022-05-03 17:58:50 -04:00
restart :
2023-02-23 18:13:16 -05:00
std : : atomic_thread_fence ( std : : memory_order_acquire ) ;
2014-12-09 10:18:29 -05:00
DEBUG_TRACE ( DEBUG : : Butler , " at restart for disk work \n " ) ;
2010-07-04 21:13:03 -04:00
disk_work_outstanding = false ;
2011-06-01 12:50:12 -04:00
2022-05-03 17:58:50 -04:00
if ( transport_work_requested ( ) ) {
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " do transport work @ %1 \n " , g_get_monotonic_time ( ) ) ) ;
2010-07-04 21:13:03 -04:00
_session . butler_transport_work ( ) ;
2022-05-03 17:58:50 -04:00
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " \t transport work complete @ %1, twr = %2 \n " , g_get_monotonic_time ( ) , transport_work_requested ( ) ) ) ;
2020-01-23 16:25:01 -05:00
2022-05-03 17:58:50 -04:00
if ( _session . locate_initiated ( ) ) {
2020-01-23 16:25:01 -05:00
/* we have done the "stop" required for a
locate ( DeclickToLocate state in TFSM ) , but
once that finishes we ' re going to do a locate ,
so do not bother with buffer refills at this
time .
*/
2021-06-13 20:35:09 -04:00
Glib : : Threads : : Mutex : : Lock lm ( request_lock ) ;
2022-05-03 17:58:50 -04:00
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " \t locate pending, so just pause @ %1 till woken again \n " , g_get_monotonic_time ( ) ) ) ;
2020-01-23 16:25:01 -05:00
paused . signal ( ) ;
continue ;
}
2010-07-04 21:13:03 -04:00
}
2017-09-18 12:39:17 -04:00
sampleoffset_t audition_seek ;
2022-05-03 17:58:50 -04:00
if ( should_run & & _session . is_auditioning ( ) & & ( audition_seek = _session . the_auditioner ( ) - > seek_sample ( ) ) > = 0 ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Track > tr = std : : dynamic_pointer_cast < Track > ( _session . the_auditioner ( ) ) ;
2014-12-09 10:18:29 -05:00
DEBUG_TRACE ( DEBUG : : Butler , " seek the auditioner \n " ) ;
2022-05-03 17:58:50 -04:00
tr - > seek ( audition_seek ) ;
2015-08-22 10:37:08 -04:00
tr - > do_refill ( ) ;
2022-05-03 17:58:50 -04:00
_session . the_auditioner ( ) - > seek_response ( audition_seek ) ;
2014-01-16 18:20:58 -05:00
}
2023-04-07 17:33:13 -04:00
std : : shared_ptr < RouteList const > rl = _session . get_routes ( ) ;
2009-10-23 20:39:28 -04:00
2010-05-13 15:35:35 -04:00
RouteList rl_with_auditioner = * rl ;
2022-05-03 17:58:50 -04:00
rl_with_auditioner . push_back ( _session . the_auditioner ( ) ) ;
2010-05-13 15:35:35 -04:00
2022-05-03 17:58:50 -04:00
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " butler starts refill loop, twr = %1 \n " , transport_work_requested ( ) ) ) ;
2009-10-23 20:39:28 -04:00
2024-04-29 18:01:32 -04:00
std : : shared_ptr < IOTaskList > tl = _session . io_tasklist ( ) ;
2022-05-03 17:58:50 -04:00
for ( i = rl_with_auditioner . begin ( ) ; ! transport_work_requested ( ) & & should_run & & i ! = rl_with_auditioner . end ( ) ; + + i ) {
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Track > tr = std : : dynamic_pointer_cast < Track > ( * i ) ;
2011-07-19 07:57:09 -04:00
2010-04-21 16:42:22 -04:00
if ( ! tr ) {
continue ;
}
2009-10-23 20:39:28 -04:00
2023-02-16 18:33:28 -05:00
std : : shared_ptr < IO > io = tr - > input ( ) ;
2009-10-23 20:39:28 -04:00
2022-05-03 17:58:50 -04:00
if ( io & & ! io - > active ( ) ) {
2011-07-19 07:57:09 -04:00
/* don't read inactive tracks */
2017-07-04 12:11:35 -04:00
// DEBUG_TRACE (DEBUG::Butler, string_compose ("butler skips inactive track %1\n", tr->name()));
2009-10-23 20:39:28 -04:00
continue ;
}
2024-04-29 18:01:32 -04:00
tl - > push_back ( [ tr , & disk_work_outstanding ] ( ) {
switch ( tr - > do_refill ( ) ) {
case 0 :
//DEBUG_TRACE (DEBUG::Butler, string_compose ("\ttrack refill done %1\n", tr->name()));
break ;
case 1 :
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " \t track refill unfinished %1 \n " , tr - > name ( ) ) ) ;
disk_work_outstanding = true ;
break ;
default :
error < < string_compose ( _ ( " Butler read ahead failure on dstream %1 " ) , tr - > name ( ) ) < < endmsg ;
2024-05-07 18:56:57 -04:00
# ifndef NDEBUG
2024-04-29 18:01:32 -04:00
std : : cerr < < string_compose ( _ ( " Butler read ahead failure on dstream %1 " ) , tr - > name ( ) ) < < std : : endl ;
2024-05-07 18:56:57 -04:00
# endif
2024-04-29 18:01:32 -04:00
break ;
}
} ) ;
2009-10-23 20:39:28 -04:00
}
2024-04-29 18:01:32 -04:00
tl - > process ( ) ;
tl . reset ( ) ;
2022-05-03 17:58:50 -04:00
if ( i ! = rl_with_auditioner . begin ( ) & & i ! = rl_with_auditioner . end ( ) ) {
2009-10-23 20:39:28 -04:00
/* we didn't get to all the streams */
disk_work_outstanding = true ;
}
2022-05-03 17:58:50 -04:00
if ( ! err & & transport_work_requested ( ) ) {
2014-12-09 10:18:29 -05:00
DEBUG_TRACE ( DEBUG : : Butler , " transport work requested during refill, back to restart \n " ) ;
2010-07-04 21:13:03 -04:00
goto restart ;
2009-10-23 20:39:28 -04:00
}
2017-07-04 12:12:05 -04:00
disk_work_outstanding = disk_work_outstanding | | flush_tracks_to_disk_normal ( rl , err ) ;
2009-10-23 20:39:28 -04:00
2022-05-03 17:58:50 -04:00
if ( err & & _session . actively_recording ( ) ) {
2009-10-23 20:39:28 -04:00
/* stop the transport and try to catch as much possible
captured state as we can .
*/
2014-12-09 10:18:29 -05:00
DEBUG_TRACE ( DEBUG : : Butler , " error occurred during recording - stop transport \n " ) ;
2009-12-17 13:24:23 -05:00
_session . request_stop ( ) ;
2009-10-23 20:39:28 -04:00
}
2022-05-03 17:58:50 -04:00
if ( ! err & & transport_work_requested ( ) ) {
2014-12-09 10:18:29 -05:00
DEBUG_TRACE ( DEBUG : : Butler , " transport work requested during flush, back to restart \n " ) ;
2010-07-04 21:13:03 -04:00
goto restart ;
2009-10-23 20:39:28 -04:00
}
if ( ! disk_work_outstanding ) {
2009-12-17 13:24:23 -05:00
_session . refresh_disk_space ( ) ;
2009-10-23 20:39:28 -04:00
}
{
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock lm ( request_lock ) ;
2009-10-23 20:39:28 -04:00
2022-05-03 17:58:50 -04:00
if ( should_run & & ( disk_work_outstanding | | transport_work_requested ( ) ) ) {
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " at end, should run %1 disk work %2 transport work %3 ... goto restart \n " ,
should_run , disk_work_outstanding , transport_work_requested ( ) ) ) ;
2010-07-04 21:13:03 -04:00
goto restart ;
2009-10-23 20:39:28 -04:00
}
2022-05-03 17:58:50 -04:00
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " %1: butler signals pause @ %2 \n " , DEBUG_THREAD_SELF , g_get_monotonic_time ( ) ) ) ;
paused . signal ( ) ;
2009-10-23 20:39:28 -04:00
}
2010-04-14 18:06:12 -04:00
2014-12-09 10:18:29 -05:00
DEBUG_TRACE ( DEBUG : : Butler , " butler emptying pool trash \n " ) ;
2010-04-14 18:06:12 -04:00
empty_pool_trash ( ) ;
2022-05-03 20:10:46 -04:00
process_delegated_work ( ) ;
2009-10-23 20:39:28 -04:00
}
return ( 0 ) ;
}
2015-09-28 17:42:02 -04:00
bool
2023-04-07 17:33:13 -04:00
Butler : : flush_tracks_to_disk_normal ( std : : shared_ptr < RouteList const > rl , uint32_t & errors )
2015-09-28 17:42:02 -04:00
{
bool disk_work_outstanding = false ;
2023-04-07 17:33:13 -04:00
for ( RouteList : : const_iterator i = rl - > begin ( ) ; ! transport_work_requested ( ) & & should_run & & i ! = rl - > end ( ) ; + + i ) {
2015-09-30 12:58:36 -04:00
// cerr << "write behind for " << (*i)->name () << endl;
2015-10-05 10:17:49 -04:00
2023-02-16 18:33:28 -05:00
std : : shared_ptr < Track > tr = std : : dynamic_pointer_cast < Track > ( * i ) ;
2015-09-30 12:58:36 -04:00
if ( ! tr ) {
continue ;
}
/* note that we still try to flush diskstreams attached to inactive routes
*/
int ret ;
2017-07-04 12:12:19 -04:00
// DEBUG_TRACE (DEBUG::Butler, string_compose ("butler flushes track %1 capture load %2\n", tr->name(), tr->capture_buffer_load()));
2015-09-30 12:58:36 -04:00
ret = tr - > do_flush ( ButlerContext , false ) ;
switch ( ret ) {
2022-05-03 17:58:50 -04:00
case 0 :
//DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush complete for %1\n", tr->name()));
break ;
2015-09-30 12:58:36 -04:00
2022-05-03 17:58:50 -04:00
case 1 :
//DEBUG_TRACE (DEBUG::Butler, string_compose ("\tflush not finished for %1\n", tr->name()));
disk_work_outstanding = true ;
break ;
2015-09-30 12:58:36 -04:00
2022-05-03 17:58:50 -04:00
default :
errors + + ;
error < < string_compose ( _ ( " Butler write-behind failure on dstream %1 " ) , ( * i ) - > name ( ) ) < < endmsg ;
2024-05-07 18:56:57 -04:00
# ifndef NDEBUG
2022-05-03 17:58:50 -04:00
std : : cerr < < string_compose ( _ ( " Butler write-behind failure on dstream %1 " ) , ( * i ) - > name ( ) ) < < std : : endl ;
2024-05-07 18:56:57 -04:00
# endif
2022-05-03 17:58:50 -04:00
/* don't break - try to flush all streams in case they
2015-09-30 12:58:36 -04:00
are split across disks .
*/
}
}
return disk_work_outstanding ;
2015-10-05 10:17:49 -04:00
}
2015-09-30 12:58:36 -04:00
2009-10-23 20:39:28 -04:00
void
Butler : : schedule_transport_work ( )
{
2017-07-04 12:12:26 -04:00
DEBUG_TRACE ( DEBUG : : Butler , " requesting more transport work \n " ) ;
2023-02-17 02:31:21 -05:00
should_do_transport_work . fetch_add ( 1 ) ;
2009-10-23 20:39:28 -04:00
summon ( ) ;
}
void
2013-07-11 14:36:43 -04:00
Butler : : queue_request ( Request : : Type r )
2009-10-23 20:39:28 -04:00
{
2013-07-11 14:36:43 -04:00
char c = r ;
2015-03-02 17:13:19 -05:00
if ( _xthread . deliver ( c ) ! = 1 ) {
/* the x-thread channel is non-blocking
* write may fail , but we really don ' t want to wait
* under normal circumstances .
*
* a lost " run " requests under normal RT operation
* is mostly harmless .
*
* TODO if ardour is freehweeling , wait & retry .
* ditto for Request : : Type Quit
*/
2022-05-03 17:58:50 -04:00
assert ( 1 ) ; // we're screwd
2015-03-02 17:13:19 -05:00
}
2009-10-23 20:39:28 -04:00
}
2013-07-11 14:36:43 -04:00
void
Butler : : summon ( )
{
2022-05-03 17:58:50 -04:00
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " %1: summon butler to run @ %2 \n " , DEBUG_THREAD_SELF , g_get_monotonic_time ( ) ) ) ;
2013-07-11 14:36:43 -04:00
queue_request ( Request : : Run ) ;
}
2009-10-23 20:39:28 -04:00
void
Butler : : stop ( )
{
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock lm ( request_lock ) ;
2022-05-03 17:58:50 -04:00
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " %1: asking butler to stop @ %2 \n " , DEBUG_THREAD_SELF , g_get_monotonic_time ( ) ) ) ;
2013-07-11 14:36:43 -04:00
queue_request ( Request : : Pause ) ;
2022-05-03 17:58:50 -04:00
paused . wait ( request_lock ) ;
2009-10-23 20:39:28 -04:00
}
void
Butler : : wait_until_finished ( )
{
2012-07-25 13:48:55 -04:00
Glib : : Threads : : Mutex : : Lock lm ( request_lock ) ;
2022-05-03 17:58:50 -04:00
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " %1: waiting for butler to finish @ %2 \n " , DEBUG_THREAD_SELF , g_get_monotonic_time ( ) ) ) ;
2013-07-11 14:36:43 -04:00
queue_request ( Request : : Pause ) ;
2022-05-03 17:58:50 -04:00
paused . wait ( request_lock ) ;
2009-10-23 20:39:28 -04:00
}
bool
Butler : : transport_work_requested ( ) const
{
2023-02-17 02:31:21 -05:00
return should_do_transport_work . load ( ) ;
2009-10-23 20:39:28 -04:00
}
2010-04-14 18:06:12 -04:00
void
Butler : : empty_pool_trash ( )
{
/* look in the trash, deleting empty pools until we come to one that is not empty */
2011-06-01 12:50:12 -04:00
2010-04-14 18:06:12 -04:00
RingBuffer < CrossThreadPool * > : : rw_vector vec ;
pool_trash . get_read_vector ( & vec ) ;
guint deleted = 0 ;
2011-06-01 12:50:12 -04:00
2010-04-14 18:06:12 -04:00
for ( int i = 0 ; i < 2 ; + + i ) {
for ( guint j = 0 ; j < vec . len [ i ] ; + + j ) {
2022-05-03 17:58:50 -04:00
if ( vec . buf [ i ] [ j ] - > empty ( ) ) {
2010-04-14 18:06:12 -04:00
delete vec . buf [ i ] [ j ] ;
+ + deleted ;
} else {
/* found a non-empty pool, so stop deleting */
if ( deleted ) {
pool_trash . increment_read_idx ( deleted ) ;
}
return ;
}
}
}
if ( deleted ) {
pool_trash . increment_read_idx ( deleted ) ;
}
}
2022-05-03 20:10:46 -04:00
void
Butler : : process_delegated_work ( )
{
sigc : : slot < void > sl ;
while ( _delegated_work . pop_front ( sl ) ) {
sl ( ) ;
}
}
2010-04-14 19:58:20 -04:00
void
Butler : : drop_references ( )
{
SessionEvent : : pool - > set_trash ( 0 ) ;
2022-05-03 20:10:46 -04:00
process_delegated_work ( ) ;
2010-04-14 19:58:20 -04:00
}
2009-10-23 19:23:00 -04:00
} // namespace ARDOUR