~noskcaj/ubuntu/saucy/sflphone/merge-1.2.3-2

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.0.1/third_party/portaudio/src/os/unix/pa_unix_util.c

  • Committer: Jackson Doak
  • Date: 2013-07-10 21:04:46 UTC
  • mfrom: (20.1.3 sid)
  • Revision ID: noskcaj@ubuntu.com-20130710210446-y8f587vza807icr9
Properly merged from upstream.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
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
 
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
 
 
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
 
 
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
 
 
98
int PaUtil_CountCurrentlyAllocatedBlocks( void )
 
99
{
 
100
#if PA_TRACK_MEMORY
 
101
    return numAllocations_;
 
102
#else
 
103
    return 0;
 
104
#endif
 
105
}
 
106
 
 
107
 
 
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
 
 
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
 
 
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
 
 
172
PaError PaUtil_InitializeThreading( PaUtilThreading *threading )
 
173
{
 
174
    (void) paUtilErr_;
 
175
    return paNoError;
 
176
}
 
177
 
 
178
void PaUtil_TerminateThreading( PaUtilThreading *threading )
 
179
{
 
180
}
 
181
 
 
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
 
 
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
    /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
 
197
    if( !wait )
 
198
        pthread_cancel( threading->callbackThread );   /* XXX: Safe to call this if the thread has exited on its own? */
 
199
    pthread_join( threading->callbackThread, &pret );
 
200
 
 
201
#ifdef PTHREAD_CANCELED
 
202
    if( pret && PTHREAD_CANCELED != pret )
 
203
#else
 
204
    /* !wait means the thread may have been canceled */
 
205
    if( pret && wait )
 
206
#endif
 
207
    {
 
208
        if( exitResult )
 
209
            *exitResult = *(PaError *) pret;
 
210
        free( pret );
 
211
    }
 
212
 
 
213
    return result;
 
214
}
 
215
 
 
216
/* Threading */
 
217
/* paUnixMainThread
 
218
 * We have to be a bit careful with defining this global variable,
 
219
 * as explained below. */
 
220
#ifdef __APPLE__
 
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
 
225
*/
 
226
pthread_t paUnixMainThread = 0;
 
227
#else
 
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;
 
231
#endif
 
232
 
 
233
PaError PaUnixThreading_Initialize()
 
234
{
 
235
    paUnixMainThread = pthread_self();
 
236
    return paNoError;
 
237
}
 
238
 
 
239
static PaError BoostPriority( PaUnixThread* self )
 
240
{
 
241
    PaError result = paNoError;
 
242
    struct sched_param spm = { 0 };
 
243
    /* Priority should only matter between contending FIFO threads? */
 
244
    spm.sched_priority = 1;
 
245
 
 
246
    assert( self );
 
247
 
 
248
    if( pthread_setschedparam( self->thread, SCHED_FIFO, &spm ) != 0 )
 
249
    {
 
250
        PA_UNLESS( errno == EPERM, paInternalError );  /* Lack permission to raise priority */
 
251
        PA_DEBUG(( "Failed bumping priority\n" ));
 
252
        result = 0;
 
253
    }
 
254
    else
 
255
    {
 
256
        result = 1; /* Success */
 
257
    }
 
258
error:
 
259
    return result;
 
260
}
 
261
 
 
262
PaError PaUnixThread_New( PaUnixThread* self, void* (*threadFunc)( void* ), void* threadArg, PaTime waitForChild,
 
263
        int rtSched )
 
264
{
 
265
    PaError result = paNoError;
 
266
    pthread_attr_t attr;
 
267
    int started = 0;
 
268
 
 
269
    memset( self, 0, sizeof (PaUnixThread) );
 
270
    PaUnixMutex_Initialize( &self->mtx );
 
271
    PA_ASSERT_CALL( pthread_cond_init( &self->cond, NULL ), 0 );
 
272
 
 
273
    self->parentWaiting = 0 != waitForChild;
 
274
 
 
275
    /* Spawn thread */
 
276
 
 
277
/* Temporarily disabled since we should test during configuration for presence of required mman.h header */
 
278
#if 0
 
279
#if defined _POSIX_MEMLOCK && (_POSIX_MEMLOCK != -1)
 
280
    if( rtSched )
 
281
    {
 
282
        if( mlockall( MCL_CURRENT | MCL_FUTURE ) < 0 )
 
283
        {
 
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__ ));
 
288
        }
 
289
        else
 
290
            PA_DEBUG(( "%s: Successfully locked memory\n", __FUNCTION__ ));
 
