- CModel3::StopThreads was rather gracelessly killing threads while they were waiting on their semaphores, which works okay Windows but not on Mac OS-X where it was preventing Supermodel from exiting properly. Hence, have instead altered StopThreads to signal to threads that they should exit and wait until until they have all done so. This is much better and is what I should have done the first time around had I not been so lazy :-)!

- Also fixed a small race condition in WakeSoundBoardThread.  Previously if it happened to be called just before the sound board thread had finished processing a frame's worth of audio but before it had got around to waiting on the sync condition variable then the wake notification would be lost.
This commit is contained in:
Nik Henson 2012-07-23 20:35:10 +00:00
parent f1e93bcd8f
commit 108ac64de8
2 changed files with 128 additions and 48 deletions

View file

@ -2169,6 +2169,10 @@ bool CModel3::StartThreads(void)
if (notifySync == NULL)
goto ThreadError;
// Reset thread flags
pauseThreads = false;
stopThreads = false;
// Create PPC main board thread, if multi-threading GPU
if (g_Config.gpuMultiThreaded)
{
@ -2216,8 +2220,8 @@ bool CModel3::PauseThreads(void)
if (!notifyLock->Lock())
goto ThreadError;
// Wait for all threads to finish their processing
pausedThreads = true;
// Let threads know that they should pause and wait for all of them to do so
pauseThreads = true;
while (ppcBrdThreadRunning || sndBrdThreadRunning || drvBrdThreadRunning)
{
if (!notifySync->Wait(notifyLock))
@ -2245,7 +2249,7 @@ bool CModel3::ResumeThreads(void)
goto ThreadError;
// Let all threads know that they can continue running
pausedThreads = false;
pauseThreads = false;
// Leave notify critical section
if (!notifyLock->Unlock())
@ -2258,26 +2262,73 @@ ThreadError:
return false;
}
void CModel3::StopThreads(void)
bool CModel3::StopThreads(void)
{
if (!startedThreads)
return;
return true;
// If sound board thread is unsync'd then remove audio callback
if (!syncSndBrdThread)
SetAudioCallback(NULL, NULL);
// Pause threads so that can safely delete thread objects
PauseThreads();
// Enter notify critical section
if (!notifyLock->Lock())
goto ThreadError;
// Let threads know that they should pause and wait for all of them to do so
pauseThreads = true;
while (ppcBrdThreadRunning || sndBrdThreadRunning || drvBrdThreadRunning)
{
if (!notifySync->Wait(notifyLock))
goto ThreadError;
}
// Now let threads know that they should exit
stopThreads = true;
// Leave notify critical section
if (!notifyLock->Unlock())
goto ThreadError;
// Resume each thread in turn and wait for them to exit
if (ppcBrdThread != NULL)
{
if (ppcBrdThreadSync->Post())
ppcBrdThread->Wait();
}
if (sndBrdThread != NULL)
{
if (syncSndBrdThread)
{
if (sndBrdThreadSync->Post())
sndBrdThread->Wait();
}
else
{
if (WakeSoundBoardThread())
sndBrdThread->Wait();
}
}
if (drvBrdThread != NULL)
{
if (drvBrdThreadSync->Post())
drvBrdThread->Wait();
}
// Delete all thread and synchronization objects
DeleteThreadObjects();
startedThreads = false;
return true;
ThreadError:
ErrorLog("Threading error in CModel3::StopThreads: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
g_Config.multiThreaded = false;
return false;
}
void CModel3::DeleteThreadObjects(void)
{
// Delete (which in turn kills) PPC main board, sound board and drive board threads
// Note that can do so here safely because threads will always be waiting on their semaphores when this method is called
// Delete PPC main board, sound board and drive board threads
if (ppcBrdThread != NULL)
{
delete ppcBrdThread;
@ -2347,40 +2398,37 @@ int CModel3::StartMainBoardThread(void *data)
{
// Call method on CModel3 to run PPC main board thread
CModel3 *model3 = (CModel3*)data;
model3->RunMainBoardThread();
return 0;
return model3->RunMainBoardThread();
}
int CModel3::StartSoundBoardThread(void *data)
{
// Call method on CModel3 to run sound board thread (unsync'd)
CModel3 *model3 = (CModel3*)data;
model3->RunSoundBoardThread();
return 0;
return model3->RunSoundBoardThread();
}
int CModel3::StartSoundBoardThreadSyncd(void *data)
{
// Call method on CModel3 to run sound board thread (sync'd)
CModel3 *model3 = (CModel3*)data;
model3->RunSoundBoardThreadSyncd();
return 0;
return model3->RunSoundBoardThreadSyncd();
}
int CModel3::StartDriveBoardThread(void *data)
{
// Call method on CModel3 to run drive board thread
CModel3 *model3 = (CModel3*)data;
model3->RunDriveBoardThread();
return 0;
return model3->RunDriveBoardThread();
}
void CModel3::RunMainBoardThread(void)
int CModel3::RunMainBoardThread(void)
{
for (;;)
{
bool wait = true;
while (wait)
bool exit = false;
while (wait && !exit)
{
// Wait on PPC main board thread semaphore
if (!ppcBrdThreadSync->Wait())
@ -2390,8 +2438,10 @@ void CModel3::RunMainBoardThread(void)
if (!notifyLock->Lock())
goto ThreadError;
// Check threads not paused
if (!pausedThreads)
// Check threads are not being stopped or paused
if (stopThreads)
exit = true;
else if (!pauseThreads)
{
wait = false;
ppcBrdThreadRunning = true;
@ -2401,6 +2451,8 @@ void CModel3::RunMainBoardThread(void)
if (!notifyLock->Unlock())
goto ThreadError;
}
if (exit)
return 0;
// Process a single frame for PPC main board
RunMainBoardFrame();
@ -2423,6 +2475,7 @@ void CModel3::RunMainBoardThread(void)
ThreadError:
ErrorLog("Threading error in RunMainBoardThread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
g_Config.multiThreaded = false;
return 1;
}
void CModel3::AudioCallback(void *data)
@ -2432,40 +2485,47 @@ void CModel3::AudioCallback(void *data)
model3->WakeSoundBoardThread();
}
void CModel3::WakeSoundBoardThread(void)
bool CModel3::WakeSoundBoardThread(void)
{
// Enter sound board notify critical section
if (!sndBrdNotifyLock->Lock())
goto ThreadError;
// Signal to sound board thread that it should start processing again
sndBrdWakeNotify = true;
if (!sndBrdNotifySync->Signal())
goto ThreadError;
// Exit sound board notify critical section
if (!sndBrdNotifyLock->Unlock())
goto ThreadError;
return;
return true;
ThreadError:
ErrorLog("Threading error in WakeSoundBoardThread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
g_Config.multiThreaded = false;
return false;
}
void CModel3::RunSoundBoardThread(void)
int CModel3::RunSoundBoardThread(void)
{
for (;;)
{
bool wait = true;
while (wait)
bool exit = false;
while (wait && !exit)
{
// Enter sound board notify critical section
if (!sndBrdNotifyLock->Lock())
goto ThreadError;
// Wait for notification from audio callback
if (!sndBrdNotifySync->Wait(sndBrdNotifyLock))
goto ThreadError;
while (!sndBrdWakeNotify)
{
if (!sndBrdNotifySync->Wait(sndBrdNotifyLock))
goto ThreadError;
}
sndBrdWakeNotify = false;
// Exit sound board notify critical section
if (!sndBrdNotifyLock->Unlock())
@ -2475,8 +2535,10 @@ void CModel3::RunSoundBoardThread(void)
if (!notifyLock->Lock())
goto ThreadError;
// Check threads not paused
if (!pausedThreads)
// Check threads are not being stopped or paused
if (stopThreads)
exit = true;
else if (!pauseThreads)
{
wait = false;
sndBrdThreadRunning = true;
@ -2486,8 +2548,10 @@ void CModel3::RunSoundBoardThread(void)
if (!notifyLock->Unlock())
goto ThreadError;
}
if (exit)
return 0;
// Keep processing frames until paused or audio buffer is full
// Keep processing frames until pausing or audio buffer is full
while (true)
{
// Enter main notify critical section
@ -2495,7 +2559,7 @@ void CModel3::RunSoundBoardThread(void)
if (!notifyLock->Lock())
goto ThreadError;
paused = pausedThreads;
paused = pauseThreads;
// Leave main notify critical section
if (!notifyLock->Unlock())
@ -2524,14 +2588,16 @@ void CModel3::RunSoundBoardThread(void)
ThreadError:
ErrorLog("Threading error in RunSoundBoardThread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
g_Config.multiThreaded = false;
return 1;
}
void CModel3::RunSoundBoardThreadSyncd(void)
int CModel3::RunSoundBoardThreadSyncd(void)
{
for (;;)
{
bool wait = true;
while (wait)
bool exit = false;
while (wait && !exit)
{
// Wait on sound board thread semaphore
if (!sndBrdThreadSync->Wait())
@ -2541,8 +2607,10 @@ void CModel3::RunSoundBoardThreadSyncd(void)
if (!notifyLock->Lock())
goto ThreadError;
// Check threads not paused
if (!pausedThreads)
// Check threads are not being stopped or paused
if (stopThreads)
exit = true;
else if (!pauseThreads)
{
wait = false;
sndBrdThreadRunning = true;
@ -2552,6 +2620,8 @@ void CModel3::RunSoundBoardThreadSyncd(void)
if (!notifyLock->Unlock())
goto ThreadError;
}
if (exit)
return 0;
// Process a single frame for sound board
RunSoundBoardFrame();
@ -2574,14 +2644,16 @@ void CModel3::RunSoundBoardThreadSyncd(void)
ThreadError:
ErrorLog("Threading error in RunSoundBoardThreadSyncd: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
g_Config.multiThreaded = false;
return 1;
}
void CModel3::RunDriveBoardThread(void)
int CModel3::RunDriveBoardThread(void)
{
for (;;)
{
bool wait = true;
while (wait)
bool wait = true;
bool exit = false;
while (wait && !exit)
{
// Wait on drive board thread semaphore
if (!drvBrdThreadSync->Wait())
@ -2591,8 +2663,10 @@ void CModel3::RunDriveBoardThread(void)
if (!notifyLock->Lock())
goto ThreadError;
// Check threads not paused
if (!pausedThreads)
// Check threads are not being stopped or paused
if (stopThreads)
exit = true;
else if (!pauseThreads)
{
wait = false;
drvBrdThreadRunning = true;
@ -2602,6 +2676,8 @@ void CModel3::RunDriveBoardThread(void)
if (!notifyLock->Unlock())
goto ThreadError;
}
if (exit)
return 0;
// Process a single frame for drive board
RunDriveBoardFrame();
@ -2624,6 +2700,7 @@ void CModel3::RunDriveBoardThread(void)
ThreadError:
ErrorLog("Threading error in RunDriveBoardThread: %s\nSwitching back to single-threaded mode.\n", CThread::GetLastError());
g_Config.multiThreaded = false;
return 1;
}
void CModel3::Reset(void)
@ -3139,7 +3216,8 @@ CModel3::CModel3(void)
securityPtr = 0;
startedThreads = false;
pausedThreads = false;
pauseThreads = false;
stopThreads = false;
ppcBrdThread = NULL;
sndBrdThread = NULL;
drvBrdThread = NULL;

View file

@ -361,7 +361,7 @@ private:
void RunDriveBoardFrame(void); // Runs drive board for a frame
bool StartThreads(void); // Starts all threads
void StopThreads(void); // Stops all threads
bool StopThreads(void); // Stops all threads
void DeleteThreadObjects(void); // Deletes all threads and synchronization objects
static int StartMainBoardThread(void *data); // Callback to start PPC main board thread
@ -371,11 +371,11 @@ private:
static void AudioCallback(void *data); // Audio buffer callback
void WakeSoundBoardThread(void); // Used by audio callback to wake sound board thread when not sync'd with PPC thread
void RunMainBoardThread(void); // Runs PPC main board thread (sync'd in step with render thread)
void RunSoundBoardThread(void); // Runs sound board thread (unsync'd with render thread, ie at full speed)
void RunSoundBoardThreadSyncd(void); // Runs sound board thread (sync'd in step with render thread)
void RunDriveBoardThread(void); // Runs drive board thread (sync'd in step with render thread)
bool WakeSoundBoardThread(void); // Used by audio callback to wake sound board thread (when not sync'd with render thread)
int RunMainBoardThread(void); // Runs PPC main board thread (sync'd in step with render thread)
int RunSoundBoardThread(void); // Runs sound board thread (not sync'd in step with render thread, ie running at full speed)
int RunSoundBoardThreadSyncd(void); // Runs sound board thread (sync'd in step with render thread)
int RunDriveBoardThread(void); // Runs drive board thread (sync'd in step with render thread)
// Game and hardware information
const struct GameInfo *Game;
@ -421,7 +421,8 @@ private:
// Multiple threading
bool gpusReady; // True if GPUs are ready to render
bool startedThreads; // True if threads have been created and started
bool pausedThreads; // True if threads are currently paused
bool pauseThreads; // True if threads should pause
bool stopThreads; // True if threads should stop
bool syncSndBrdThread; // True if sound board thread should be sync'd in step with render thread
CThread *ppcBrdThread; // PPC main board thread
CThread *sndBrdThread; // Sound board thread
@ -430,6 +431,7 @@ private:
bool ppcBrdThreadDone; // Flag to indicate PPC main board thread has finished processing
bool sndBrdThreadRunning; // Flag to indicate sound board thread is currently processing
bool sndBrdThreadDone; // Flag to indicate sound board thread has finished processing
bool sndBrdWakeNotify; // Flag to indicate that sound board thread has been woken by audio callback (when not sync'd with render thread)
bool drvBrdThreadRunning; // Flag to indicate drive board thread is currently processing
bool drvBrdThreadDone; // Flag to indicate drive board thread has finished processing