Make the emulation stop asynchronous to prevent deadlocks.

This may expose bugs which relied on the Main Thread to be suspended in the stopping state.
This commit is contained in:
Jules Blok
2014-06-20 02:43:57 +02:00
parent ff6fa68b71
commit 3266394dfb
7 changed files with 127 additions and 82 deletions

View File

@ -83,6 +83,7 @@ bool g_bStarted = false;
void *g_pWindowHandle = nullptr; void *g_pWindowHandle = nullptr;
std::string g_stateFileName; std::string g_stateFileName;
std::thread g_EmuThread; std::thread g_EmuThread;
static StoppedCallbackFunc s_onStoppedCb = nullptr;
static std::thread g_cpu_thread; static std::thread g_cpu_thread;
static bool g_requestRefreshInfo = false; static bool g_requestRefreshInfo = false;
@ -155,7 +156,7 @@ bool IsRunning()
bool IsRunningAndStarted() bool IsRunningAndStarted()
{ {
return g_bStarted; return g_bStarted && !g_bStopping;
} }
bool IsRunningInCurrentThread() bool IsRunningInCurrentThread()
@ -190,11 +191,17 @@ bool Init()
SConfig::GetInstance().m_LocalCoreStartupParameter; SConfig::GetInstance().m_LocalCoreStartupParameter;
if (g_EmuThread.joinable()) if (g_EmuThread.joinable())
{
if (IsRunning())
{ {
PanicAlertT("Emu Thread already running"); PanicAlertT("Emu Thread already running");
return false; return false;
} }
// The Emu Thread was stopped, synchronize with it.
g_EmuThread.join();
}
g_CoreStartupParameter = _CoreParameter; g_CoreStartupParameter = _CoreParameter;
INFO_LOG(OSREPORT, "Starting core = %s mode", INFO_LOG(OSREPORT, "Starting core = %s mode",
@ -226,12 +233,8 @@ bool Init()
// Called from GUI thread // Called from GUI thread
void Stop() // - Hammertime! void Stop() // - Hammertime!
{ {
if (PowerPC::GetState() == PowerPC::CPU_POWERDOWN) if (GetState() == CORE_STOPPING)
{
if (g_EmuThread.joinable())
g_EmuThread.join();
return; return;
}
const SCoreStartupParameter& _CoreParameter = const SCoreStartupParameter& _CoreParameter =
SConfig::GetInstance().m_LocalCoreStartupParameter; SConfig::GetInstance().m_LocalCoreStartupParameter;
@ -258,28 +261,6 @@ void Stop() // - Hammertime!
g_video_backend->Video_ExitLoop(); g_video_backend->Video_ExitLoop();
} }
INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping Emu thread ...").c_str());
g_EmuThread.join(); // Wait for emuthread to close.
INFO_LOG(CONSOLE, "%s", StopMessage(true, "Main Emu thread stopped").c_str());
// Clear on screen messages that haven't expired
g_video_backend->Video_ClearMessages();
// Close the trace file
Core::StopTrace();
// Reload sysconf file in order to see changes committed during emulation
if (_CoreParameter.bWii)
SConfig::GetInstance().m_SYSCONF->Reload();
INFO_LOG(CONSOLE, "Stop [Main Thread]\t\t---- Shutdown complete ----");
Movie::Shutdown();
PatchEngine::Shutdown();
g_bStopping = false;
} }
// Create the CPU thread, which is a CPU + Video thread in Single Core mode. // Create the CPU thread, which is a CPU + Video thread in Single Core mode.
@ -478,6 +459,8 @@ void EmuThread()
} }
} }
INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping Emu thread ...").c_str());
// Wait for g_cpu_thread to exit // Wait for g_cpu_thread to exit
INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping CPU-GPU thread ...").c_str()); INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping CPU-GPU thread ...").c_str());
@ -510,6 +493,27 @@ void EmuThread()
Wiimote::Shutdown(); Wiimote::Shutdown();
g_video_backend->Shutdown(); g_video_backend->Shutdown();
AudioCommon::ShutdownSoundStream(); AudioCommon::ShutdownSoundStream();
INFO_LOG(CONSOLE, "%s", StopMessage(true, "Main Emu thread stopped").c_str());
// Clear on screen messages that haven't expired
g_video_backend->Video_ClearMessages();
// Close the trace file
Core::StopTrace();
// Reload sysconf file in order to see changes committed during emulation
if (_CoreParameter.bWii)
SConfig::GetInstance().m_SYSCONF->Reload();
INFO_LOG(CONSOLE, "Stop [Video Thread]\t\t---- Shutdown complete ----");
Movie::Shutdown();
PatchEngine::Shutdown();
g_bStopping = false;
if (s_onStoppedCb)
s_onStoppedCb();
} }
// Set or get the running state // Set or get the running state
@ -740,4 +744,15 @@ void UpdateTitle()
} }
} }
void Shutdown()
{
if (g_EmuThread.joinable())
g_EmuThread.join();
}
void SetOnStoppedCallback(StoppedCallbackFunc callback)
{
s_onStoppedCb = callback;
}
} // Core } // Core

