2
* $Id: pa_unix_util.c 1419 2009-10-22 17:28:35Z bjornroche $
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 */
54
#if defined(__APPLE__) && !defined(HAVE_MACH_ABSOLUTE_TIME)
55
#define HAVE_MACH_ABSOLUTE_TIME
57
#ifdef HAVE_MACH_ABSOLUTE_TIME
58
#include <mach/mach_time.h>
62
#include "pa_unix_util.h"
63
#include "pa_debugprint.h"
66
Track memory allocations to avoid leaks.
70
static int numAllocations_ = 0;
74
void *PaUtil_AllocateMemory( long size )
76
void *result = malloc( size );
79
if( result != NULL ) numAllocations_ += 1;
85
void PaUtil_FreeMemory( void *block )
98
int PaUtil_CountCurrentlyAllocatedBlocks( void )
101
return numAllocations_;
108
void Pa_Sleep( long msec )
110
#ifdef HAVE_NANOSLEEP
111
struct timespec req = {0}, rem = {0};
112
PaTime time = msec / 1.e3;
113
req.tv_sec = (time_t)time;
114
assert(time - req.tv_sec < 1.0);
115
req.tv_nsec = (long)((time - req.tv_sec) * 1.e9);
116
nanosleep(&req, &rem);
117
/* XXX: Try sleeping the remaining time (contained in rem) if interrupted by a signal? */
119
while( msec > 999 ) /* For OpenBSD and IRIX, argument */
120
{ /* to usleep must be < 1000000. */
124
usleep( msec * 1000 );
128
#ifdef HAVE_MACH_ABSOLUTE_TIME
130
Discussion on the CoreAudio mailing list suggests that calling
131
gettimeofday (or anything else in the BSD layer) is not real-time
132
safe, so we use mach_absolute_time on OSX. This implementation is
133
based on these two links:
135
Technical Q&A QA1398 - Mach Absolute Time Units
136
http://developer.apple.com/mac/library/qa/qa2004/qa1398.html
138
Tutorial: Performance and Time.
139
http://www.macresearch.org/tutorial_performance_and_time
142
/* Scaler to convert the result of mach_absolute_time to seconds */
143
static double machSecondsConversionScaler_ = 0.0;
146
void PaUtil_InitializeClock( void )
148
#ifdef HAVE_MACH_ABSOLUTE_TIME
149
mach_timebase_info_data_t info;
150
kern_return_t err = mach_timebase_info( &info );
152
machSecondsConversionScaler_ = 1e-9 * (double) info.numer / (double) info.denom;
157
PaTime PaUtil_GetTime( void )
159
#ifdef HAVE_MACH_ABSOLUTE_TIME
160
return mach_absolute_time() * machSecondsConversionScaler_;
161
#elif defined(HAVE_CLOCK_GETTIME)
163
clock_gettime(CLOCK_REALTIME, &tp);
164
return (PaTime)(tp.tv_sec + tp.tv_nsec * 1e-9);
167
gettimeofday( &tv, NULL );
168
return (PaTime) tv.tv_usec * 1e-6 + tv.tv_sec;
172
PaError PaUtil_InitializeThreading( PaUtilThreading *threading )
178
void PaUtil_TerminateThreading( PaUtilThreading *threading )
182
PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data )
184
pthread_create( &threading->callbackThread, NULL, threadRoutine, data );
188
PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult )
190
PaError result = paNoError;
194
*exitResult = paNoError;
196
/* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
198
pthread_cancel( threading->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */
199
pthread_join( threading->callbackThread, &pret );
201
#ifdef PTHREAD_CANCELED
202
if( pret && PTHREAD_CANCELED != pret )
204
/* !wait means the thread may have been canceled */
209
*exitResult = *(PaError *) pret;
218
* We have to be a bit careful with defining this global variable,
219
* as explained below. */
221
/* apple/gcc has a "problem" with global vars and dynamic libs.
222
Initializing it seems to fix the problem.
223
Described a bit in this thread:
224
http://gcc.gnu.org/ml/gcc/2005-06/msg00179.html
226
pthread_t paUnixMainThread = 0;
228
/*pthreads are opaque. We don't know that asigning it an int value
229
always makes sense, so we don't initialize it unless we have to.*/
230
pthread_t paUnixMainThread = 0;
233
PaError PaUnixThreading_Initialize()
235
paUnixMainThread = pthread_self();
239
static PaError BoostPriority( PaUnixThread* self )
241
PaError result = paNoError;
242
struct sched_param spm = { 0 };
243
/* Priority should only matter between contending FIFO threads? */
244
spm.sched_priority = 1;
248
if( pthread_setschedparam( self->thread, SCHED_FIFO, &spm ) != 0 )
250
PA_UNLESS( errno == EPERM, paInternalError ); /* Lack permission to raise priority */
251
PA_DEBUG(( "Failed bumping priority\n" ));
256
result = 1; /* Success */
262
PaError PaUnixThread_New( PaUnixThread* self, void* (*threadFunc)( void* ), void* threadArg, PaTime waitForChild,
265
PaError result = paNoError;
269
memset( self, 0, sizeof (PaUnixThread) );
270
PaUnixMutex_Initialize( &self->mtx );
271
PA_ASSERT_CALL( pthread_cond_init( &self->cond, NULL ), 0 );
273
self->parentWaiting = 0 != waitForChild;
277
/* Temporarily disabled since we should test during configuration for presence of required mman.h header */
279
#if defined _POSIX_MEMLOCK && (_POSIX_MEMLOCK != -1)
282
if( mlockall( MCL_CURRENT | MCL_FUTURE ) < 0 )
284
int savedErrno = errno; /* In case errno gets overwritten */
285
assert( savedErrno != EINVAL ); /* Most likely a programmer error */
286
PA_UNLESS( (savedErrno == EPERM), paInternalError );
287
PA_DEBUG(( "%s: Failed locking memory\n", __FUNCTION__ ));
290
PA_DEBUG(( "%s: Successfully locked memory\n", __FUNCTION__ ));
295
PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
296
/* Priority relative to other processes */
297
PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
299
PA_UNLESS( !pthread_create( &self->thread, &attr, threadFunc, threadArg ), paInternalError );
305
if( self->useWatchdog )
308
struct sched_param wdSpm = { 0 };
309
/* Launch watchdog, watchdog sets callback thread priority */
310
int prio = PA_MIN( self->rtPrio + 4, sched_get_priority_max( SCHED_FIFO ) );
311
wdSpm.sched_priority = prio;
313
PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
314
PA_UNLESS( !pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ), paInternalError );
315
PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
316
PA_UNLESS( !pthread_attr_setschedpolicy( &attr, SCHED_FIFO ), paInternalError );
317
PA_UNLESS( !pthread_attr_setschedparam( &attr, &wdSpm ), paInternalError );
318
if( (err = pthread_create( &self->watchdogThread, &attr, &WatchdogFunc, self )) )
320
PA_UNLESS( err == EPERM, paInternalError );
321
/* Permission error, go on without realtime privileges */
322
PA_DEBUG(( "Failed bumping priority\n" ));
327
self->watchdogRunning = 1;
328
PA_ENSURE_SYSTEM( pthread_getschedparam( self->watchdogThread, &policy, &wdSpm ), 0 );
329
/* Check if priority is right, policy could potentially differ from SCHED_FIFO (but that's alright) */
330
if( wdSpm.sched_priority != prio )
332
PA_DEBUG(( "Watchdog priority not set correctly (%d)\n", wdSpm.sched_priority ));
333
PA_ENSURE( paInternalError );
339
PA_ENSURE( BoostPriority( self ) );
343
struct sched_param spm;
344
pthread_getschedparam(self->thread, &policy, &spm);
348
if( self->parentWaiting )
355
PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) );
357
/* Wait for stream to be started */
358
now = PaUtil_GetTime();
359
till = now + waitForChild;
361
while( self->parentWaiting && !res )
363
if( waitForChild > 0 )
365
ts.tv_sec = (time_t) floor( till );
366
ts.tv_nsec = (long) ((till - floor( till )) * 1e9);
367
res = pthread_cond_timedwait( &self->cond, &self->mtx.mtx, &ts );
371
res = pthread_cond_wait( &self->cond, &self->mtx.mtx );
375
PA_ENSURE( PaUnixMutex_Unlock( &self->mtx ) );
377
PA_UNLESS( !res || ETIMEDOUT == res, paInternalError );
378
PA_DEBUG(( "%s: Waited for %g seconds for stream to start\n", __FUNCTION__, PaUtil_GetTime() - now ));
379
if( ETIMEDOUT == res )
381
PA_ENSURE( paTimedOut );
390
PaUnixThread_Terminate( self, 0, NULL );
396
PaError PaUnixThread_Terminate( PaUnixThread* self, int wait, PaError* exitResult )
398
PaError result = paNoError;
403
*exitResult = paNoError;
406
if( watchdogExitResult )
407
*watchdogExitResult = paNoError;
409
if( th->watchdogRunning )
411
pthread_cancel( th->watchdogThread );
412
PA_ENSURE_SYSTEM( pthread_join( th->watchdogThread, &pret ), 0 );
414
if( pret && pret != PTHREAD_CANCELED )
416
if( watchdogExitResult )
417
*watchdogExitResult = *(PaError *) pret;
423
/* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
424
/* TODO: Make join time out */
425
self->stopRequested = wait;
428
PA_DEBUG(( "%s: Canceling thread %d\n", __FUNCTION__, self->thread ));
429
/* XXX: Safe to call this if the thread has exited on its own? */
430
pthread_cancel( self->thread );
432
PA_DEBUG(( "%s: Joining thread %d\n", __FUNCTION__, self->thread ));
433
PA_ENSURE_SYSTEM( pthread_join( self->thread, &pret ), 0 );
435
if( pret && PTHREAD_CANCELED != pret )
439
*exitResult = *(PaError*)pret;
445
PA_ASSERT_CALL( PaUnixMutex_Terminate( &self->mtx ), paNoError );
446
PA_ASSERT_CALL( pthread_cond_destroy( &self->cond ), 0 );
451
PaError PaUnixThread_PrepareNotify( PaUnixThread* self )
453
PaError result = paNoError;
454
PA_UNLESS( self->parentWaiting, paInternalError );
456
PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) );
463
PaError PaUnixThread_NotifyParent( PaUnixThread* self )
465
PaError result = paNoError;
466
PA_UNLESS( self->parentWaiting, paInternalError );
470
PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) );
473
self->parentWaiting = 0;
474
pthread_cond_signal( &self->cond );
475
PA_ENSURE( PaUnixMutex_Unlock( &self->mtx ) );
482
int PaUnixThread_StopRequested( PaUnixThread* self )
484
return self->stopRequested;
487
PaError PaUnixMutex_Initialize( PaUnixMutex* self )
489
PaError result = paNoError;
490
PA_ASSERT_CALL( pthread_mutex_init( &self->mtx, NULL ), 0 );
494
PaError PaUnixMutex_Terminate( PaUnixMutex* self )
496
PaError result = paNoError;
497
PA_ASSERT_CALL( pthread_mutex_destroy( &self->mtx ), 0 );
503
* We're disabling thread cancellation while the thread is holding a lock, so mutexes are
504
* properly unlocked at termination time.
506
PaError PaUnixMutex_Lock( PaUnixMutex* self )
508
PaError result = paNoError;
511
PA_ENSURE_SYSTEM( pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldState ), 0 );
512
PA_ENSURE_SYSTEM( pthread_mutex_lock( &self->mtx ), 0 );
520
* Thread cancellation is enabled again after the mutex is properly unlocked.
522
PaError PaUnixMutex_Unlock( PaUnixMutex* self )
524
PaError result = paNoError;
527
PA_ENSURE_SYSTEM( pthread_mutex_unlock( &self->mtx ), 0 );
528
PA_ENSURE_SYSTEM( pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldState ), 0 );
536
static void OnWatchdogExit( void *userData )
538
PaAlsaThreading *th = (PaAlsaThreading *) userData;
539
struct sched_param spm = { 0 };
542
PA_ASSERT_CALL( pthread_setschedparam( th->callbackThread, SCHED_OTHER, &spm ), 0 ); /* Lower before exiting */
543
PA_DEBUG(( "Watchdog exiting\n" ));
546
static void *WatchdogFunc( void *userData )
548
PaError result = paNoError, *pres = NULL;
550
PaAlsaThreading *th = (PaAlsaThreading *) userData;
551
unsigned intervalMsec = 500;
552
const PaTime maxSeconds = 3.; /* Max seconds between callbacks */
553
PaTime timeThen = PaUtil_GetTime(), timeNow, timeElapsed, cpuTimeThen, cpuTimeNow, cpuTimeElapsed;
554
double cpuLoad, avgCpuLoad = 0.;
559
/* Execute OnWatchdogExit when exiting */
560
pthread_cleanup_push( &OnWatchdogExit, th );
562
/* Boost priority of callback thread */
563
PA_ENSURE( result = BoostPriority( th ) );
566
/* Boost failed, might as well exit */
567
pthread_exit( NULL );
570
cpuTimeThen = th->callbackCpuTime;
573
struct sched_param spm = { 0 };
574
pthread_getschedparam( pthread_self(), &policy, &spm );
575
PA_DEBUG(( "%s: Watchdog priority is %d\n", __FUNCTION__, spm.sched_priority ));
580
double lowpassCoeff = 0.9, lowpassCoeff1 = 0.99999 - lowpassCoeff;
582
/* Test before and after in case whatever underlying sleep call isn't interrupted by pthread_cancel */
583
pthread_testcancel();
584
Pa_Sleep( intervalMsec );
585
pthread_testcancel();
587
if( PaUtil_GetTime() - th->callbackTime > maxSeconds )
589
PA_DEBUG(( "Watchdog: Terminating callback thread\n" ));
590
/* Tell thread to terminate */
591
err = pthread_kill( th->callbackThread, SIGKILL );
592
pthread_exit( NULL );
595
PA_DEBUG(( "%s: PortAudio reports CPU load: %g\n", __FUNCTION__, PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) ));
597
/* Check if we should throttle, or unthrottle :P */
598
cpuTimeNow = th->callbackCpuTime;
599
cpuTimeElapsed = cpuTimeNow - cpuTimeThen;
600
cpuTimeThen = cpuTimeNow;
602
timeNow = PaUtil_GetTime();
603
timeElapsed = timeNow - timeThen;
605
cpuLoad = cpuTimeElapsed / timeElapsed;
606
avgCpuLoad = avgCpuLoad * lowpassCoeff + cpuLoad * lowpassCoeff1;
609
PA_DEBUG(( "Watchdog: CPU load: %g, %g\n", avgCpuLoad, cpuTimeElapsed ));
611
if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) > .925 )
614
static struct sched_param spm = { 0 };
615
static const struct sched_param defaultSpm = { 0 };
616
PA_DEBUG(( "%s: Throttling audio thread, priority %d\n", __FUNCTION__, spm.sched_priority ));
618
pthread_getschedparam( th->callbackThread, &policy, &spm );
619
if( !pthread_setschedparam( th->callbackThread, SCHED_OTHER, &defaultSpm ) )
624
PA_DEBUG(( "Watchdog: Couldn't lower priority of audio thread: %s\n", strerror( errno ) ));
626
/* Give other processes a go, before raising priority again */
627
PA_DEBUG(( "%s: Watchdog sleeping for %lu msecs before unthrottling\n", __FUNCTION__, th->throttledSleepTime ));
628
Pa_Sleep( th->throttledSleepTime );
630
/* Reset callback priority */
631
if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
633
PA_DEBUG(( "%s: Couldn't raise priority of audio thread: %s\n", __FUNCTION__, strerror( errno ) ));
636
if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) >= .99 )
643
lowpassCoeff1 = .99999 - lowpassCoeff;
646
else if( throttled && avgCpuLoad < .8 )
653
lowpassCoeff1 = .99999 - lowpassCoeff;
658
pthread_cleanup_pop( 1 ); /* Execute cleanup on exit */
661
/* Shouldn't get here in the normal case */
663
/* Pass on error code */
664
pres = malloc( sizeof (PaError) );
667
pthread_exit( pres );
670
static void CallbackUpdate( PaAlsaThreading *th )
672
th->callbackTime = PaUtil_GetTime();
673
th->callbackCpuTime = PaUtil_GetCpuLoad( th->cpuLoadMeasurer );
677
static void *CanaryFunc( void *userData )
679
const unsigned intervalMsec = 1000;
680
PaUtilThreading *th = (PaUtilThreading *) userData;
684
th->canaryTime = PaUtil_GetTime();
686
pthread_testcancel();
687
Pa_Sleep( intervalMsec );
690
pthread_exit( NULL );