Refactor timing locking behavior

- Do not start CPU thread until after splash screen
    - Insure that only CPU thread can actually create/destroy the audio interfaces
This commit is contained in:
Aaron Culliney 2015-07-26 13:38:43 -07:00
parent fa1de4dee7
commit a0cadc83c6
15 changed files with 160 additions and 112 deletions

View File

@ -140,9 +140,6 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnCreate(JNIEnv *env, jobje
c_initialize_firsttime();
pthread_create(&cpu_thread_id, NULL, (void *) &cpu_thread, (void *)NULL);
#endif
sleep(1);
#warning FIXME TODO instead of problematic sleep ... need to preempt CPU thread by holding interface lock and displaying a splash screen and setting preferences/settings defaults before starting CPU
}
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeGraphicsChanged(JNIEnv *env, jobject obj, jint width, jint height) {

View File

@ -57,18 +57,16 @@ void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetSpeakerVolume(JNIEnv
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetAudioLatency(JNIEnv *env, jclass cls, jfloat latencySecs) {
LOG("native set audio latency : %f", latencySecs);
//assert(cpu_isPaused());
assert(cpu_isPaused());
audio_setLatency(latencySecs);
timing_reinitializeAudio();
}
jboolean Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetMockingboardEnabled(JNIEnv *env, jclass cls, jboolean enabled) {
LOG("native set set mockingboard enabled : %d", enabled);
//assert(cpu_isPaused());
#warning FIXME ^^^ this should be true
MB_Destroy();
if (enabled) {
MB_Initialize();
}
assert(cpu_isPaused());
MB_SetEnabled(enabled);
timing_reinitializeAudio();
return MB_ISEnabled();
}

View File

@ -177,9 +177,7 @@
[self.altSliderLabel setStringValue:[NSString stringWithFormat:@"%.0f%%", value*100]];
}
#warning HACK TODO FIXME ... refactor timing stuff
timing_toggle_cpu_speed();
timing_toggle_cpu_speed();
timing_initialize();
[self _savePrefs];
}
@ -198,9 +196,7 @@
cpu_altscale_factor = ([maxButton state] == NSOnState) ? CPU_SCALE_FASTEST : [self.altSlider doubleValue];
}
#warning HACK TODO FIXME ... refactor timing stuff
timing_toggle_cpu_speed();
timing_toggle_cpu_speed();
timing_initialize();
[self _savePrefs];
}

View File

@ -97,7 +97,9 @@
- (IBAction)toggleCPUSpeed:(id)sender
{
timing_toggle_cpu_speed();
cpu_pause();
timing_toggleCPUSpeed();
cpu_resume();
}
- (IBAction)togglePause:(id)sender
@ -117,7 +119,7 @@
if (paused)
{
[[self pauseMenuItem] setTitle:@"Resume Emulation"];
pthread_mutex_lock(&interface_mutex);
cpu_pause();
#ifdef AUDIO_ENABLED
SoundSystemPause();
#endif
@ -128,7 +130,7 @@
#ifdef AUDIO_ENABLED
SoundSystemUnpause();
#endif
pthread_mutex_unlock(&interface_mutex);
cpu_resume();
}
}

View File

@ -1689,11 +1689,12 @@ static void MB_DSUninit()
void MB_Initialize()
{
#ifdef APPLE2IX
assert(pthread_self() == cpu_thread_id);
memset(SSI263Voice, 0x0, sizeof(AudioBuffer_s)*MAX_VOICES);
#endif
if (g_bDisableDirectSoundMockingboard)
{
MockingboardVoice->bMute = true;
//MockingboardVoice->bMute = true;
g_SoundcardType = CT_Empty;
}
else
@ -1745,6 +1746,7 @@ void MB_Reinitialize()
void MB_Destroy()
{
assert(pthread_self() == cpu_thread_id);
MB_DSUninit();
for(int i=0; i<NUM_VOICES; i++)
@ -1757,6 +1759,10 @@ void MB_Destroy()
}
}
void MB_SetEnabled(bool enabled) {
g_bDisableDirectSoundMockingboard = true;
}
bool MB_ISEnabled(void) {
return (MockingboardVoice != NULL);
}

View File

