~ubuntu-branches/ubuntu/trusty/teeworlds/trusty-updates

« back to all changes in this revision

Viewing changes to src/engine/external/portaudio/src/os/unix/pa_unix_util.c

  • Committer: Bazaar Package Importer
  • Author(s): Jack Coulter
  • Date: 2008-04-13 18:48:12 UTC
  • Revision ID: james.westby@ubuntu.com-20080413184812-efc80waq2er6p1bs
Tags: upstream-0.4.2
ImportĀ upstreamĀ versionĀ 0.4.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
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
 
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
#include "pa_util.h"
 
55
#include "pa_unix_util.h"
 
56
#include "pa_debugprint.h"
 
57
 
 
58
/*
 
59
   Track memory allocations to avoid leaks.
 
60
 */
 
61
 
 
62
#if PA_TRACK_MEMORY
 
63
static int numAllocations_ = 0;
 
64
#endif
 
65
 
 
66
 
 
67
void *PaUtil_AllocateMemory( long size )
 
68
{
 
69
    void *result = malloc( size );
 
70
 
 
71
#if PA_TRACK_MEMORY
 
72
    if( result != NULL ) numAllocations_ += 1;
 
73
#endif
 
74
    return result;
 
75
}
 
76
 
 
77
 
 
78
void PaUtil_FreeMemory( void *block )
 
79
{
 
80
    if( block != NULL )
 
81
    {
 
82
        free( block );
 
83
#if PA_TRACK_MEMORY
 
84
        numAllocations_ -= 1;
 
85
#endif
 
86
 
 
87
    }
 
88
}
 
89
 
 
90
 
 
91
int PaUtil_CountCurrentlyAllocatedBlocks( void )
 
92
{
 
93
#if PA_TRACK_MEMORY
 
94
    return numAllocations_;
 
95
#else
 
96
    return 0;
 
97
#endif
 
98
}
 
99
 
 
100
 
 
101
void Pa_Sleep( long msec )
 
102
{
 
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? */
 
111
#else
 
112
    while( msec > 999 )     /* For OpenBSD and IRIX, argument */
 
113
        {                   /* to usleep must be < 1000000.   */
 
114
        usleep( 999000 );
 
115
        msec -= 999;
 
116
        }
 
117
    usleep( msec * 1000 );
 
118
#endif
 
119
}
 
120
 
 
121
/*            *** NOT USED YET: ***
 
122
static int usePerformanceCounter_;
 
123
static double microsecondsPerTick_;
 
124
*/
 
125
 
 
126
void PaUtil_InitializeClock( void )
 
127
{
 
128
    /* TODO */
 
129
}
 
130
 
 
131
 
 
132
PaTime PaUtil_GetTime( void )
 
133
{
 
134
#ifdef HAVE_CLOCK_GETTIME
 
135
    struct timespec tp;
 
136
    clock_gettime(CLOCK_REALTIME, &tp);
 
137
    return (PaTime)(tp.tv_sec + tp.tv_nsec / 1.e9);
 
138
#else
 
139
    struct timeval tv;
 
140
    gettimeofday( &tv, NULL );
 
141
    return (PaTime) tv.tv_usec / 1000000. + tv.tv_sec;
 
142
#endif
 
143
}
 
144
 
 
145
PaError PaUtil_InitializeThreading( PaUtilThreading *threading )
 
146
{
 
147
    (void) paUtilErr_;
 
148
    return paNoError;
 
149
}
 
150
 
 
151
void PaUtil_TerminateThreading( PaUtilThreading *threading )
 
152
{
 
153
}
 
154
 
 
155
PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data )
 
156
{
 
157
    pthread_create( &threading->callbackThread, NULL, threadRoutine, data );
 
158
    return paNoError;
 
159
}
 
160
 
 
161
PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult )
 
