forked from dolphin-emu/dolphin
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:
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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)
|
||||||
|
@@ -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);
|
||||||
|
@@ -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
|
||||||
@@ -1154,10 +1160,10 @@ void CFrame::DoStop()
|
|||||||
{
|
{
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
// Disable the full screen button when not in a game.
|
// Disable the full screen button when not in a game.
|
||||||
NSView *view = (NSView *) m_RenderFrame->GetHandle();
|
NSView *view = (NSView *)m_RenderFrame->GetHandle();
|
||||||
NSWindow *window = [view window];
|
NSWindow *window = [view window];
|
||||||
|
|
||||||
[window setCollectionBehavior:NSWindowCollectionBehaviorDefault];
|
[window setCollectionBehavior : NSWindowCollectionBehaviorDefault];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Make sure the window is not longer set to stay on top
|
// Make sure the window is not longer set to stay on top
|
||||||
@@ -1185,7 +1191,6 @@ void CFrame::DoStop()
|
|||||||
m_GameListCtrl->Show();
|
m_GameListCtrl->Show();
|
||||||
m_GameListCtrl->SetFocus();
|
m_GameListCtrl->SetFocus();
|
||||||
UpdateGUI();
|
UpdateGUI();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFrame::DoRecordingSave()
|
void CFrame::DoRecordingSave()
|
||||||
|
@@ -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,
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user