291
    }
 
292
#endif
 
293
#endif
 
294
 
 
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 );
 
298
 
 
299
    PA_UNLESS( !pthread_create( &self->thread, &attr, threadFunc, threadArg ), paInternalError );
 
300
    started = 1;
 
301
 
 
302
    if( rtSched )
 
303
    {
 
304
#if 0
 
305
        if( self->useWatchdog )
 
306
        {
 
307
            int err;
 
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;
 
312
 
 
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 )) )
 
319
            {
 
320
                PA_UNLESS( err == EPERM, paInternalError );
 
321
                /* Permission error, go on without realtime privileges */
 
322
                PA_DEBUG(( "Failed bumping priority\n" ));
 
323
            }
 
324
            else
 
325
            {
 
326
                int policy;
 
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 )
 
331
                {
 
332
                    PA_DEBUG(( "Watchdog priority not set correctly (%d)\n", wdSpm.sched_priority ));
 
333
                    PA_ENSURE( paInternalError );
 
334
                }
 
335
            }
 
336
        }
 
337
        else
 
338
#endif
 
339
            PA_ENSURE( BoostPriority( self ) );
 
340
 
 
341
        {
 
342
            int policy;
 
343
            struct sched_param spm;
 
344
            pthread_getschedparam(self->thread, &policy, &spm);
 
345
        }
 
346
    }
 
347
 
 
348
    if( self->parentWaiting )
 
349
    {
 
350
        PaTime till;
 
351
        struct timespec ts;
 
352
        int res = 0;
 
353
        PaTime now;
 
354
 
 
355
        PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) );
 
356
 
 
357
        /* Wait for stream to be started */
 
358
        now = PaUtil_GetTime();
 
359
        till = now + waitForChild;
 
360
 
 
361
        while( self->parentWaiting && !res )
 
362
        {
 
363
            if( waitForChild > 0 )
 
364
            {
 
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 );
 
368
            }
 
369
            else
 
370
            {
 
371
                res = pthread_cond_wait( &self->cond, &self->mtx.mtx );
 
372
            }
 
373
        }
 
374
 
 
375
        PA_ENSURE( PaUnixMutex_Unlock( &self->mtx ) );
 
376
 
 
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 )
 
380
        {
 
381
            PA_ENSURE( paTimedOut );
 
382
        }
 
383
    }
 
384
 
 
385
end:
 
386
    return result;
 
387
error:
 
388
    if( started )
 
389
    {
 
390
        PaUnixThread_Terminate( self, 0, NULL );
 
391
    }
 
392
 
 
393
    goto end;
 
394
}
 
395
 
 
396
PaError PaUnixThread_Terminate( PaUnixThread* self, int wait, PaError* exitResult )
 
397
{
 
398
    PaError result = paNoError;
 
399
    void* pret;
 
400
 
 
401
    if( exitResult )
 
402
    {
 
403
        *exitResult = paNoError;
 
404
    }
 
405
#if 0
 
406
    if( watchdogExitResult )
 
407
        *watchdogExitResult = paNoError;
 
408
 
 
409
    if( th->watchdogRunning )
 
410
    {
 
411
        pthread_cancel( th->watchdogThread );
 
412
        PA_ENSURE_SYSTEM( pthread_join( th->watchdogThread, &pret ), 0 );
 
413
 
 
414
        if( pret && pret != PTHREAD_CANCELED )
 
415
        {
 
416
            if( watchdogExitResult )
 
417
                *watchdogExitResult = *(PaError *) pret;
 
418
            free( pret );
 
419
        }
 
420
    }
 
421
#endif
 
422
 
 
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;
 
426
    if( !wait )
 
427
    {
 
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 );
 
431
    }
 
432
    PA_DEBUG(( "%s: Joining thread %d\n", __FUNCTION__, self->thread ));
 
433
    PA_ENSURE_SYSTEM( pthread_join( self->thread, &pret ), 0 );
 
434
 
 
435
    if( pret && PTHREAD_CANCELED != pret )
 
436
    {
 
437
        if( exitResult )
 
438
        {
 
439
            *exitResult = *(PaError*)pret;
 
440
        }
 
441
        free( pret );
 
442
    }
 
443
 
 
444
error:
 
445
    PA_ASSERT_CALL( PaUnixMutex_Terminate( &self->mtx ), paNoError );
 
446
    PA_ASSERT_CALL( pthread_cond_destroy( &self->cond ), 0 );
 
447
 
 
448
    return result;
 