162
{
 
163
    PaError result = paNoError;
 
164
    void *pret;
 
165
 
 
166
    if( exitResult )
 
167
        *exitResult = paNoError;
 
168
 
 
169
    /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
 
170
    if( !wait )
 
171
        pthread_cancel( threading->callbackThread );   /* XXX: Safe to call this if the thread has exited on its own? */
 
172
    pthread_join( threading->callbackThread, &pret );
 
173
 
 
174
#ifdef PTHREAD_CANCELED
 
175
    if( pret && PTHREAD_CANCELED != pret )
 
176
#else
 
177
    /* !wait means the thread may have been canceled */
 
178
    if( pret && wait )
 
179
#endif
 
180
    {
 
181
        if( exitResult )
 
182
            *exitResult = *(PaError *) pret;
 
183
        free( pret );
 
184
    }
 
185
 
 
186
    return result;
 
187
}
 
188
 
 
189
/* Threading */
 
190
/* paUnixMainThread 
 
191
 * We have to be a bit careful with defining this global variable,
 
192
 * as explained below. */
 
193
#ifdef __apple__
 
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
 
198
*/
 
199
pthread_t paUnixMainThread = 0;
 
200
#else
 
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;
 
204
#endif
 
205
 
 
206
PaError PaUnixThreading_Initialize()
 
207
{
 
208
    paUnixMainThread = pthread_self();
 
209
    return paNoError;
 
210
}
 
211
 
 
212
static PaError BoostPriority( PaUnixThread* self )
 
213
{
 
214
    PaError result = paNoError;
 
215
    struct sched_param spm = { 0 };
 
216
    /* Priority should only matter between contending FIFO threads? */
 
217
    spm.sched_priority = 1;
 
218
 
 
219
    assert( self );
 
220
 
 
221
    if( pthread_setschedparam( self->thread, SCHED_FIFO, &spm ) != 0 )
 
222
    {
 
223
        PA_UNLESS( errno == EPERM, paInternalError );  /* Lack permission to raise priority */
 
224
        PA_DEBUG(( "Failed bumping priority\n" ));
 
225
        result = 0;
 
226
    }
 
227
    else
 
228
    {
 
229
        result = 1; /* Success */
 
230
    }
 
231
error:
 
232
    return result;
 
233
}
 
234
 
 
235
PaError PaUnixThread_New( PaUnixThread* self, void* (*threadFunc)( void* ), void* threadArg, PaTime waitForChild,
 
236
        int rtSched )
 
237
{
 
238
    PaError result = paNoError;
 
239
    pthread_attr_t attr;
 
240
    int started = 0;
 
241
 
 
242
    memset( self, 0, sizeof (PaUnixThread) );
 
243
    PaUnixMutex_Initialize( &self->mtx );
 
244
    PA_ASSERT_CALL( pthread_cond_init( &self->cond, NULL ), 0 );
 
245
 
 
246
    self->parentWaiting = 0 != waitForChild;
 
247
 
 
248
    /* Spawn thread */
 
249
 
 
250
/* Temporarily disabled since we should test during configuration for presence of required mman.h header */
 
251
#if 0
 
252
#if defined _POSIX_MEMLOCK && (_POSIX_MEMLOCK != -1)
 
253
    if( rtSched )
 
254
    {
 
255
        if( mlockall( MCL_CURRENT | MCL_FUTURE ) < 0 )
 
256
        {
 
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__ ));
 
261
        }
 
262
        else
 
263
            PA_DEBUG(( "%s: Successfully locked memory\n", __FUNCTION__ ));
 
264
    }
 
265
#endif
 
266
#endif
 
267
 
 
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 );   
 
271
 
 
272
    PA_UNLESS( !pthread_create( &self->thread, &attr, threadFunc, threadArg ), paInternalError );
 
273
    started = 1;
 
274
 
 
275
    if( rtSched )
 
276
    {
 
277
#if 0
 
278
        if( self->useWatchdog )
 
279
        {
 
280
            int err;
 
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;
 
285
 
 
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 )) )
 
292
            {
 
293
                PA_UNLESS( err == EPERM, paInternalError );
 
294
                /* Permission error, go on without realtime privileges */
 
295
                PA_DEBUG(( "Failed bumping priority\n" ));
 
296
            }
 
297
            else
 
298
            {
 
299
                int policy;
 
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 )
 
304
                {
 
305
                    PA_DEBUG(( "Watchdog priority not set correctly (%d)\n", wdSpm.sched_priority ));
 
306
                    PA_ENSURE( paInternalError );
 
307
                }
 