View File

@ -39,6 +39,7 @@ enum EState
bool Init(); bool Init();
void Stop(); void Stop();
void Shutdown();
std::string StopMessage(bool, std::string); std::string StopMessage(bool, std::string);
@ -81,4 +82,8 @@ void UpdateTitle();
// the return value of the first call should be passed in as the second argument of the second call. // the return value of the first call should be passed in as the second argument of the second call.
bool PauseAndLock(bool doLock, bool unpauseOnUnlock=true); bool PauseAndLock(bool doLock, bool unpauseOnUnlock=true);
// for calling back into UI code without introducing a dependency on it in core
typedef void(*StoppedCallbackFunc)(void);
void SetOnStoppedCallback(StoppedCallbackFunc callback);
} // namespace } // namespace

View File

@ -425,6 +425,7 @@ CFrame::CFrame(wxFrame* parent,
Movie::SetInputManip(TASManipFunction); Movie::SetInputManip(TASManipFunction);
State::SetOnAfterLoadCallback(OnAfterLoadCallback); State::SetOnAfterLoadCallback(OnAfterLoadCallback);
Core::SetOnStoppedCallback(OnStoppedCallback);
// Setup perspectives // Setup perspectives
if (g_pCodeWindow) if (g_pCodeWindow)
@ -692,6 +693,10 @@ void CFrame::OnHostMessage(wxCommandEvent& event)
case WM_USER_STOP: case WM_USER_STOP:
DoStop(); DoStop();
break; break;
case IDM_STOPPED:
OnStopped();
break;
} }
} }
@ -904,6 +909,16 @@ void OnAfterLoadCallback()
} }
} }
void OnStoppedCallback()
{
// warning: this gets called from the EmuThread, so we should only queue things to do on the proper thread
if (main_frame)
{
wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_STOPPED);
main_frame->GetEventHandler()->AddPendingEvent(event);
}
}
void TASManipFunction(SPADStatus *PadStatus, int controllerID) void TASManipFunction(SPADStatus *PadStatus, int controllerID)
{ {
if (main_frame) if (main_frame)

View File

@ -123,6 +123,7 @@ public:
void InitBitmaps(); void InitBitmaps();
void DoPause(); void DoPause();
void DoStop(); void DoStop();
void OnStopped();
void DoRecordingSave(); void DoRecordingSave();
void UpdateGUI(); void UpdateGUI();
void UpdateGameList(); void UpdateGameList();
@ -353,6 +354,7 @@ private:
int GetCmdForHotkey(unsigned int key); int GetCmdForHotkey(unsigned int key);
void OnAfterLoadCallback(); void OnAfterLoadCallback();
void OnStoppedCallback();
// For TASInputDlg // For TASInputDlg
void TASManipFunction(SPADStatus *PadStatus, int controllerID); void TASManipFunction(SPADStatus *PadStatus, int controllerID);

View File

@ -1120,7 +1120,13 @@ void CFrame::DoStop()
wxBeginBusyCursor(); wxBeginBusyCursor();
BootManager::Stop(); BootManager::Stop();
}
}
void CFrame::OnStopped()
{
wxEndBusyCursor(); wxEndBusyCursor();
confirmStop = false; confirmStop = false;
#if defined(HAVE_X11) && HAVE_X11 #if defined(HAVE_X11) && HAVE_X11
@ -1186,7 +1192,6 @@ void CFrame::DoStop()
m_GameListCtrl->SetFocus(); m_GameListCtrl->SetFocus();
UpdateGUI(); UpdateGUI();
} }
}
void CFrame::DoRecordingSave() void CFrame::DoRecordingSave()
{ {

View File

@ -254,6 +254,7 @@ enum
IDM_PANIC, IDM_PANIC,
IDM_KEYSTATE, IDM_KEYSTATE,
IDM_WINDOWSIZEREQUEST, IDM_WINDOWSIZEREQUEST,
IDM_STOPPED,
IDM_HOST_MESSAGE, IDM_HOST_MESSAGE,
IDM_MPANEL, ID_STATUSBAR, IDM_MPANEL, ID_STATUSBAR,

View File

@ -37,6 +37,7 @@
#include "Common/Logging/LogManager.h" #include "Common/Logging/LogManager.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/CoreParameter.h" #include "Core/CoreParameter.h"
#include "Core/Movie.h" #include "Core/Movie.h"
#include "Core/HW/Wiimote.h" #include "Core/HW/Wiimote.h"
@ -455,6 +456,7 @@ int DolphinApp::OnExit()
VideoBackend::ClearList(); VideoBackend::ClearList();
SConfig::Shutdown(); SConfig::Shutdown();
LogManager::Shutdown(); LogManager::Shutdown();
Core::Shutdown();
delete m_locale; delete m_locale;