449
}
 
450
 
 
451
PaError PaUnixThread_PrepareNotify( PaUnixThread* self )
 
452
{
 
453
    PaError result = paNoError;
 
454
    PA_UNLESS( self->parentWaiting, paInternalError );
 
455
 
 
456
    PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) );
 
457
    self->locked = 1;
 
458
 
 
459
error:
 
460
    return result;
 
461
}
 
462
 
 
463
PaError PaUnixThread_NotifyParent( PaUnixThread* self )
 
464
{
 
465
    PaError result = paNoError;
 
466
    PA_UNLESS( self->parentWaiting, paInternalError );
 
467
 
 
468
    if( !self->locked )
 
469
    {
 
470
        PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) );
 
471
        self->locked = 1;
 
472
    }
 
473
    self->parentWaiting = 0;
 
474
    pthread_cond_signal( &self->cond );
 
475
    PA_ENSURE( PaUnixMutex_Unlock( &self->mtx ) );
 
476
    self->locked = 0;
 
477
 
 
478
error:
 
479
    return result;
 
480
}
 
481
 
 
482
int PaUnixThread_StopRequested( PaUnixThread* self )
 
483
{
 
484
    return self->stopRequested;
 
485
}
 
486
 
 
487
PaError PaUnixMutex_Initialize( PaUnixMutex* self )
 
488
{
 
489
    PaError result = paNoError;
 
490
    PA_ASSERT_CALL( pthread_mutex_init( &self->mtx, NULL ), 0 );
 
491
    return result;
 
492
}
 
493
 
 
494
PaError PaUnixMutex_Terminate( PaUnixMutex* self )
 
495
{
 
496
    PaError result = paNoError;
 
497
    PA_ASSERT_CALL( pthread_mutex_destroy( &self->mtx ), 0 );
 
498
    return result;
 
499
}
 
500
 
 
501
/** Lock mutex.
 
502
 *
 
503
 * We're disabling thread cancellation while the thread is holding a lock, so mutexes are
 
504
 * properly unlocked at termination time.
 
505
 */
 
506
PaError PaUnixMutex_Lock( PaUnixMutex* self )
 
507
{
 
508
    PaError result = paNoError;
 
509
    int oldState;
 
510
 
 
511
    PA_ENSURE_SYSTEM( pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldState ), 0 );
 
512
    PA_ENSURE_SYSTEM( pthread_mutex_lock( &self->mtx ), 0 );
 
513
 
 
514
error:
 
515
    return result;
 
516
}
 
517
 
 
518
/** Unlock mutex.
 
519
 *
 
520
 * Thread cancellation is enabled again after the mutex is properly unlocked.
 
521
 */
 
522
PaError PaUnixMutex_Unlock( PaUnixMutex* self )
 
523
{
 
524
    PaError result = paNoError;
 
525
    int oldState;
 
526
 
 
527
    PA_ENSURE_SYSTEM( pthread_mutex_unlock( &self->mtx ), 0 );
 
528
    PA_ENSURE_SYSTEM( pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldState ), 0 );
 
529
 
 
530
error:
 
531
    return result;
 
532
}
 
533
 
 
534
 
 
535
#if 0
 
536
static void OnWatchdogExit( void *userData )
 
537
{
 
538
    PaAlsaThreading *th = (PaAlsaThreading *) userData;
 
539
    struct sched_param spm = { 0 };
 
540
    assert( th );
 
541
 
 
542
    PA_ASSERT_CALL( pthread_setschedparam( th->callbackThread, SCHED_OTHER, &spm ), 0 );    /* Lower before exiting */
 
543
    PA_DEBUG(( "Watchdog exiting\n" ));
 
544
}
 
545
 
 
546
static void *WatchdogFunc( void *userData )
 
547
{
 
548
    PaError result = paNoError, *pres = NULL;
 
549
    int err;
 
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.;
 
555
    int throttled = 0;
 
556
 
 
557
    assert( th );
 
558
 
 
559
    /* Execute OnWatchdogExit when exiting */
 
560
    pthread_cleanup_push( &OnWatchdogExit, th );
 
561
 
 
562
    /* Boost priority of callback thread */
 
563
    PA_ENSURE( result = BoostPriority( th ) );
 
564
    if( !result )
 
565
    {
 
566
        /* Boost failed, might as well exit */
 
567
        pthread_exit( NULL );
 
568
    }
 
569
 
 
570
    cpuTimeThen = th->callbackCpuTime;
 
571
    {
 
572
        int policy;
 
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 ));
 