308
            }
 
309
        }
 
310
        else
 
311
#endif
 
312
            PA_ENSURE( BoostPriority( self ) );
 
313
 
 
314
        {
 
315
            int policy;
 
316
            struct sched_param spm;
 
317
            pthread_getschedparam(self->thread, &policy, &spm);
 
318
        }
 
319
    }
 
320
    
 
321
    if( self->parentWaiting )
 
322
    {
 
323
        PaTime till;
 
324
        struct timespec ts;
 
325
        int res = 0;
 
326
        PaTime now;
 
327
 
 
328
        PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) );
 
329
 
 
330
        /* Wait for stream to be started */
 
331
        now = PaUtil_GetTime();
 
332
        till = now + waitForChild;
 
333
 
 
334
        while( self->parentWaiting && !res )
 
335
        {
 
336
            if( waitForChild > 0 )
 
337
            {
 
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 );
 
341
            }
 
342
            else
 
343
            {
 
344
                res = pthread_cond_wait( &self->cond, &self->mtx.mtx );
 
345
            }
 
346
        }
 
347
 
 
348
        PA_ENSURE( PaUnixMutex_Unlock( &self->mtx ) );
 
349
 
 
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 )
 
353
        {
 
354
            PA_ENSURE( paTimedOut );
 
355
        }
 
356
    }
 
357
 
 
358
end:
 
359
    return result;
 
360
error:
 
361
    if( started )
 
362
    {
 
363
        PaUnixThread_Terminate( self, 0, NULL );
 
364
    }
 
365
 
 
366
    goto end;
 
367
}
 
368
 
 
369
PaError PaUnixThread_Terminate( PaUnixThread* self, int wait, PaError* exitResult )
 
370
{
 
371
    PaError result = paNoError;
 
372
    void* pret;
 
373
 
 
374
    if( exitResult )
 
375
    {
 
376
        *exitResult = paNoError;
 
377
    }
 
378
#if 0
 
379
    if( watchdogExitResult )
 
380
        *watchdogExitResult = paNoError;
 
381
 
 
382
    if( th->watchdogRunning )
 
383
    {
 
384
        pthread_cancel( th->watchdogThread );
 
385
        PA_ENSURE_SYSTEM( pthread_join( th->watchdogThread, &pret ), 0 );
 
386
 
 
387
        if( pret && pret != PTHREAD_CANCELED )
 
388
        {
 
389
            if( watchdogExitResult )
 
390
                *watchdogExitResult = *(PaError *) pret;
 
391
            free( pret );
 
392
        }
 
393
    }
 
394
#endif
 
395
 
 
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;
 
399
    if( !wait )
 
400
    {
 
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 );
 
404
    }
 
405
    PA_DEBUG(( "%s: Joining thread %d\n", __FUNCTION__, self->thread ));
 
406
    PA_ENSURE_SYSTEM( pthread_join( self->thread, &pret ), 0 );
 
407
 
 
408
    if( pret && PTHREAD_CANCELED != pret )
 
409
    {
 
410
        if( exitResult )
 
411
        {
 
412
            *exitResult = *(PaError*)pret;
 
413
        }
 
414
        free( pret );
 
415
    }
 
416
 
 
417
error:
 
418
    PA_ASSERT_CALL( PaUnixMutex_Terminate( &self->mtx ), paNoError );
 
419
    PA_ASSERT_CALL( pthread_cond_destroy( &self->cond ), 0 );
 
420
 
 
421
    return result;
 
422
}
 
423
 
 
424
PaError PaUnixThread_PrepareNotify( PaUnixThread* self )
 
425
{
 
426
    PaError result = paNoError;
 
427
    PA_UNLESS( self->parentWaiting, paInternalError );
 
428
 
 
429
    PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) );
 
430
    self->locked = 1;
 
431
 
 
432
error:
 
433
    return result;
 
434
}
 
435
 
 
436
PaError PaUnixThread_NotifyParent( PaUnixThread* self )
 