@ -98,6 +98,7 @@ extern uint32_t g_uTimer1IrqCount; // DEBUG
void MB_Initialize();
void MB_Reinitialize();
void MB_Destroy();
void MB_SetEnabled(bool enabled);
bool MB_ISEnabled(void);
void MB_Reset();
void MB_InitializeIO(char *pCxRomPeripheral, unsigned int uSlot4, unsigned int uSlot5);

View File

@ -61,6 +61,7 @@ void audio_destroySoundBuffer(INOUT AudioBuffer_s **audioBuffer) {
}
bool audio_init(void) {
assert(pthread_self() == cpu_thread_id);
if (audio_isAvailable) {
return true;
}
@ -88,6 +89,7 @@ bool audio_init(void) {
}
void audio_shutdown(void) {
assert(pthread_self() == cpu_thread_id);
if (!audio_isAvailable) {
return;
}
@ -110,17 +112,7 @@ void audio_resume(void) {
}
void audio_setLatency(float latencySecs) {
#warning FIXME TODO ... dynamically changing buffer size is really heavyweight since this buffer size percolates up to speaker/mockingboard allocations ... ugh we will live with this for now ... pumping this will be a great test for Valgrind heap profiling =P
speaker_destroy();
MB_Destroy();
audio_shutdown();
audio_latencySecs = latencySecs;
audio_init();
speaker_init();
MB_Initialize();
#warning FIXME TODO ... also ugh we should have a registration mechanism of audio devices so we don't need to explicitly list them here, suggest something similar to the glnode registration stuff in video
}
float audio_getLatency(void) {

View File

@ -324,6 +324,7 @@ static unsigned int _submit_samples_buffer(const unsigned long num_channel_sampl
// speaker public API functions
void speaker_destroy(void) {
assert(pthread_self() == cpu_thread_id);
speaker_isAvailable = false;
audio_destroySoundBuffer(&speakerBuffer);
FREE(samples_buffer);
@ -331,6 +332,8 @@ void speaker_destroy(void) {
}
void speaker_init(void) {
assert(pthread_self() == cpu_thread_id);
long err = 0;
speaker_isAvailable = false;
do {
@ -479,9 +482,11 @@ GLUE_C_READ(speaker_toggle)
speaker_accessed_since_last_flush = true;
speaker_recently_active = true;
if (timing_should_auto_adjust_speed()) {
#if !defined(MOBILE_DEVICE)
if (timing_shouldAutoAdjustSpeed()) {
is_fullspeed = false;
}
#endif
if (speaker_isAvailable) {
_speaker_update(/*toggled:true*/);

View File

@ -160,7 +160,6 @@ static void _interface_plotMessageCentered(uint8_t *fb, int fb_cols, int fb_rows
static struct stat statbuf = { 0 };
static int altdrive = 0;
bool in_interface = false;
void video_plotchar(const int col, const int row, const interface_colorscheme_t cs, const uint8_t c) {
unsigned int off = row * SCANWIDTH * FONT_HEIGHT_PIXELS + col * FONT80_WIDTH_PIXELS + _INTERPOLATED_PIXEL_ADJUSTMENT_PRE;
@ -1547,11 +1546,7 @@ void c_interface_keyboard_layout()
static void *interface_thread(void *current_key)
{
pthread_mutex_lock(&interface_mutex);
#ifdef AUDIO_ENABLED
audio_pause();
#endif
in_interface = true;
cpu_pause();
switch ((__SWORD_TYPE)current_key) {
case kF1:
@ -1592,11 +1587,7 @@ static void *interface_thread(void *current_key)
break;
}
#ifdef AUDIO_ENABLED
audio_resume();
#endif
pthread_mutex_unlock(&interface_mutex);
in_interface = false;
cpu_resume();
return NULL;
}

View File

@ -26,7 +26,6 @@ typedef enum interface_colorscheme_t {
} interface_colorscheme_t;
#ifdef INTERFACE_CLASSIC
extern bool in_interface;
void video_plotchar(int col, int row, interface_colorscheme_t cs, uint8_t c);
void c_interface_begin(int current_key);
void c_interface_print(int x, int y, const interface_colorscheme_t cs, const char *s);

View File

@ -234,7 +234,7 @@ void c_keys_handle_input(int scancode, int pressed, int is_cooked)
if ((next_key >= 0)
#ifdef INTERFACE_CLASSIC
&& !in_interface
&& !cpu_isPaused()
#endif
)
{
@ -252,7 +252,9 @@ void c_keys_handle_input(int scancode, int pressed, int is_cooked)
#ifdef INTERFACE_CLASSIC
if (current_key == kF9)
{
timing_toggle_cpu_speed();
cpu_pause();
timing_toggleCPUSpeed();
cpu_resume();
if (video_backend->animation_showCPUSpeed) {
video_backend->animation_showCPUSpeed();
}
@ -287,9 +289,10 @@ void c_keys_handle_input(int scancode, int pressed, int is_cooked)
if (video_backend->animation_showCPUSpeed) {
video_backend->animation_showCPUSpeed();
}
#warning HACK TODO FIXME ... refactor timing stuff
timing_toggle_cpu_speed();
timing_toggle_cpu_speed();
cpu_pause();
timing_initialize();
cpu_resume();
break;
}
if (current_key == kF4) {
@ -316,9 +319,10 @@ void c_keys_handle_input(int scancode, int pressed, int is_cooked)
if (video_backend->animation_showCPUSpeed) {
video_backend->animation_showCPUSpeed();
}
#warning HACK TODO FIXME ... refactor timing stuff
timing_toggle_cpu_speed();
timing_toggle_cpu_speed();
cpu_pause();
timing_initialize();
cpu_resume();
break;
}
#endif

View File

@ -40,6 +40,11 @@ static unsigned int stepping_timeout = 0;
volatile bool is_debugging = false;
extern pthread_mutex_t interface_mutex;
extern pthread_cond_t cpu_thread_cond;
extern pthread_cond_t dbg_thread_cond;
#warning ^^^ HACK FIXME TODO ... debugger should not have raw access to mutex variables
#define BUF_X DEBUGGER_BUF_X
#define BUF_Y DEBUGGER_BUF_Y
#define SCREEN_X 81 // 80col + 1

View File

@ -42,6 +42,16 @@
#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
@ -59,8 +69,9 @@ static unsigned int g_dwCyclesThisFrame = 0;
// scaling and speed adjustments
#if MOBILE_DEVICE
static bool auto_adjust_speed = false;
static bool is_paused = true;
#else
static bool is_paused = false;
static bool auto_adjust_speed = true;
#endif
double cpu_scale_factor = 1.0;
@ -69,7 +80,8 @@ bool is_fullspeed = false;
bool alt_speed_enabled = false;
// misc
volatile uint8_t emul_reinitialize = 0;
volatile uint8_t emul_reinitialize = 1;
unsigned long emul_reinitialize_audio = 1UL;
pthread_t cpu_thread_id = 0;
pthread_mutex_t interface_mutex = { 0 };
pthread_cond_t dbg_thread_cond = PTHREAD_COND_INITIALIZER;
@ -145,67 +157,77 @@ static void _timing_initialize(double scale) {
#endif
}
static inline void _lock_gui_thread(void) {
if (pthread_self() != cpu_thread_id) {
pthread_mutex_lock(&interface_mutex);
}
}
static inline void _unlock_gui_thread(void) {
if (pthread_self() != cpu_thread_id) {
pthread_mutex_unlock(&interface_mutex);
}
}
void timing_initialize(void) {
_lock_gui_thread();
assert(cpu_isPaused() || (pthread_self() == cpu_thread_id));
_timing_initialize(alt_speed_enabled ? cpu_altscale_factor : cpu_scale_factor);
_unlock_gui_thread();
}
void timing_toggle_cpu_speed(void) {
_lock_gui_thread();
void timing_toggleCPUSpeed(void) {
assert(cpu_isPaused() || (pthread_self() == cpu_thread_id));
alt_speed_enabled = !alt_speed_enabled;
timing_initialize();
_unlock_gui_thread();
}
void timing_set_auto_adjust_speed(bool auto_adjust) {
_lock_gui_thread();
auto_adjust_speed = auto_adjust;
timing_initialize();
_unlock_gui_thread();
void timing_reinitializeAudio(void) {
assert(cpu_isPaused() || (pthread_self() == cpu_thread_id));
__sync_fetch_and_or(&emul_reinitialize_audio, 1UL);
}
void cpu_pause(void) {
_lock_gui_thread();
_LOCK_CPU_THREAD();
#ifdef AUDIO_ENABLED
audio_pause();
#endif
is_paused = true;
}
static void _cpu_thread_really_start(void) {
int err = 0;
if ( (err = pthread_cond_signal(&cpu_thread_cond)) ) {
ERRLOG("pthread_cond_signal : %d", err);
}
}
void cpu_resume(void) {
assert(cpu_isPaused());
is_paused = false;
static pthread_once_t onceToken = PTHREAD_ONCE_INIT;
if (onceToken == PTHREAD_ONCE_INIT) {
int err = 0;
if ( (err = pthread_once(&onceToken, _cpu_thread_really_start)) ) {
ERRLOG("pthread_once : %d", err);
}
}
#ifdef AUDIO_ENABLED
audio_resume();
_unlock_gui_thread();
#endif
_UNLOCK_CPU_THREAD();
}
bool timing_should_auto_adjust_speed(void) {
bool cpu_isPaused(void) {
return is_paused;
}
#if !MOBILE_DEVICE
bool timing_shouldAutoAdjustSpeed(void) {
double speed = alt_speed_enabled ? cpu_altscale_factor : cpu_scale_factor;
return auto_adjust_speed && (speed < CPU_SCALE_FASTEST);
}
#endif
void *cpu_thread(void *dummyptr) {
assert(pthread_self() == cpu_thread_id);
#ifdef AUDIO_ENABLED
audio_init();
speaker_init();
MB_Initialize();
#endif
reinitialize();
LOG("cpu_thread : initialized...");
struct timespec deltat;
#if !MOBILE_DEVICE
struct timespec disk_motor_time;
#endif
struct timespec t0; // the target timer
struct timespec ti, tj; // actual time samples
bool negative = false;
@ -221,13 +243,37 @@ void *cpu_thread(void *dummyptr) {
unsigned int dbg_cycles_executed = 0;
#endif
#if !TESTING
pthread_mutex_lock(&interface_mutex);
int err = 0;
LOG("cpu_thread : waiting for splash screen completion...");
pthread_cond_wait(&cpu_thread_cond, &interface_mutex);
pthread_mutex_unlock(&interface_mutex);
LOG("cpu_thread : starting...");
#endif
do
{
#ifdef AUDIO_ENABLED
bool reinit_audio = __sync_fetch_and_and(&emul_reinitialize_audio, 0UL);
if (reinit_audio) {
speaker_destroy();
MB_Destroy();
audio_shutdown();
audio_init();
speaker_init();
MB_Initialize();
}
#endif
if (emul_reinitialize) {
reinitialize();
}
LOG("cpu_thread : begin main loop ...");
clock_gettime(CLOCK_MONOTONIC, &t0);
emul_reinitialize = 1;
do {
// -LOCK----------------------------------------------------------------------------------------- SAMPLE ti
pthread_mutex_lock(&interface_mutex);
@ -321,7 +367,8 @@ void *cpu_thread(void *dummyptr) {
pthread_mutex_unlock(&interface_mutex);
// -UNLOCK--------------------------------------------------------------------------------------- SAMPLE tj
if (timing_should_auto_adjust_speed()) {
#if !MOBILE_DEVICE
if (timing_shouldAutoAdjustSpeed()) {
disk_motor_time = timespec_diff(disk6.motor_time, tj, &negative);
assert(!negative);
if (!is_fullspeed &&
@ -334,6 +381,7 @@ void *cpu_thread(void *dummyptr) {
_timing_initialize(CPU_SCALE_FASTEST);
}
}
#endif
if (!is_fullspeed) {
deltat = timespec_diff(ti, tj, &negative);
@ -384,7 +432,8 @@ void *cpu_thread(void *dummyptr) {
#endif
}
if (timing_should_auto_adjust_speed()) {
#if !MOBILE_DEVICE
if (timing_shouldAutoAdjustSpeed()) {
if (is_fullspeed && (
#ifdef AUDIO_ENABLED
speaker_isActive() ||
@ -398,11 +447,16 @@ void *cpu_thread(void *dummyptr) {
}
}
}
#endif
if (UNLIKELY(emul_reinitialize)) {
break;
}
if (UNLIKELY(emul_reinitialize_audio)) {
break;
}
if (UNLIKELY(emulator_shutting_down)) {
break;
}
@ -411,8 +465,6 @@ void *cpu_thread(void *dummyptr) {
if (UNLIKELY(emulator_shutting_down)) {
break;
}
reinitialize();
} while (1);
#ifdef AUDIO_ENABLED

View File

@ -62,10 +62,7 @@ extern double cpu_altscale_factor; // scale factor #2
extern bool is_fullspeed; // emulation in full native speed?
extern bool alt_speed_enabled;
extern pthread_t cpu_thread_id;
extern pthread_mutex_t interface_mutex;
extern pthread_cond_t cpu_thread_cond;
extern pthread_cond_t dbg_thread_cond;
extern READONLY pthread_t cpu_thread_id;
/*
* calculate the difference between two timespec structures
@ -75,23 +72,25 @@ struct timespec timespec_diff(struct timespec start, struct timespec end, bool *
/*
* toggles CPU speed between configured values
*/
void timing_toggle_cpu_speed(void);
/*
* (dis)allow automatic adjusting of CPU speed between presently configured and max (when loading disk images).
*/
void timing_set_auto_adjust_speed(bool auto_adjust);
void timing_toggleCPUSpeed(void);
#if !defined(MOBILE_DEVICE)
/*
* check whether automatic adjusting of CPU speed is configured.
*/
bool timing_should_auto_adjust_speed(void);
bool timing_shouldAutoAdjustSpeed(void);
#endif
/*
* initialize timing
*/
void timing_initialize(void);
/*
* force audio reinitialization
*/
void timing_reinitializeAudio(void);
/*
* timing/CPU thread entry point
*/
@ -107,6 +106,11 @@ void cpu_pause(void);
*/
void cpu_resume(void);
/*
* Is the CPU paused?
*/
bool cpu_isPaused(void);
/*
* checkpoints current cycle count and updates total (for timing-dependent I/O)
*/

View File

@ -185,11 +185,11 @@ static inline void _screen_to_menu(float x, float y, OUTPARM int *col, OUTPARM i
*row = 0;
}
LOG("SCREEN TO MENU : menuX:%d menuXMax:%d menuW:%d keyW:%d ... scrn:(%f,%f)->kybd:(%d,%d)", touchport.topLeftX, touchport.topLeftXMax, touchport.width, keyW, x, y, *col, *row);
//LOG("SCREEN TO MENU : menuX:%d menuXMax:%d menuW:%d keyW:%d ... scrn:(%f,%f)->kybd:(%d,%d)", touchport.topLeftX, touchport.topLeftXMax, touchport.width, keyW, x, y, *col, *row);
}
static void _increase_cpu_speed(void) {
pthread_mutex_lock(&interface_mutex);
cpu_pause();
int percent_scale = (int)round(cpu_scale_factor * 100.0);
if (percent_scale >= 100) {
@ -209,15 +209,13 @@ static void _increase_cpu_speed(void) {
video_backend->animation_showCPUSpeed();
}
#warning HACK TODO FIXME ... refactor timing stuff
timing_toggle_cpu_speed();
timing_toggle_cpu_speed();
timing_initialize();
pthread_mutex_unlock(&interface_mutex);
cpu_resume();
}
void _decrease_cpu_speed(void) {
pthread_mutex_lock(&interface_mutex);
cpu_pause();
int percent_scale = (int)round(cpu_scale_factor * 100.0);
if (cpu_scale_factor == CPU_SCALE_FASTEST) {
@ -242,11 +240,9 @@ void _decrease_cpu_speed(void) {
video_backend->animation_showCPUSpeed();
}
#warning HACK TODO FIXME ... refactor timing stuff
timing_toggle_cpu_speed();
timing_toggle_cpu_speed();
timing_initialize();
pthread_mutex_unlock(&interface_mutex);
cpu_resume();
}
static inline bool _sprout_menu(float x, float y) {