576
    }
 
577
 
 
578
    while( 1 )
 
579
    {
 
580
        double lowpassCoeff = 0.9, lowpassCoeff1 = 0.99999 - lowpassCoeff;
 
581
 
 
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();
 
586
 
 
587
        if( PaUtil_GetTime() - th->callbackTime > maxSeconds )
 
588
        {
 
589
            PA_DEBUG(( "Watchdog: Terminating callback thread\n" ));
 
590
            /* Tell thread to terminate */
 
591
            err = pthread_kill( th->callbackThread, SIGKILL );
 
592
            pthread_exit( NULL );
 
593
        }
 
594
 
 
595
        PA_DEBUG(( "%s: PortAudio reports CPU load: %g\n", __FUNCTION__, PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) ));
 
596
 
 
597
        /* Check if we should throttle, or unthrottle :P */
 
598
        cpuTimeNow = th->callbackCpuTime;
 
599
        cpuTimeElapsed = cpuTimeNow - cpuTimeThen;
 
600
        cpuTimeThen = cpuTimeNow;
 
601
 
 
602
        timeNow = PaUtil_GetTime();
 
603
        timeElapsed = timeNow - timeThen;
 
604
        timeThen = timeNow;
 
605
        cpuLoad = cpuTimeElapsed / timeElapsed;
 
606
        avgCpuLoad = avgCpuLoad * lowpassCoeff + cpuLoad * lowpassCoeff1;
 
607
        /*
 
608
        if( throttled )
 
609
            PA_DEBUG(( "Watchdog: CPU load: %g, %g\n", avgCpuLoad, cpuTimeElapsed ));
 
610
            */
 
611
        if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) > .925 )
 
612
        {
 
613
            static int policy;
 
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 ));
 
617
 
 
618
            pthread_getschedparam( th->callbackThread, &policy, &spm );
 
619
            if( !pthread_setschedparam( th->callbackThread, SCHED_OTHER, &defaultSpm ) )
 
620
            {
 
621
                throttled = 1;
 
622
            }
 
623
            else
 
624
                PA_DEBUG(( "Watchdog: Couldn't lower priority of audio thread: %s\n", strerror( errno ) ));
 
625
 
 
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 );
 
629
 
 
630
            /* Reset callback priority */
 
631
            if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
 
632
            {
 
633
                PA_DEBUG(( "%s: Couldn't raise priority of audio thread: %s\n", __FUNCTION__, strerror( errno ) ));
 
634
            }
 
635
 
 
636
            if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) >= .99 )
 
637
                intervalMsec = 50;
 
638
            else
 
639
                intervalMsec = 100;
 
640
 
 
641
            /*
 
642
            lowpassCoeff = .97;
 
643
            lowpassCoeff1 = .99999 - lowpassCoeff;
 
644
            */
 
645
        }
 
646
        else if( throttled && avgCpuLoad < .8 )
 
647
        {
 
648
            intervalMsec = 500;
 
649
            throttled = 0;
 
650
 
 
651
            /*
 
652
            lowpassCoeff = .9;
 
653
            lowpassCoeff1 = .99999 - lowpassCoeff;
 
654
            */
 
655
        }
 
656
    }
 
657
 
 
658
    pthread_cleanup_pop( 1 );   /* Execute cleanup on exit */
 
659
 
 
660
error:
 
661
    /* Shouldn't get here in the normal case */
 
662
 
 
663
    /* Pass on error code */
 
664
    pres = malloc( sizeof (PaError) );
 
665
    *pres = result;
 
666
 
 
667
    pthread_exit( pres );
 
668
}
 
669
 
 
670
static void CallbackUpdate( PaAlsaThreading *th )
 
671
{
 
672
    th->callbackTime = PaUtil_GetTime();
 
673
    th->callbackCpuTime = PaUtil_GetCpuLoad( th->cpuLoadMeasurer );
 
674
}
 
675
 
 
676
/*
 
677
static void *CanaryFunc( void *userData )
 
678
{
 
679
    const unsigned intervalMsec = 1000;
 
680
    PaUtilThreading *th = (PaUtilThreading *) userData;
 
681
 
 
682
    while( 1 )
 
683
    {
 
684
        th->canaryTime = PaUtil_GetTime();
 
685
 
 
686
        pthread_testcancel();
 
687
        Pa_Sleep( intervalMsec );
 
688
    }
 
689
 
 
690
    pthread_exit( NULL );
 
691
}
 
692
*/
 
693
#endif