437
{
 
438
    PaError result = paNoError;
 
439
    PA_UNLESS( self->parentWaiting, paInternalError );
 
440
 
 
441
    if( !self->locked )
 
442
    {
 
443
        PA_ENSURE( PaUnixMutex_Lock( &self->mtx ) );
 
444
        self->locked = 1;
 
445
    }
 
446
    self->parentWaiting = 0;
 
447
    pthread_cond_signal( &self->cond );
 
448
    PA_ENSURE( PaUnixMutex_Unlock( &self->mtx ) );
 
449
    self->locked = 0;
 
450
 
 
451
error:
 
452
    return result;
 
453
}
 
454
 
 
455
int PaUnixThread_StopRequested( PaUnixThread* self )
 
456
{
 
457
    return self->stopRequested;
 
458
}
 
459
 
 
460
PaError PaUnixMutex_Initialize( PaUnixMutex* self )
 
461
{
 
462
    PaError result = paNoError;
 
463
    PA_ASSERT_CALL( pthread_mutex_init( &self->mtx, NULL ), 0 );
 
464
    return result;
 
465
}
 
466
 
 
467
PaError PaUnixMutex_Terminate( PaUnixMutex* self )
 
468
{
 
469
    PaError result = paNoError;
 
470
    PA_ASSERT_CALL( pthread_mutex_destroy( &self->mtx ), 0 );
 
471
    return result;
 
472
}
 
473
 
 
474
/** Lock mutex.
 
475
 *
 
476
 * We're disabling thread cancellation while the thread is holding a lock, so mutexes are 
 
477
 * properly unlocked at termination time.
 
478
 */
 
479
PaError PaUnixMutex_Lock( PaUnixMutex* self )
 
480
{
 
481
    PaError result = paNoError;
 
482
    int oldState;
 
483
    
 
484
    PA_ENSURE_SYSTEM( pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldState ), 0 );
 
485
    PA_ENSURE_SYSTEM( pthread_mutex_lock( &self->mtx ), 0 );
 
486
 
 
487
error:
 
488
    return result;
 
489
}
 
490
 
 
491
/** Unlock mutex.
 
492
 *
 
493
 * Thread cancellation is enabled again after the mutex is properly unlocked.
 
494
 */
 
495
PaError PaUnixMutex_Unlock( PaUnixMutex* self )
 
496
{
 
497
    PaError result = paNoError;
 
498
    int oldState;
 
499
 
 
500
    PA_ENSURE_SYSTEM( pthread_mutex_unlock( &self->mtx ), 0 );
 
501
    PA_ENSURE_SYSTEM( pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldState ), 0 );
 
502
 
 
503
error:
 
504
    return result;
 
505
}
 
506
 
 
507
 
 
508
#if 0
 
509
static void OnWatchdogExit( void *userData )
 
510
{
 
511
    PaAlsaThreading *th = (PaAlsaThreading *) userData;
 
512
    struct sched_param spm = { 0 };
 
513
    assert( th );
 
514
 
 
515
    PA_ASSERT_CALL( pthread_setschedparam( th->callbackThread, SCHED_OTHER, &spm ), 0 );    /* Lower before exiting */
 
516
    PA_DEBUG(( "Watchdog exiting\n" ));
 
517
}
 
518
 
 
519
static void *WatchdogFunc( void *userData )
 
520
{
 
521
    PaError result = paNoError, *pres = NULL;
 
522
    int err;
 
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.;
 
528
    int throttled = 0;
 
529
 
 
530
    assert( th );
 
531
 
 
532
    /* Execute OnWatchdogExit when exiting */
 
533
    pthread_cleanup_push( &OnWatchdogExit, th );
 
534
 
 
535
    /* Boost priority of callback thread */
 
536
    PA_ENSURE( result = BoostPriority( th ) );
 
537
    if( !result )
 
538
    {
 
539
        /* Boost failed, might as well exit */
 
540
        pthread_exit( NULL );
 
541
    }
 
542
 
 
543
    cpuTimeThen = th->callbackCpuTime;
 
544
    {
 
545
        int policy;
 
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 ));
 
549
    }
 
550
 
 
551
    while( 1 )
 
