📄 pa_unix.c
字号:
* and the watchdog will detect it. */#define SCHEDULER_POLICY SCHED_RR#define WATCHDOG_MAX_SECONDS (3)#define WATCHDOG_INTERVAL_USEC (1000000)static int PaHost_CanaryProc( PaHostSoundControl *pahsc ){ int result = 0;#ifdef GNUSTEP GSRegisterCurrentThread(); /* SB20010904 */#endif while( pahsc->pahsc_CanaryRun) { usleep( WATCHDOG_INTERVAL_USEC ); gettimeofday( &pahsc->pahsc_CanaryTime, NULL ); } DBUG(("PaHost_CanaryProc: exiting.\n"));#ifdef GNUSTEP GSUnregisterCurrentThread(); /* SB20010904 */#endif return result;}/******************************************************************************************* * Monitor audio thread and lower its it if it hogs the CPU. * To prevent getting killed, the audio thread must update a * variable with a timer value. * If the value is not recent enough, then the * thread will get killed. */static PaError PaHost_WatchDogProc( PaHostSoundControl *pahsc ){ struct sched_param schp = { 0 }; int maxPri;#ifdef GNUSTEP GSRegisterCurrentThread(); /* SB20010904 */#endif/* Run at a priority level above audio thread so we can still run if it hangs. *//* Rise more than 1 because of rumored off-by-one scheduler bugs. */ schp.sched_priority = pahsc->pahsc_AudioPriority + 4; maxPri = sched_get_priority_max(SCHEDULER_POLICY); if( schp.sched_priority > maxPri ) schp.sched_priority = maxPri; if (sched_setscheduler(0, SCHEDULER_POLICY, &schp) != 0) { ERR_RPT(("PaHost_WatchDogProc: cannot set watch dog priority!\n")); goto killAudio; } /* Compare watchdog time with audio and canary thread times. */ /* Sleep for a while or until thread cancelled. */ while( pahsc->pahsc_WatchDogRun ) { int delta; struct timeval currentTime; usleep( WATCHDOG_INTERVAL_USEC ); gettimeofday( ¤tTime, NULL ); /* If audio thread is not advancing, then it must be hung so kill it. */ delta = currentTime.tv_sec - pahsc->pahsc_EntryTime.tv_sec; DBUG(("PaHost_WatchDogProc: audio delta = %d\n", delta )); if( delta > WATCHDOG_MAX_SECONDS ) { goto killAudio; } /* If canary died, then lower audio priority and halt canary. */ delta = currentTime.tv_sec - pahsc->pahsc_CanaryTime.tv_sec; if( delta > WATCHDOG_MAX_SECONDS ) { ERR_RPT(("PaHost_WatchDogProc: canary died!\n")); goto lowerAudio; } } DBUG(("PaHost_WatchDogProc: exiting.\n"));#ifdef GNUSTEP GSUnregisterCurrentThread(); /* SB20010904 */#endif return 0;lowerAudio: { struct sched_param schat = { 0 }; if( sched_setscheduler(pahsc->pahsc_AudioThreadPID, SCHED_OTHER, &schat) != 0) { ERR_RPT(("PaHost_WatchDogProc: failed to lower audio priority. errno = %d\n", errno )); /* Fall through into killing audio thread. */ } else { ERR_RPT(("PaHost_WatchDogProc: lowered audio priority to prevent hogging of CPU.\n")); goto cleanup; } }killAudio: ERR_RPT(("PaHost_WatchDogProc: killing hung audio thread!\n")); pthread_kill( pahsc->pahsc_AudioThread, SIGKILL );cleanup: pahsc->pahsc_CanaryRun = 0; DBUG(("PaHost_WatchDogProc: cancel Canary\n")); pthread_cancel( pahsc->pahsc_CanaryThread ); DBUG(("PaHost_WatchDogProc: join Canary\n")); pthread_join( pahsc->pahsc_CanaryThread, NULL ); DBUG(("PaHost_WatchDogProc: forget Canary\n")); pahsc->pahsc_IsCanaryThreadValid = 0;#ifdef GNUSTEP GSUnregisterCurrentThread(); /* SB20010904 */#endif return 0;}/*******************************************************************************************/static void PaHost_StopWatchDog( PaHostSoundControl *pahsc ){/* Cancel WatchDog thread if there is one. */ if( pahsc->pahsc_IsWatchDogThreadValid ) { pahsc->pahsc_WatchDogRun = 0; DBUG(("PaHost_StopWatchDog: cancel WatchDog\n")); pthread_cancel( pahsc->pahsc_WatchDogThread ); pthread_join( pahsc->pahsc_WatchDogThread, NULL ); pahsc->pahsc_IsWatchDogThreadValid = 0; }/* Cancel Canary thread if there is one. */ if( pahsc->pahsc_IsCanaryThreadValid ) { pahsc->pahsc_CanaryRun = 0; DBUG(("PaHost_StopWatchDog: cancel Canary\n")); pthread_cancel( pahsc->pahsc_CanaryThread ); DBUG(("PaHost_StopWatchDog: join Canary\n")); pthread_join( pahsc->pahsc_CanaryThread, NULL ); pahsc->pahsc_IsCanaryThreadValid = 0; }}/*******************************************************************************************/static PaError PaHost_StartWatchDog( PaHostSoundControl *pahsc ){ int hres; PaError result = 0; /* The watch dog watches for these timer updates */ gettimeofday( &pahsc->pahsc_EntryTime, NULL ); gettimeofday( &pahsc->pahsc_CanaryTime, NULL ); /* Launch a canary thread to detect priority abuse. */ pahsc->pahsc_CanaryRun = 1; hres = pthread_create(&(pahsc->pahsc_CanaryThread), NULL /*pthread_attr_t * attr*/, (pthread_function_t)PaHost_CanaryProc, pahsc); if( hres != 0 ) { pahsc->pahsc_IsCanaryThreadValid = 0; result = paHostError; sPaHostError = hres; goto error; } pahsc->pahsc_IsCanaryThreadValid = 1; /* Launch a watchdog thread to prevent runaway audio thread. */ pahsc->pahsc_WatchDogRun = 1; hres = pthread_create(&(pahsc->pahsc_WatchDogThread), NULL /*pthread_attr_t * attr*/, (pthread_function_t)PaHost_WatchDogProc, pahsc); if( hres != 0 ) { pahsc->pahsc_IsWatchDogThreadValid = 0; result = paHostError; sPaHostError = hres; goto error; } pahsc->pahsc_IsWatchDogThreadValid = 1; return result;error: PaHost_StopWatchDog( pahsc ); return result;}/******************************************************************************************* * Bump priority of audio thread if running with superuser priveledges. * if priority bumped then launch a watchdog. */static PaError PaHost_BoostPriority( internalPortAudioStream *past ){ PaHostSoundControl *pahsc; PaError result = paNoError; struct sched_param schp = { 0 }; pahsc = (PaHostSoundControl *) past->past_DeviceData; if( pahsc == NULL ) return paInternalError; pahsc->pahsc_AudioThreadPID = getpid(); DBUG(("PaHost_BoostPriority: audio PID = %d\n", pahsc->pahsc_AudioThreadPID )); /* Choose a priority in the middle of the range. */ pahsc->pahsc_AudioPriority = (sched_get_priority_max(SCHEDULER_POLICY) - sched_get_priority_min(SCHEDULER_POLICY)) / 2; schp.sched_priority = pahsc->pahsc_AudioPriority; if (sched_setscheduler(0, SCHEDULER_POLICY, &schp) != 0) { DBUG(("PortAudio: only superuser can use real-time priority.\n")); } else { DBUG(("PortAudio: audio callback priority set to level %d!\n", schp.sched_priority)); /* We are running at high priority so we should have a watchdog in case audio goes wild. */ result = PaHost_StartWatchDog( pahsc ); } return result;}/*******************************************************************************************/static PaError Pa_AudioThreadProc( internalPortAudioStream *past ){ PaError result; PaHostSoundControl *pahsc; ssize_t bytes_read, bytes_written; pahsc = (PaHostSoundControl *) past->past_DeviceData; if( pahsc == NULL ) return paInternalError;#ifdef GNUSTEP GSRegisterCurrentThread(); /* SB20010904 */#endif result = PaHost_BoostPriority( past ); if( result < 0 ) goto error; past->past_IsActive = 1; DBUG(("entering thread.\n")); while( (past->past_StopNow == 0) && (past->past_StopSoon == 0) ) { /* Read data from device */ if(pahsc->pahsc_NativeInputBuffer) { unsigned int totalread = 0; DBUG(("Pa_AudioThreadProc: attempt to read %d bytes\n", pahsc->pahsc_BytesPerInputBuffer)); do { bytes_read = read(pahsc->pahsc_InputHandle, (char *)pahsc->pahsc_NativeInputBuffer + totalread, pahsc->pahsc_BytesPerInputBuffer - totalread); if (bytes_read < 0) { ERR_RPT(("PortAudio: read interrupted!\n")); break; } totalread += bytes_read; } while( totalread < pahsc->pahsc_BytesPerInputBuffer); } /* Convert 16 bit native data to user data and call user routine. */ DBUG(("converting...\n")); Pa_StartUsageCalculation( past ); result = Pa_CallConvertInt16( past, pahsc->pahsc_NativeInputBuffer, pahsc->pahsc_NativeOutputBuffer ); Pa_EndUsageCalculation( past ); if( result != 0) { DBUG(("hmm, Pa_CallConvertInt16() says: %d. i'm bailing.\n", result)); break; } /* Write data to device. */ if( pahsc->pahsc_NativeOutputBuffer ) { unsigned int totalwritten = 0; do { bytes_written = write(pahsc->pahsc_OutputHandle, (void *)pahsc->pahsc_NativeOutputBuffer, pahsc->pahsc_BytesPerOutputBuffer); if( bytes_written < 0 ) { ERR_RPT(("PortAudio: write interrupted!")); break; } totalwritten += bytes_written; } while( totalwritten < pahsc->pahsc_BytesPerOutputBuffer); } Pa_UpdateStreamTime(pahsc); } DBUG(("Pa_AudioThreadProc: left audio loop.\n")); past->past_IsActive = 0; PaHost_StopWatchDog( pahsc );error: DBUG(("leaving audio thread.\n"));#ifdef GNUSTEP GSUnregisterCurrentThread(); /* SB20010904 */#endif return result;}/*************************************************************************** Determine minimum number of buffers required for this host based** on minimum latency. Latency can be optionally set by user by setting** an environment variable. For example, to set latency to 200 msec, put:**** set PA_MIN_LATENCY_MSEC=200**** in the cshrc file.*/#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")int Pa_GetMinNumBuffers( int framesPerBuffer, double framesPerSecond ){ int minBuffers; int minLatencyMsec = MIN_LATENCY_MSEC; char *minLatencyText = getenv(PA_LATENCY_ENV_NAME); if( minLatencyText != NULL ) { PRINT(("PA_MIN_LATENCY_MSEC = %s\n", minLatencyText )); minLatencyMsec = atoi( minLatencyText ); if( minLatencyMsec < 1 ) minLatencyMsec = 1; else if( minLatencyMsec > 5000 ) minLatencyMsec = 5000; } minBuffers = (int) ((minLatencyMsec * framesPerSecond) / ( 1000.0 * framesPerBuffer )); if( minBuffers < 2 ) minBuffers = 2; return minBuffers;}/*******************************************************************/PaError PaHost_OpenStream( internalPortAudioStream *past ){ PaError result = paNoError; PaHostSoundControl *pahsc; unsigned int minNumBuffers; internalPortAudioDevice *pad; DBUG(("PaHost_OpenStream() called.\n" )); /* Allocate and initialize host data. */ pahsc = (PaHostSoundControl *) malloc(sizeof(PaHostSoundControl)); if( pahsc == NULL ) { result = paInsufficientMemory; goto error; } memset( pahsc, 0, sizeof(PaHostSoundControl) ); past->past_DeviceData = (void *) pahsc; pahsc->pahsc_OutputHandle = BAD_DEVICE_ID; /* No device currently opened. */ pahsc->pahsc_InputHandle = BAD_DEVICE_ID; pahsc->pahsc_IsAudioThreadValid = 0; pahsc->pahsc_IsWatchDogThreadValid = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -