2
* $Id: pa_unix_util.c 1232 2007-06-16 14:49:43Z rossb $
3
* Portable Audio I/O Library
4
* UNIX platform-specific support functions
6
* Based on the Open Source API proposed by Ross Bencina
7
* Copyright (c) 1999-2000 Ross Bencina
9
* Permission is hereby granted, free of charge, to any person obtaining
10
* a copy of this software and associated documentation files
11
* (the "Software"), to deal in the Software without restriction,
12
* including without limitation the rights to use, copy, modify, merge,
13
* publish, distribute, sublicense, and/or sell copies of the Software,
14
* and to permit persons to whom the Software is furnished to do so,
15
* subject to the following conditions:
17
* The above copyright notice and this permission notice shall be
18
* included in all copies or substantial portions of the Software.
20
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
24
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
25
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
* The text above constitutes the entire PortAudio license; however,
31
* the PortAudio community also makes the following non-binding requests:
33
* Any person wishing to distribute modifications to the Software is
34
* requested to send the modifications to the original developer so that
35
* they can be incorporated into the canonical version. It is also
36
* requested that these non-binding requests be included along with the
50
#include <string.h> /* For memset */
55
#include "pa_unix_util.h"
56
#include "pa_debugprint.h"
59
Track memory allocations to avoid leaks.
63
static int numAllocations_ = 0;
67
void *PaUtil_AllocateMemory( long size )
69
void *result = malloc( size );
72
if( result != NULL ) numAllocations_ += 1;
78
void PaUtil_FreeMemory( void *block )
91
int PaUtil_CountCurrentlyAllocatedBlocks( void )
94
return numAllocations_;
101
void Pa_Sleep( long msec )
103
#ifdef HAVE_NANOSLEEP
104
struct timespec req = {0}, rem = {0};
105
PaTime time = msec / 1.e3;
106
req.tv_sec = (time_t)time;
107
assert(time - req.tv_sec < 1.0);
108
req.tv_nsec = (long)((time - req.tv_sec) * 1.e9);
109
nanosleep(&req, &rem);
110
/* XXX: Try sleeping the remaining time (contained in rem) if interrupted by a signal? */
112
while( msec > 999 ) /* For OpenBSD and IRIX, argument */
113
{ /* to usleep must be < 1000000. */
117
usleep( msec * 1000 );
121
/* *** NOT USED YET: ***
122
static int usePerformanceCounter_;
123
static double microsecondsPerTick_;
126
void PaUtil_InitializeClock( void )
132
PaTime PaUtil_GetTime( void )
134
#ifdef HAVE_CLOCK_GETTIME
136
clock_gettime(CLOCK_REALTIME, &tp);
137
return (PaTime)(tp.tv_sec + tp.tv_nsec / 1.e9);
140
gettimeofday( &tv, NULL );
141
return (PaTime) tv.tv_usec / 1000000. + tv.tv_sec;
145
PaError PaUtil_InitializeThreading( PaUtilThreading *threading )
151
void PaUtil_TerminateThreading( PaUtilThreading *threading )
155
PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data )
157
pthread_create( &threading->callbackThread, NULL, threadRoutine, data );
161
PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult )
163
PaError result = paNoError;
167
*exitResult = paNoError;
169
/* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
171
pthread_cancel( threading->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */
172
pthread_join( threading->callbackThread, &pret );
174
#ifdef PTHREAD_CANCELED
175
if( pret && PTHREAD_CANCELED != pret )
177
/* !wait means the thread may have been canceled */
182
*exitResult = *(PaError *) pret;
191
* We have to be a bit careful with defining this global variable,
192
* as explained below. */
194
/* apple/gcc has a "problem" with global vars and dynamic libs.
195
Initializing it seems to fix the problem.
196
Described a bit in this thread:
197
http://gcc.gnu.org/ml/gcc/2005-06/msg00179.html
199
pthread_t paUnixMainThread = 0;
201
/*pthreads are opaque. We don't know that asigning it an int value
202
always makes sense, so we don't initialize it unless we have to.*/
203
pthread_t paUnixMainThread = 0;
206
PaError PaUnixThreading_Initialize()
208
paUnixMainThread = pthread_self();
212
static PaError BoostPriority( PaUnixThread* self )
214
PaError result = paNoError;
215
struct sched_param spm = { 0 };
216
/* Priority should only matter between contending FIFO threads? */
217
spm.sched_priority = 1;
221
if( pthread_setschedparam( self->thread, SCHED_FIFO, &spm ) != 0 )
223
PA_UNLESS( errno == EPERM, paInternalError ); /* Lack permission to raise priority */
224
PA_DEBUG(( "Failed bumping priority\n" ));
229
result = 1; /* Success */
235
PaError PaUnixThread_New( PaUnixThread* self, void* (*threadFunc)( void* ), void* threadArg, PaTime waitForChild,
238
PaError result = paNoError;
242
memset( self, 0, sizeof (PaUnixThread) );
243
PaUnixMutex_Initialize( &self->mtx );
244
PA_ASSERT_CALL( pthread_cond_init( &self->cond, NULL ), 0 );
246
self->parentWaiting = 0 != waitForChild;
250
/* Temporarily disabled since we should test during configuration for presence of required mman.h header */
252
#if defined _POSIX_MEMLOCK && (_POSIX_MEMLOCK != -1)
255
if( mlockall( MCL_CURRENT | MCL_FUTURE ) < 0 )
257
int savedErrno = errno; /* In case errno gets overwritten */
258
assert( savedErrno != EINVAL ); /* Most likely a programmer error */
259
PA_UNLESS( (savedErrno == EPERM), paInternalError );
260
PA_DEBUG(( "%s: Failed locking memory\n", __FUNCTION__ ));
263
PA_DEBUG(( "%s: Successfully locked memory\n", __FUNCTION__ ));
268
PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
269
/* Priority relative to other processes */
270
PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
272
PA_UNLESS( !pthread_create( &self->thread, &attr, threadFunc, threadArg ), paInternalError );
278
if( self->useWatchdog )
281
struct sched_param wdSpm = { 0 };
282
/* Launch watchdog, watchdog sets callback thread priority */
283
int prio = PA_MIN( self->rtPrio + 4, sched_get_priority_max( SCHED_FIFO ) );
284
wdSpm.sched_priority = prio;
286
PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
287
PA_UNLESS( !pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ), paInternalError );
288
PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
289
PA_UNLESS( !pthread_attr_setschedpolicy( &attr, SCHED_FIFO ), paInternalError );
290
PA_UNLESS( !pthread_attr_setschedparam( &attr, &wdSpm ), paInternalError );
291
if( (err = pthread_create( &self->watchdogThread, &attr, &WatchdogFunc, self )) )
293
PA_UNLESS( err == EPERM, paInternalError );
294
/* Permission error, go on without realtime privileges */
295
PA_DEBUG(( "Failed bumping priority\n" ));
300
self->watchdogRunning = 1;
301
PA_ENSURE_SYSTEM( pthread_getschedparam( self->watchdogThread, &policy, &wdSpm ), 0 );
302
/* Check if priority is right, policy could potentially differ from SCHED_FIFO (but that's alright) */
303
if( wdSpm.sched_priority != prio )
305
PA_DEBUG(( "Watchdog priority not set correctly (%d)\n", wdSpm.sched_priority ));
306
PA_ENSURE( paInternalError );
312
PA_ENSURE( BoostPriority( self ) );
316
struct sched_param spm;
317
pthread_getschedparam(self->thread, &policy, &spm);
321
if( self->parentWaiting )
328
PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) );
330
/* Wait for stream to be started */
331
now = PaUtil_GetTime();
332
till = now + waitForChild;
334
while( self->parentWaiting && !res )
336
if( waitForChild > 0 )
338
ts.tv_sec = (time_t) floor( till );
339
ts.tv_nsec = (long) ((till - floor( till )) * 1e9);
340
res = pthread_cond_timedwait( &self->cond, &self->mtx.mtx, &ts );
344
res = pthread_cond_wait( &self->cond, &self->mtx.mtx );
348
PA_ENSURE( PaUnixMutex_Unlock( &self->mtx ) );
350
PA_UNLESS( !res || ETIMEDOUT == res, paInternalError );
351
PA_DEBUG(( "%s: Waited for %g seconds for stream to start\n", __FUNCTION__, PaUtil_GetTime() - now ));
352
if( ETIMEDOUT == res )
354
PA_ENSURE( paTimedOut );
363
PaUnixThread_Terminate( self, 0, NULL );
369
PaError PaUnixThread_Terminate( PaUnixThread* self, int wait, PaError* exitResult )
371
PaError result = paNoError;
376
*exitResult = paNoError;
379
if( watchdogExitResult )
380
*watchdogExitResult = paNoError;
382
if( th->watchdogRunning )
384
pthread_cancel( th->watchdogThread );
385
PA_ENSURE_SYSTEM( pthread_join( th->watchdogThread, &pret ), 0 );
387
if( pret && pret != PTHREAD_CANCELED )
389
if( watchdogExitResult )
390
*watchdogExitResult = *(PaError *) pret;
396
/* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
397
/* TODO: Make join time out */
398
self->stopRequested = wait;
401
PA_DEBUG(( "%s: Canceling thread %d\n", __FUNCTION__, self->thread ));
402
/* XXX: Safe to call this if the thread has exited on its own? */
403
pthread_cancel( self->thread );
405
PA_DEBUG(( "%s: Joining thread %d\n", __FUNCTION__, self->thread ));
406
PA_ENSURE_SYSTEM( pthread_join( self->thread, &pret ), 0 );
408
if( pret && PTHREAD_CANCELED != pret )
412
*exitResult = *(PaError*)pret;
418
PA_ASSERT_CALL( PaUnixMutex_Terminate( &self->mtx ), paNoError );
419
PA_ASSERT_CALL( pthread_cond_destroy( &self->cond ), 0 );
424
PaError PaUnixThread_PrepareNotify( PaUnixThread* self )
426
PaError result = paNoError;
427
PA_UNLESS( self->parentWaiting, paInternalError );
429
PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) );
436
PaError PaUnixThread_NotifyParent( PaUnixThread* self )
438
PaError result = paNoError;
439
PA_UNLESS( self->parentWaiting, paInternalError );
443
PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) );
446
self->parentWaiting = 0;
447
pthread_cond_signal( &self->cond );
448
PA_ENSURE( PaUnixMutex_Unlock( &self->mtx ) );
455
int PaUnixThread_StopRequested( PaUnixThread* self )
457
return self->stopRequested;
460
PaError PaUnixMutex_Initialize( PaUnixMutex* self )
462
PaError result = paNoError;
463
PA_ASSERT_CALL( pthread_mutex_init( &self->mtx, NULL ), 0 );
467
PaError PaUnixMutex_Terminate( PaUnixMutex* self )
469
PaError result = paNoError;
470
PA_ASSERT_CALL( pthread_mutex_destroy( &self->mtx ), 0 );
476
* We're disabling thread cancellation while the thread is holding a lock, so mutexes are
477
* properly unlocked at termination time.
479
PaError PaUnixMutex_Lock( PaUnixMutex* self )
481
PaError result = paNoError;
484
PA_ENSURE_SYSTEM( pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldState ), 0 );
485
PA_ENSURE_SYSTEM( pthread_mutex_lock( &self->mtx ), 0 );
493
* Thread cancellation is enabled again after the mutex is properly unlocked.
495
PaError PaUnixMutex_Unlock( PaUnixMutex* self )
497
PaError result = paNoError;
500
PA_ENSURE_SYSTEM( pthread_mutex_unlock( &self->mtx ), 0 );
501
PA_ENSURE_SYSTEM( pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldState ), 0 );
509
static void OnWatchdogExit( void *userData )
511
PaAlsaThreading *th = (PaAlsaThreading *) userData;
512
struct sched_param spm = { 0 };
515
PA_ASSERT_CALL( pthread_setschedparam( th->callbackThread, SCHED_OTHER, &spm ), 0 ); /* Lower before exiting */
516
PA_DEBUG(( "Watchdog exiting\n" ));
519
static void *WatchdogFunc( void *userData )
521
PaError result = paNoError, *pres = NULL;
523
PaAlsaThreading *th = (PaAlsaThreading *) userData;
524
unsigned intervalMsec = 500;
525
const PaTime maxSeconds = 3.; /* Max seconds between callbacks */
526
PaTime timeThen = PaUtil_GetTime(), timeNow, timeElapsed, cpuTimeThen, cpuTimeNow, cpuTimeElapsed;
527
double cpuLoad, avgCpuLoad = 0.;
532
/* Execute OnWatchdogExit when exiting */
533
pthread_cleanup_push( &OnWatchdogExit, th );
535
/* Boost priority of callback thread */
536
PA_ENSURE( result = BoostPriority( th ) );
539
/* Boost failed, might as well exit */
540
pthread_exit( NULL );
543
cpuTimeThen = th->callbackCpuTime;
546
struct sched_param spm = { 0 };
547
pthread_getschedparam( pthread_self(), &policy, &spm );
548
PA_DEBUG(( "%s: Watchdog priority is %d\n", __FUNCTION__, spm.sched_priority ));
553
double lowpassCoeff = 0.9, lowpassCoeff1 = 0.99999 - lowpassCoeff;
555
/* Test before and after in case whatever underlying sleep call isn't interrupted by pthread_cancel */
556
pthread_testcancel();
557
Pa_Sleep( intervalMsec );
558
pthread_testcancel();
560
if( PaUtil_GetTime() - th->callbackTime > maxSeconds )
562
PA_DEBUG(( "Watchdog: Terminating callback thread\n" ));
563
/* Tell thread to terminate */
564
err = pthread_kill( th->callbackThread, SIGKILL );
565
pthread_exit( NULL );
568
PA_DEBUG(( "%s: PortAudio reports CPU load: %g\n", __FUNCTION__, PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) ));
570
/* Check if we should throttle, or unthrottle :P */
571
cpuTimeNow = th->callbackCpuTime;
572
cpuTimeElapsed = cpuTimeNow - cpuTimeThen;
573
cpuTimeThen = cpuTimeNow;
575
timeNow = PaUtil_GetTime();
576
timeElapsed = timeNow - timeThen;
578
cpuLoad = cpuTimeElapsed / timeElapsed;
579
avgCpuLoad = avgCpuLoad * lowpassCoeff + cpuLoad * lowpassCoeff1;
582
PA_DEBUG(( "Watchdog: CPU load: %g, %g\n", avgCpuLoad, cpuTimeElapsed ));
584
if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) > .925 )
587
static struct sched_param spm = { 0 };
588
static const struct sched_param defaultSpm = { 0 };
589
PA_DEBUG(( "%s: Throttling audio thread, priority %d\n", __FUNCTION__, spm.sched_priority ));
591
pthread_getschedparam( th->callbackThread, &policy, &spm );
592
if( !pthread_setschedparam( th->callbackThread, SCHED_OTHER, &defaultSpm ) )
597
PA_DEBUG(( "Watchdog: Couldn't lower priority of audio thread: %s\n", strerror( errno ) ));
599
/* Give other processes a go, before raising priority again */
600
PA_DEBUG(( "%s: Watchdog sleeping for %lu msecs before unthrottling\n", __FUNCTION__, th->throttledSleepTime ));
601
Pa_Sleep( th->throttledSleepTime );
603
/* Reset callback priority */
604
if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
606
PA_DEBUG(( "%s: Couldn't raise priority of audio thread: %s\n", __FUNCTION__, strerror( errno ) ));
609
if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) >= .99 )
616
lowpassCoeff1 = .99999 - lowpassCoeff;
619
else if( throttled && avgCpuLoad < .8 )
626
lowpassCoeff1 = .99999 - lowpassCoeff;
631
pthread_cleanup_pop( 1 ); /* Execute cleanup on exit */
634
/* Shouldn't get here in the normal case */
636
/* Pass on error code */
637
pres = malloc( sizeof (PaError) );
640
pthread_exit( pres );
643
static void CallbackUpdate( PaAlsaThreading *th )
645
th->callbackTime = PaUtil_GetTime();
646
th->callbackCpuTime = PaUtil_GetCpuLoad( th->cpuLoadMeasurer );
650
static void *CanaryFunc( void *userData )
652
const unsigned intervalMsec = 1000;
653
PaUtilThreading *th = (PaUtilThreading *) userData;
657
th->canaryTime = PaUtil_GetTime();
659
pthread_testcancel();
660
Pa_Sleep( intervalMsec );
663
pthread_exit( NULL );