1 /*
2  * $Id$
3  * Portable Audio I/O Library
4  * UNIX platform-specific support functions
5  *
6  * Based on the Open Source API proposed by Ross Bencina
7  * Copyright (c) 1999-2000 Ross Bencina
8  *
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:
16  *
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  *
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.
27  */
28 
29 /*
30  * The text above constitutes the entire PortAudio license; however,
31  * the PortAudio community also makes the following non-binding requests:
32  *
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
37  * license above.
38  */
39 
40 /** @file
41  @ingroup unix_src
42 */
43 
44 #include <pthread.h>
45 #include <unistd.h>
46 #include <stdlib.h>
47 #include <time.h>
48 #include <sys/time.h>
49 #include <assert.h>
50 #include <string.h> /* For memset */
51 #include <math.h>
52 #include <errno.h>
53 
54 #if defined(__APPLE__) && !defined(HAVE_MACH_ABSOLUTE_TIME)
55 #define HAVE_MACH_ABSOLUTE_TIME
56 #endif
57 #ifdef HAVE_MACH_ABSOLUTE_TIME
58 #include <mach/mach_time.h>
59 #endif
60 
61 #include "pa_util.h"
62 #include "pa_unix_util.h"
63 #include "pa_debugprint.h"
64 
65 /*
66    Track memory allocations to avoid leaks.
67  */
68 
69 #if PA_TRACK_MEMORY
70 static int numAllocations_ = 0;
71 #endif
72 
73 
PaUtil_AllocateMemory(long size)74 void *PaUtil_AllocateMemory( long size )
75 {
76     void *result = malloc( size );
77 
78 #if PA_TRACK_MEMORY
79     if( result != NULL ) numAllocations_ += 1;
80 #endif
81     return result;
82 }
83 
84 
PaUtil_FreeMemory(void * block)85 void PaUtil_FreeMemory( void *block )
86 {
87     if( block != NULL )
88     {
89         free( block );
90 #if PA_TRACK_MEMORY
91         numAllocations_ -= 1;
92 #endif
93 
94     }
95 }
96 
97 
PaUtil_CountCurrentlyAllocatedBlocks(void)98 int PaUtil_CountCurrentlyAllocatedBlocks( void )
99 {
100 #if PA_TRACK_MEMORY
101     return numAllocations_;
102 #else
103     return 0;
104 #endif
105 }
106 
107 
Pa_Sleep(long msec)108 void Pa_Sleep( long msec )
109 {
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? */
118 #else
119     while( msec > 999 )     /* For OpenBSD and IRIX, argument */
120         {                   /* to usleep must be < 1000000.   */
121         usleep( 999000 );
122         msec -= 999;
123         }
124     usleep( msec * 1000 );
125 #endif
126 }
127 
128 #ifdef HAVE_MACH_ABSOLUTE_TIME
129 /*
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:
134 
135     Technical Q&A QA1398 - Mach Absolute Time Units
136     http://developer.apple.com/mac/library/qa/qa2004/qa1398.html
137 
138     Tutorial: Performance and Time.
139     http://www.macresearch.org/tutorial_performance_and_time
140 */
141 
142 /* Scaler to convert the result of mach_absolute_time to seconds */
143 static double machSecondsConversionScaler_ = 0.0;
144 #endif
145 
PaUtil_InitializeClock(void)146 void PaUtil_InitializeClock( void )
147 {
148 #ifdef HAVE_MACH_ABSOLUTE_TIME
149     mach_timebase_info_data_t info;
150     kern_return_t err = mach_timebase_info( &info );
151     if( err == 0  )
152         machSecondsConversionScaler_ = 1e-9 * (double) info.numer / (double) info.denom;
153 #endif
154 }
155 
156 
PaUtil_GetTime(void)157 PaTime PaUtil_GetTime( void )
158 {
159 #ifdef HAVE_MACH_ABSOLUTE_TIME
160     return mach_absolute_time() * machSecondsConversionScaler_;
161 #elif defined(HAVE_CLOCK_GETTIME)
162     struct timespec tp;
163     clock_gettime(CLOCK_REALTIME, &tp);
164     return (PaTime)(tp.tv_sec + tp.tv_nsec * 1e-9);
165 #else
166     struct timeval tv;
167     gettimeofday( &tv, NULL );
168     return (PaTime) tv.tv_usec * 1e-6 + tv.tv_sec;
169 #endif
170 }
171 
PaUtil_InitializeThreading(PaUtilThreading * threading)172 PaError PaUtil_InitializeThreading( PaUtilThreading *threading )
173 {
174     (void) paUtilErr_;
175     return paNoError;
176 }
177 
PaUtil_TerminateThreading(PaUtilThreading * threading)178 void PaUtil_TerminateThreading( PaUtilThreading *threading )
179 {
180 }
181 
PaUtil_StartThreading(PaUtilThreading * threading,void * (* threadRoutine)(void *),void * data)182 PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data )
183 {
184     pthread_create( &threading->callbackThread, NULL, threadRoutine, data );
185     return paNoError;
186 }
187 
PaUtil_CancelThreading(PaUtilThreading * threading,int wait,PaError * exitResult)188 PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult )
189 {
190     PaError result = paNoError;
191     void *pret;
192 
193     if( exitResult )
194         *exitResult = paNoError;
195 
196     /* If pthread_cancel is not supported (Android platform) whole this function can lead to indefinite waiting if
197        working thread (callbackThread) has'n received any stop signals from outside, please keep
198        this in mind when considering using PaUtil_CancelThreading
199     */
200 #ifdef PTHREAD_CANCELED
201     /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
202     if( !wait )
203         pthread_cancel( threading->callbackThread );   /* XXX: Safe to call this if the thread has exited on its own? */
204 #endif
205     pthread_join( threading->callbackThread, &pret );
206 
207 #ifdef PTHREAD_CANCELED
208     if( pret && PTHREAD_CANCELED != pret )
209 #else
210     /* !wait means the thread may have been canceled */
211     if( pret && wait )
212 #endif
213     {
214         if( exitResult )
215             *exitResult = *(PaError *) pret;
216         free( pret );
217     }
218 
219     return result;
220 }
221 
222 /* Threading */
223 /* paUnixMainThread
224  * We have to be a bit careful with defining this global variable,
225  * as explained below. */
226 #ifdef __APPLE__
227 /* apple/gcc has a "problem" with global vars and dynamic libs.
228    Initializing it seems to fix the problem.
229    Described a bit in this thread:
230    http://gcc.gnu.org/ml/gcc/2005-06/msg00179.html
231 */
232 pthread_t paUnixMainThread = 0;
233 #else
234 /*pthreads are opaque. We don't know that asigning it an int value
235   always makes sense, so we don't initialize it unless we have to.*/
236 pthread_t paUnixMainThread = 0;
237 #endif
238 
PaUnixThreading_Initialize()239 PaError PaUnixThreading_Initialize()
240 {
241     paUnixMainThread = pthread_self();
242     return paNoError;
243 }
244 
BoostPriority(PaUnixThread * self)245 static PaError BoostPriority( PaUnixThread* self )
246 {
247     PaError result = paNoError;
248     struct sched_param spm = { 0 };
249     /* Priority should only matter between contending FIFO threads? */
250     spm.sched_priority = 1;
251 
252     assert( self );
253 
254     if( pthread_setschedparam( self->thread, SCHED_FIFO, &spm ) != 0 )
255     {
256         PA_UNLESS( errno == EPERM, paInternalError );  /* Lack permission to raise priority */
257         PA_DEBUG(( "Failed bumping priority\n" ));
258         result = 0;
259     }
260     else
261     {
262         result = 1; /* Success */
263     }
264 error:
265     return result;
266 }
267 
PaUnixThread_New(PaUnixThread * self,void * (* threadFunc)(void *),void * threadArg,PaTime waitForChild,int rtSched)268 PaError PaUnixThread_New( PaUnixThread* self, void* (*threadFunc)( void* ), void* threadArg, PaTime waitForChild,
269         int rtSched )
270 {
271     PaError result = paNoError;
272     pthread_attr_t attr;
273     int started = 0;
274 
275     memset( self, 0, sizeof (PaUnixThread) );
276     PaUnixMutex_Initialize( &self->mtx );
277     PA_ASSERT_CALL( pthread_cond_init( &self->cond, NULL ), 0 );
278 
279     self->parentWaiting = 0 != waitForChild;
280 
281     /* Spawn thread */
282 
283 /* Temporarily disabled since we should test during configuration for presence of required mman.h header */
284 #if 0
285 #if defined _POSIX_MEMLOCK && (_POSIX_MEMLOCK != -1)
286     if( rtSched )
287     {
288         if( mlockall( MCL_CURRENT | MCL_FUTURE ) < 0 )
289         {
290             int savedErrno = errno;             /* In case errno gets overwritten */
291             assert( savedErrno != EINVAL );     /* Most likely a programmer error */
292             PA_UNLESS( (savedErrno == EPERM), paInternalError );
293             PA_DEBUG(( "%s: Failed locking memory\n", __FUNCTION__ ));
294         }
295         else
296             PA_DEBUG(( "%s: Successfully locked memory\n", __FUNCTION__ ));
297     }
298 #endif
299 #endif
300 
301     PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
302     /* Priority relative to other processes */
303     PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
304 
305     PA_UNLESS( !pthread_create( &self->thread, &attr, threadFunc, threadArg ), paInternalError );
306     started = 1;
307 
308     if( rtSched )
309     {
310 #if 0
311         if( self->useWatchdog )
312         {
313             int err;
314             struct sched_param wdSpm = { 0 };
315             /* Launch watchdog, watchdog sets callback thread priority */
316             int prio = PA_MIN( self->rtPrio + 4, sched_get_priority_max( SCHED_FIFO ) );
317             wdSpm.sched_priority = prio;
318 
319             PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
320             PA_UNLESS( !pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ), paInternalError );
321             PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
322             PA_UNLESS( !pthread_attr_setschedpolicy( &attr, SCHED_FIFO ), paInternalError );
323             PA_UNLESS( !pthread_attr_setschedparam( &attr, &wdSpm ), paInternalError );
324             if( (err = pthread_create( &self->watchdogThread, &attr, &WatchdogFunc, self )) )
325             {
326                 PA_UNLESS( err == EPERM, paInternalError );
327                 /* Permission error, go on without realtime privileges */
328                 PA_DEBUG(( "Failed bumping priority\n" ));
329             }
330             else
331             {
332                 int policy;
333                 self->watchdogRunning = 1;
334                 PA_ENSURE_SYSTEM( pthread_getschedparam( self->watchdogThread, &policy, &wdSpm ), 0 );
335                 /* Check if priority is right, policy could potentially differ from SCHED_FIFO (but that's alright) */
336                 if( wdSpm.sched_priority != prio )
337                 {
338                     PA_DEBUG(( "Watchdog priority not set correctly (%d)\n", wdSpm.sched_priority ));
339                     PA_ENSURE( paInternalError );
340                 }
341             }
342         }
343         else
344 #endif
345             PA_ENSURE( BoostPriority( self ) );
346 
347         {
348             int policy;
349             struct sched_param spm;
350             pthread_getschedparam(self->thread, &policy, &spm);
351         }
352     }
353 
354     if( self->parentWaiting )
355     {
356         PaTime till;
357         struct timespec ts;
358         int res = 0;
359         PaTime now;
360 
361         PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) );
362 
363         /* Wait for stream to be started */
364         now = PaUtil_GetTime();
365         till = now + waitForChild;
366 
367         while( self->parentWaiting && !res )
368         {
369             if( waitForChild > 0 )
370             {
371                 ts.tv_sec = (time_t) floor( till );
372                 ts.tv_nsec = (long) ((till - floor( till )) * 1e9);
373                 res = pthread_cond_timedwait( &self->cond, &self->mtx.mtx, &ts );
374             }
375             else
376             {
377                 res = pthread_cond_wait( &self->cond, &self->mtx.mtx );
378             }
379         }
380 
381         PA_ENSURE( PaUnixMutex_Unlock( &self->mtx ) );
382 
383         PA_UNLESS( !res || ETIMEDOUT == res, paInternalError );
384         PA_DEBUG(( "%s: Waited for %g seconds for stream to start\n", __FUNCTION__, PaUtil_GetTime() - now ));
385         if( ETIMEDOUT == res )
386         {
387             PA_ENSURE( paTimedOut );
388         }
389     }
390 
391 end:
392     return result;
393 error:
394     if( started )
395     {
396         PaUnixThread_Terminate( self, 0, NULL );
397     }
398 
399     goto end;
400 }
401 
PaUnixThread_Terminate(PaUnixThread * self,int wait,PaError * exitResult)402 PaError PaUnixThread_Terminate( PaUnixThread* self, int wait, PaError* exitResult )
403 {
404     PaError result = paNoError;
405     void* pret;
406 
407     if( exitResult )
408     {
409         *exitResult = paNoError;
410     }
411 #if 0
412     if( watchdogExitResult )
413         *watchdogExitResult = paNoError;
414 
415     if( th->watchdogRunning )
416     {
417         pthread_cancel( th->watchdogThread );
418         PA_ENSURE_SYSTEM( pthread_join( th->watchdogThread, &pret ), 0 );
419 
420         if( pret && pret != PTHREAD_CANCELED )
421         {
422             if( watchdogExitResult )
423                 *watchdogExitResult = *(PaError *) pret;
424             free( pret );
425         }
426     }
427 #endif
428 
429     /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
430     /* TODO: Make join time out */
431     self->stopRequested = wait;
432     if( !wait )
433     {
434         PA_DEBUG(( "%s: Canceling thread %d\n", __FUNCTION__, self->thread ));
435         /* XXX: Safe to call this if the thread has exited on its own? */
436 #ifdef PTHREAD_CANCELED
437         pthread_cancel( self->thread );
438 #endif
439     }
440     PA_DEBUG(( "%s: Joining thread %d\n", __FUNCTION__, self->thread ));
441     PA_ENSURE_SYSTEM( pthread_join( self->thread, &pret ), 0 );
442 
443 #ifdef PTHREAD_CANCELED
444     if( pret && PTHREAD_CANCELED != pret )
445 #else
446     /* !wait means the thread may have been canceled */
447     if( pret && wait )
448 #endif
449     {
450         if( exitResult )
451         {
452             *exitResult = *(PaError*)pret;
453         }
454         free( pret );
455     }
456 
457 error:
458     PA_ASSERT_CALL( PaUnixMutex_Terminate( &self->mtx ), paNoError );
459     PA_ASSERT_CALL( pthread_cond_destroy( &self->cond ), 0 );
460 
461     return result;
462 }
463 
PaUnixThread_PrepareNotify(PaUnixThread * self)464 PaError PaUnixThread_PrepareNotify( PaUnixThread* self )
465 {
466     PaError result = paNoError;
467     PA_UNLESS( self->parentWaiting, paInternalError );
468 
469     PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) );
470     self->locked = 1;
471 
472 error:
473     return result;
474 }
475 
PaUnixThread_NotifyParent(PaUnixThread * self)476 PaError PaUnixThread_NotifyParent( PaUnixThread* self )
477 {
478     PaError result = paNoError;
479     PA_UNLESS( self->parentWaiting, paInternalError );
480 
481     if( !self->locked )
482     {
483         PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) );
484         self->locked = 1;
485     }
486     self->parentWaiting = 0;
487     pthread_cond_signal( &self->cond );
488     PA_ENSURE( PaUnixMutex_Unlock( &self->mtx ) );
489     self->locked = 0;
490 
491 error:
492     return result;
493 }
494 
PaUnixThread_StopRequested(PaUnixThread * self)495 int PaUnixThread_StopRequested( PaUnixThread* self )
496 {
497     return self->stopRequested;
498 }
499 
PaUnixMutex_Initialize(PaUnixMutex * self)500 PaError PaUnixMutex_Initialize( PaUnixMutex* self )
501 {
502     PaError result = paNoError;
503     PA_ASSERT_CALL( pthread_mutex_init( &self->mtx, NULL ), 0 );
504     return result;
505 }
506 
PaUnixMutex_Terminate(PaUnixMutex * self)507 PaError PaUnixMutex_Terminate( PaUnixMutex* self )
508 {
509     PaError result = paNoError;
510     PA_ASSERT_CALL( pthread_mutex_destroy( &self->mtx ), 0 );
511     return result;
512 }
513 
514 /** Lock mutex.
515  *
516  * We're disabling thread cancellation while the thread is holding a lock, so mutexes are
517  * properly unlocked at termination time.
518  */
PaUnixMutex_Lock(PaUnixMutex * self)519 PaError PaUnixMutex_Lock( PaUnixMutex* self )
520 {
521     PaError result = paNoError;
522 
523 #ifdef PTHREAD_CANCEL
524 	int oldState;
525     PA_ENSURE_SYSTEM( pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldState ), 0 );
526 #endif
527     PA_ENSURE_SYSTEM( pthread_mutex_lock( &self->mtx ), 0 );
528 
529 error:
530     return result;
531 }
532 
533 /** Unlock mutex.
534  *
535  * Thread cancellation is enabled again after the mutex is properly unlocked.
536  */
PaUnixMutex_Unlock(PaUnixMutex * self)537 PaError PaUnixMutex_Unlock( PaUnixMutex* self )
538 {
539     PaError result = paNoError;
540 
541     PA_ENSURE_SYSTEM( pthread_mutex_unlock( &self->mtx ), 0 );
542 #ifdef PTHREAD_CANCEL
543 	int oldState;
544     PA_ENSURE_SYSTEM( pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldState ), 0 );
545 #endif
546 
547 error:
548     return result;
549 }
550 
551 
552 #if 0
553 static void OnWatchdogExit( void *userData )
554 {
555     PaAlsaThreading *th = (PaAlsaThreading *) userData;
556     struct sched_param spm = { 0 };
557     assert( th );
558 
559     PA_ASSERT_CALL( pthread_setschedparam( th->callbackThread, SCHED_OTHER, &spm ), 0 );    /* Lower before exiting */
560     PA_DEBUG(( "Watchdog exiting\n" ));
561 }
562 
563 static void *WatchdogFunc( void *userData )
564 {
565     PaError result = paNoError, *pres = NULL;
566     int err;
567     PaAlsaThreading *th = (PaAlsaThreading *) userData;
568     unsigned intervalMsec = 500;
569     const PaTime maxSeconds = 3.;   /* Max seconds between callbacks */
570     PaTime timeThen = PaUtil_GetTime(), timeNow, timeElapsed, cpuTimeThen, cpuTimeNow, cpuTimeElapsed;
571     double cpuLoad, avgCpuLoad = 0.;
572     int throttled = 0;
573 
574     assert( th );
575 
576     /* Execute OnWatchdogExit when exiting */
577     pthread_cleanup_push( &OnWatchdogExit, th );
578 
579     /* Boost priority of callback thread */
580     PA_ENSURE( result = BoostPriority( th ) );
581     if( !result )
582     {
583         /* Boost failed, might as well exit */
584         pthread_exit( NULL );
585     }
586 
587     cpuTimeThen = th->callbackCpuTime;
588     {
589         int policy;
590         struct sched_param spm = { 0 };
591         pthread_getschedparam( pthread_self(), &policy, &spm );
592         PA_DEBUG(( "%s: Watchdog priority is %d\n", __FUNCTION__, spm.sched_priority ));
593     }
594 
595     while( 1 )
596     {
597         double lowpassCoeff = 0.9, lowpassCoeff1 = 0.99999 - lowpassCoeff;
598 
599         /* Test before and after in case whatever underlying sleep call isn't interrupted by pthread_cancel */
600         pthread_testcancel();
601         Pa_Sleep( intervalMsec );
602         pthread_testcancel();
603 
604         if( PaUtil_GetTime() - th->callbackTime > maxSeconds )
605         {
606             PA_DEBUG(( "Watchdog: Terminating callback thread\n" ));
607             /* Tell thread to terminate */
608             err = pthread_kill( th->callbackThread, SIGKILL );
609             pthread_exit( NULL );
610         }
611 
612         PA_DEBUG(( "%s: PortAudio reports CPU load: %g\n", __FUNCTION__, PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) ));
613 
614         /* Check if we should throttle, or unthrottle :P */
615         cpuTimeNow = th->callbackCpuTime;
616         cpuTimeElapsed = cpuTimeNow - cpuTimeThen;
617         cpuTimeThen = cpuTimeNow;
618 
619         timeNow = PaUtil_GetTime();
620         timeElapsed = timeNow - timeThen;
621         timeThen = timeNow;
622         cpuLoad = cpuTimeElapsed / timeElapsed;
623         avgCpuLoad = avgCpuLoad * lowpassCoeff + cpuLoad * lowpassCoeff1;
624         /*
625         if( throttled )
626             PA_DEBUG(( "Watchdog: CPU load: %g, %g\n", avgCpuLoad, cpuTimeElapsed ));
627             */
628         if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) > .925 )
629         {
630             static int policy;
631             static struct sched_param spm = { 0 };
632             static const struct sched_param defaultSpm = { 0 };
633             PA_DEBUG(( "%s: Throttling audio thread, priority %d\n", __FUNCTION__, spm.sched_priority ));
634 
635             pthread_getschedparam( th->callbackThread, &policy, &spm );
636             if( !pthread_setschedparam( th->callbackThread, SCHED_OTHER, &defaultSpm ) )
637             {
638                 throttled = 1;
639             }
640             else
641                 PA_DEBUG(( "Watchdog: Couldn't lower priority of audio thread: %s\n", strerror( errno ) ));
642 
643             /* Give other processes a go, before raising priority again */
644             PA_DEBUG(( "%s: Watchdog sleeping for %lu msecs before unthrottling\n", __FUNCTION__, th->throttledSleepTime ));
645             Pa_Sleep( th->throttledSleepTime );
646 
647             /* Reset callback priority */
648             if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
649             {
650                 PA_DEBUG(( "%s: Couldn't raise priority of audio thread: %s\n", __FUNCTION__, strerror( errno ) ));
651             }
652 
653             if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) >= .99 )
654                 intervalMsec = 50;
655             else
656                 intervalMsec = 100;
657 
658             /*
659             lowpassCoeff = .97;
660             lowpassCoeff1 = .99999 - lowpassCoeff;
661             */
662         }
663         else if( throttled && avgCpuLoad < .8 )
664         {
665             intervalMsec = 500;
666             throttled = 0;
667 
668             /*
669             lowpassCoeff = .9;
670             lowpassCoeff1 = .99999 - lowpassCoeff;
671             */
672         }
673     }
674 
675     pthread_cleanup_pop( 1 );   /* Execute cleanup on exit */
676 
677 error:
678     /* Shouldn't get here in the normal case */
679 
680     /* Pass on error code */
681     pres = malloc( sizeof (PaError) );
682     *pres = result;
683 
684     pthread_exit( pres );
685 }
686 
687 static void CallbackUpdate( PaAlsaThreading *th )
688 {
689     th->callbackTime = PaUtil_GetTime();
690     th->callbackCpuTime = PaUtil_GetCpuLoad( th->cpuLoadMeasurer );
691 }
692 
693 /*
694 static void *CanaryFunc( void *userData )
695 {
696     const unsigned intervalMsec = 1000;
697     PaUtilThreading *th = (PaUtilThreading *) userData;
698 
699     while( 1 )
700     {
701         th->canaryTime = PaUtil_GetTime();
702 
703         pthread_testcancel();
704         Pa_Sleep( intervalMsec );
705     }
706 
707     pthread_exit( NULL );
708 }
709 */
710 #endif
711