Safer CPU/audio lifecycle management that appears to fix a deadlock on Kindle Fire 1st Gen

This commit is contained in:
Aaron Culliney 2015-10-02 00:11:49 -07:00
parent b4f871613d
commit aa5b9eb971
2 changed files with 63 additions and 25 deletions

View File

@ -11,6 +11,9 @@
/*
* Apple //e core sound system support. Source inspired/derived from AppleWin.
*
* 2015/10/01 AUDIO LIFECYCLE WARNING : CPU thread owns all audio, otherwise bad things may happen in system sound layer
* (OpenAL/OpenSLES/ALSA/etc)
*/
#include "common.h"
@ -26,6 +29,8 @@ AudioBackend_s *audio_backend = NULL;
//-----------------------------------------------------------------------------
long audio_createSoundBuffer(INOUT AudioBuffer_s **audioBuffer) {
// CPU thread owns audio lifecycle (see note above)
assert(pthread_self() == cpu_thread_id);
if (!audio_isAvailable) {
*audioBuffer = NULL;
@ -55,12 +60,15 @@ long audio_createSoundBuffer(INOUT AudioBuffer_s **audioBuffer) {
}
void audio_destroySoundBuffer(INOUT AudioBuffer_s **audioBuffer) {
// CPU thread owns audio lifecycle (see note above)
assert(pthread_self() == cpu_thread_id);
if (audioContext) {
audioContext->DestroySoundBuffer(audioContext, audioBuffer);
}
}
bool audio_init(void) {
// CPU thread owns audio lifecycle (see note above)
assert(pthread_self() == cpu_thread_id);
if (audio_isAvailable) {
return true;
@ -89,6 +97,7 @@ bool audio_init(void) {
}
void audio_shutdown(void) {
// CPU thread owns audio lifecycle (see note above)
assert(pthread_self() == cpu_thread_id);
if (!audio_isAvailable) {
return;
@ -98,6 +107,9 @@ void audio_shutdown(void) {
}
void audio_pause(void) {
// CPU thread owns audio lifecycle (see note above)
// Deadlock on Kindle Fire 1st Gen if audio_pause() and audio_resume() happen off CPU thread ...
assert(pthread_self() == cpu_thread_id);
if (!audio_isAvailable) {
return;
}
@ -105,6 +117,8 @@ void audio_pause(void) {
}
void audio_resume(void) {
// CPU thread owns audio lifecycle (see note above)
assert(pthread_self() == cpu_thread_id);
if (!audio_isAvailable) {
return;
}

View File

@ -42,16 +42,6 @@
#define DISK_MOTOR_QUIET_NSECS 2000000
#define _LOCK_CPU_THREAD() \
if (pthread_self() != cpu_thread_id) { \
pthread_mutex_lock(&interface_mutex); \
}
#define _UNLOCK_CPU_THREAD() \
if (pthread_self() != cpu_thread_id) { \
pthread_mutex_unlock(&interface_mutex); \
}
// VBL constants?
#define uCyclesPerLine 65 // 25 cycles of HBL & 40 cycles of HBL'
#define uVisibleLinesPerFrame (64*3) // 192
@ -68,12 +58,12 @@ static int32_t cycles_checkpoint_count = 0;
static unsigned int g_dwCyclesThisFrame = 0;
// scaling and speed adjustments
#if MOBILE_DEVICE
static bool is_paused = true;
#else
static bool is_paused = false;
#if !MOBILE_DEVICE
static bool auto_adjust_speed = true;
#endif
static bool is_paused = false;
static unsigned long _pause_spinLock = 0;
double cpu_scale_factor = 1.0;
double cpu_altscale_factor = 1.0;
bool is_fullspeed = false;
@ -82,7 +72,9 @@ bool alt_speed_enabled = false;
// misc
volatile uint8_t emul_reinitialize = 1;
#ifdef AUDIO_ENABLED
bool emul_reinitialize_audio = true;
static bool emul_reinitialize_audio = true;
static bool emul_pause_audio = false;
static bool emul_resume_audio = false;
#endif
static bool cpu_shutting_down = false;
pthread_t cpu_thread_id = 0;
@ -199,32 +191,56 @@ void timing_toggleCPUSpeed(void) {
#ifdef AUDIO_ENABLED
void timing_reinitializeAudio(void) {
assert(cpu_isPaused() || (pthread_self() == cpu_thread_id));
SPINLOCK_ACQUIRE(&_pause_spinLock);
assert(pthread_self() != cpu_thread_id);
assert(cpu_isPaused());
emul_reinitialize_audio = true;
emul_pause_audio = false;
emul_resume_audio = false;
SPINLOCK_RELINQUISH(&_pause_spinLock);
}
#endif
void cpu_pause(void) {
assert(pthread_self() != cpu_thread_id);
_LOCK_CPU_THREAD();
SPINLOCK_ACQUIRE(&_pause_spinLock);
do {
if (is_paused) {
break;
}
// CPU thread will be paused when it next tries to acquire interface_mutex
#ifdef AUDIO_ENABLED
audio_pause();
if (!emul_reinitialize_audio) {
emul_pause_audio = true;
}
#endif
is_paused = true;
pthread_mutex_lock(&interface_mutex);
is_paused = true;
} while (0);
SPINLOCK_RELINQUISH(&_pause_spinLock);
}
void cpu_resume(void) {
assert(pthread_self() != cpu_thread_id);
assert(cpu_isPaused());
is_paused = false;
SPINLOCK_ACQUIRE(&_pause_spinLock);
do {
if (!is_paused) {
break;
}
// CPU thread will be unblocked to acquire interface_mutex
#ifdef AUDIO_ENABLED
audio_resume();
if (!emul_reinitialize_audio) {
emul_resume_audio = true;
}
#endif
_UNLOCK_CPU_THREAD();
pthread_mutex_unlock(&interface_mutex);
is_paused = false;
} while (0);
SPINLOCK_RELINQUISH(&_pause_spinLock);
}
bool cpu_isPaused(void) {
@ -292,7 +308,15 @@ static void *cpu_thread(void *dummyptr) {
do {
// -LOCK----------------------------------------------------------------------------------------- SAMPLE ti
if (UNLIKELY(emul_pause_audio)) {
emul_pause_audio = false;
audio_pause();
}
pthread_mutex_lock(&interface_mutex);
if (UNLIKELY(emul_resume_audio)) {
emul_resume_audio = false;
audio_resume();
}
clock_gettime(CLOCK_MONOTONIC, &ti);
deltat = timespec_diff(t0, ti, &negative);