552
    {
 
553
        double lowpassCoeff = 0.9, lowpassCoeff1 = 0.99999 - lowpassCoeff;
 
554
        
 
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();
 
559
 
 
560
        if( PaUtil_GetTime() - th->callbackTime > maxSeconds )
 
561
        {
 
562
            PA_DEBUG(( "Watchdog: Terminating callback thread\n" ));
 
563
            /* Tell thread to terminate */
 
564
            err = pthread_kill( th->callbackThread, SIGKILL );
 
565
            pthread_exit( NULL );
 
566
        }
 
567
 
 
568
        PA_DEBUG(( "%s: PortAudio reports CPU load: %g\n", __FUNCTION__, PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) ));
 
569
 
 
570
        /* Check if we should throttle, or unthrottle :P */
 
571
        cpuTimeNow = th->callbackCpuTime;
 
572
        cpuTimeElapsed = cpuTimeNow - cpuTimeThen;
 
573
        cpuTimeThen = cpuTimeNow;
 
574
 
 
575
        timeNow = PaUtil_GetTime();
 
576
        timeElapsed = timeNow - timeThen;
 
577
        timeThen = timeNow;
 
578
        cpuLoad = cpuTimeElapsed / timeElapsed;
 
579
        avgCpuLoad = avgCpuLoad * lowpassCoeff + cpuLoad * lowpassCoeff1;
 
580
        /*
 
581
        if( throttled )
 
582
            PA_DEBUG(( "Watchdog: CPU load: %g, %g\n", avgCpuLoad, cpuTimeElapsed ));
 
583
            */
 
584
        if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) > .925 )
 
585
        {
 
586
            static int policy;
 
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 ));
 
590
 
 
591
            pthread_getschedparam( th->callbackThread, &policy, &spm );
 
592
            if( !pthread_setschedparam( th->callbackThread, SCHED_OTHER, &defaultSpm ) )
 
593
            {
 
594
                throttled = 1;
 
595
            }
 
596
            else
 
597
                PA_DEBUG(( "Watchdog: Couldn't lower priority of audio thread: %s\n", strerror( errno ) ));
 
598
 
 
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 );
 
602
 
 
603
            /* Reset callback priority */
 
604
            if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
 
605
            {
 
606
                PA_DEBUG(( "%s: Couldn't raise priority of audio thread: %s\n", __FUNCTION__, strerror( errno ) ));
 
607
            }
 
608
 
 
609
            if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) >= .99 )
 
610
                intervalMsec = 50;
 
611
            else
 
612
                intervalMsec = 100;
 
613
 
 
614
            /*
 
615
            lowpassCoeff = .97;
 
616
            lowpassCoeff1 = .99999 - lowpassCoeff;
 
617
            */
 
618
        }
 
619
        else if( throttled && avgCpuLoad < .8 )
 
620
        {
 
621
            intervalMsec = 500;
 
622
            throttled = 0;
 
623
 
 
624
            /*
 
625
            lowpassCoeff = .9;
 
626
            lowpassCoeff1 = .99999 - lowpassCoeff;
 
627
            */
 
628
        }
 
629
    }
 
630
 
 
631
    pthread_cleanup_pop( 1 );   /* Execute cleanup on exit */
 
632
 
 
633
error:
 
634
    /* Shouldn't get here in the normal case */
 
635
 
 
636
    /* Pass on error code */
 
637
    pres = malloc( sizeof (PaError) );
 
638
    *pres = result;
 
639
    
 
640
    pthread_exit( pres );
 
641
}
 
642
 
 
643
static void CallbackUpdate( PaAlsaThreading *th )
 
644
{
 
645
    th->callbackTime = PaUtil_GetTime();
 
646
    th->callbackCpuTime = PaUtil_GetCpuLoad( th->cpuLoadMeasurer );
 
647
}
 
648
 
 
649
/*
 
650
static void *CanaryFunc( void *userData )
 
651
{
 
652
    const unsigned intervalMsec = 1000;
 
653
    PaUtilThreading *th = (PaUtilThreading *) userData;
 
654
 
 
655
    while( 1 )
 
656
    {
 
657
        th->canaryTime = PaUtil_GetTime();
 
658
 
 
659
        pthread_testcancel();
 
660
        Pa_Sleep( intervalMsec );
 
661
    }
 
662
 
 
663
    pthread_exit( NULL );
 
664
}
 
665
*/
 
666
#endif