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:
parent
ff6fa68b71
commit
3266394dfb
|
@ -83,6 +83,7 @@ bool g_bStarted = false;
|
|||
void *g_pWindowHandle = nullptr;
|
||||
std::string g_stateFileName;
|
||||
std::thread g_EmuThread;
|
||||
static StoppedCallbackFunc s_onStoppedCb = nullptr;
|
||||
|
||||
static std::thread g_cpu_thread;
|
||||
static bool g_requestRefreshInfo = false;
|
||||
|
@ -155,7 +156,7 @@ bool IsRunning()
|
|||
|
||||
bool IsRunningAndStarted()
|
||||
{
|
||||
return g_bStarted;
|
||||
return g_bStarted && !g_bStopping;
|
||||
}
|
||||
|
||||
bool IsRunningInCurrentThread()
|
||||
|
@ -191,8 +192,14 @@ bool Init()
|
|||
|
||||
if (g_EmuThread.joinable())
|
||||
{
|
||||
PanicAlertT("Emu Thread already running");
|
||||
return false;
|
||||
if (IsRunning())
|
||||
{
|
||||
PanicAlertT("Emu Thread already running");
|
||||
return false;
|
||||
}
|
||||
|
||||
// The Emu Thread was stopped, synchronize with it.
|
||||
g_EmuThread.join();
|
||||
}
|
||||
|
||||
g_CoreStartupParameter = _CoreParameter;
|
||||
|
@ -226,12 +233,8 @@ bool Init()
|
|||
// Called from GUI thread
|
||||
void Stop() // - Hammertime!
|
||||
{
|
||||
if (PowerPC::GetState() == PowerPC::CPU_POWERDOWN)
|
||||
{
|
||||
if (g_EmuThread.joinable())
|
||||
g_EmuThread.join();
|
||||
if (GetState() == CORE_STOPPING)
|
||||
return;
|
||||
}
|
||||
|
||||
const SCoreStartupParameter& _CoreParameter =
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter;
|
||||
|
@ -258,28 +261,6 @@ void Stop() // - Hammertime!
|
|||
|
||||
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.
|
||||
|
@ -478,6 +459,8 @@ void EmuThread()
|
|||
}
|
||||
}
|
||||
|
||||
INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping Emu thread ...").c_str());
|
||||
|
||||
// Wait for g_cpu_thread to exit
|
||||
INFO_LOG(CONSOLE, "%s", StopMessage(true, "Stopping CPU-GPU thread ...").c_str());
|
||||
|
||||
|
@ -510,6 +493,27 @@ void EmuThread()
|
|||
Wiimote::Shutdown();
|
||||
g_video_backend->Shutdown();
|
||||
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
|
||||
|
@ -740,4 +744,15 @@ void UpdateTitle()
|
|||
}
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
if (g_EmuThread.joinable())
|
||||
g_EmuThread.join();
|
||||
}
|
||||
|
||||
void SetOnStoppedCallback(StoppedCallbackFunc callback)
|
||||
{
|
||||
s_onStoppedCb = callback;
|
||||
}
|
||||
|
||||
} // Core
|
||||
|
|
|
@ -39,6 +39,7 @@ enum EState
|
|||
|
||||
bool Init();
|
||||
void Stop();
|
||||
void Shutdown();
|
||||
|
||||
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.
|
||||
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
|
||||
|
|
|
@ -425,6 +425,7 @@ CFrame::CFrame(wxFrame* parent,
|
|||
Movie::SetInputManip(TASManipFunction);
|
||||
|
||||
State::SetOnAfterLoadCallback(OnAfterLoadCallback);
|
||||
Core::SetOnStoppedCallback(OnStoppedCallback);
|
||||
|
||||
// Setup perspectives
|
||||
if (g_pCodeWindow)
|
||||
|
@ -692,6 +693,10 @@ void CFrame::OnHostMessage(wxCommandEvent& event)
|
|||
case WM_USER_STOP:
|
||||
DoStop();
|
||||
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)
|
||||
{
|
||||
if (main_frame)
|
||||
|
|
|
@ -123,6 +123,7 @@ public:
|
|||
void InitBitmaps();
|
||||
void DoPause();
|
||||
void DoStop();
|
||||
void OnStopped();
|
||||
void DoRecordingSave();
|
||||
void UpdateGUI();
|
||||
void UpdateGameList();
|
||||
|
@ -353,6 +354,7 @@ private:
|
|||
int GetCmdForHotkey(unsigned int key);
|
||||
|
||||
void OnAfterLoadCallback();
|
||||
void OnStoppedCallback();
|
||||
|
||||
// For TASInputDlg
|
||||
void TASManipFunction(SPADStatus *PadStatus, int controllerID);
|
||||
|
|
|
@ -1120,72 +1120,77 @@ void CFrame::DoStop()
|
|||
|
||||
wxBeginBusyCursor();
|
||||
BootManager::Stop();
|
||||
wxEndBusyCursor();
|
||||
confirmStop = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CFrame::OnStopped()
|
||||
{
|
||||
wxEndBusyCursor();
|
||||
|
||||
confirmStop = false;
|
||||
|
||||
#if defined(HAVE_X11) && HAVE_X11
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bDisableScreenSaver)
|
||||
X11Utils::InhibitScreensaver(X11Utils::XDisplayFromHandle(GetHandle()),
|
||||
X11Utils::XWindowFromHandle(GetHandle()), false);
|
||||
#endif
|
||||
m_RenderFrame->SetTitle(StrToWxStr(scm_rev_str));
|
||||
m_RenderFrame->SetTitle(StrToWxStr(scm_rev_str));
|
||||
|
||||
// Destroy the renderer frame when not rendering to main
|
||||
m_RenderParent->Unbind(wxEVT_SIZE, &CFrame::OnRenderParentResize, this);
|
||||
// Destroy the renderer frame when not rendering to main
|
||||
m_RenderParent->Unbind(wxEVT_SIZE, &CFrame::OnRenderParentResize, this);
|
||||
|
||||
// Keyboard
|
||||
wxTheApp->Unbind(wxEVT_KEY_DOWN, &CFrame::OnKeyDown, this);
|
||||
wxTheApp->Unbind(wxEVT_KEY_UP, &CFrame::OnKeyUp, this);
|
||||
// Keyboard
|
||||
wxTheApp->Unbind(wxEVT_KEY_DOWN, &CFrame::OnKeyDown, this);
|
||||
wxTheApp->Unbind(wxEVT_KEY_UP, &CFrame::OnKeyUp, this);
|
||||
|
||||
// Mouse
|
||||
wxTheApp->Unbind(wxEVT_RIGHT_DOWN, &CFrame::OnMouse, this);
|
||||
wxTheApp->Unbind(wxEVT_RIGHT_UP, &CFrame::OnMouse, this);
|
||||
wxTheApp->Unbind(wxEVT_MIDDLE_DOWN, &CFrame::OnMouse, this);
|
||||
wxTheApp->Unbind(wxEVT_MIDDLE_UP, &CFrame::OnMouse, this);
|
||||
wxTheApp->Unbind(wxEVT_MOTION, &CFrame::OnMouse, this);
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bHideCursor)
|
||||
m_RenderParent->SetCursor(wxNullCursor);
|
||||
DoFullscreen(false);
|
||||
if (!SConfig::GetInstance().m_LocalCoreStartupParameter.bRenderToMain)
|
||||
{
|
||||
m_RenderFrame->Destroy();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Mouse
|
||||
wxTheApp->Unbind(wxEVT_RIGHT_DOWN, &CFrame::OnMouse, this);
|
||||
wxTheApp->Unbind(wxEVT_RIGHT_UP, &CFrame::OnMouse, this);
|
||||
wxTheApp->Unbind(wxEVT_MIDDLE_DOWN, &CFrame::OnMouse, this);
|
||||
wxTheApp->Unbind(wxEVT_MIDDLE_UP, &CFrame::OnMouse, this);
|
||||
wxTheApp->Unbind(wxEVT_MOTION, &CFrame::OnMouse, this);
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bHideCursor)
|
||||
m_RenderParent->SetCursor(wxNullCursor);
|
||||
DoFullscreen(false);
|
||||
if (!SConfig::GetInstance().m_LocalCoreStartupParameter.bRenderToMain)
|
||||
{
|
||||
m_RenderFrame->Destroy();
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(__APPLE__)
|
||||
// Disable the full screen button when not in a game.
|
||||
NSView *view = (NSView *) m_RenderFrame->GetHandle();
|
||||
NSWindow *window = [view window];
|
||||
// Disable the full screen button when not in a game.
|
||||
NSView *view = (NSView *)m_RenderFrame->GetHandle();
|
||||
NSWindow *window = [view window];
|
||||
|
||||
[window setCollectionBehavior:NSWindowCollectionBehaviorDefault];
|
||||
[window setCollectionBehavior : NSWindowCollectionBehaviorDefault];
|
||||
#endif
|
||||
|
||||
// Make sure the window is not longer set to stay on top
|
||||
m_RenderFrame->SetWindowStyle(m_RenderFrame->GetWindowStyle() & ~wxSTAY_ON_TOP);
|
||||
}
|
||||
m_RenderParent = nullptr;
|
||||
|
||||
// Clean framerate indications from the status bar.
|
||||
GetStatusBar()->SetStatusText(" ", 0);
|
||||
|
||||
// Clear wiimote connection status from the status bar.
|
||||
GetStatusBar()->SetStatusText(" ", 1);
|
||||
|
||||
// If batch mode was specified on the command-line, exit now.
|
||||
if (m_bBatchMode)
|
||||
Close(true);
|
||||
|
||||
// If using auto size with render to main, reset the application size.
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bRenderToMain &&
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.bRenderWindowAutoSize)
|
||||
SetSize(SConfig::GetInstance().m_LocalCoreStartupParameter.iWidth,
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.iHeight);
|
||||
|
||||
m_GameListCtrl->Enable();
|
||||
m_GameListCtrl->Show();
|
||||
m_GameListCtrl->SetFocus();
|
||||
UpdateGUI();
|
||||
// Make sure the window is not longer set to stay on top
|
||||
m_RenderFrame->SetWindowStyle(m_RenderFrame->GetWindowStyle() & ~wxSTAY_ON_TOP);
|
||||
}
|
||||
m_RenderParent = nullptr;
|
||||
|
||||
// Clean framerate indications from the status bar.
|
||||
GetStatusBar()->SetStatusText(" ", 0);
|
||||
|
||||
// Clear wiimote connection status from the status bar.
|
||||
GetStatusBar()->SetStatusText(" ", 1);
|
||||
|
||||
// If batch mode was specified on the command-line, exit now.
|
||||
if (m_bBatchMode)
|
||||
Close(true);
|
||||
|
||||
// If using auto size with render to main, reset the application size.
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bRenderToMain &&
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.bRenderWindowAutoSize)
|
||||
SetSize(SConfig::GetInstance().m_LocalCoreStartupParameter.iWidth,
|
||||
SConfig::GetInstance().m_LocalCoreStartupParameter.iHeight);
|
||||
|
||||
m_GameListCtrl->Enable();
|
||||
m_GameListCtrl->Show();
|
||||
m_GameListCtrl->SetFocus();
|
||||
UpdateGUI();
|
||||
}
|
||||
|
||||
void CFrame::DoRecordingSave()
|
||||
|
|
|
@ -254,6 +254,7 @@ enum
|
|||
IDM_PANIC,
|
||||
IDM_KEYSTATE,
|
||||
IDM_WINDOWSIZEREQUEST,
|
||||
IDM_STOPPED,
|
||||
IDM_HOST_MESSAGE,
|
||||
|
||||
IDM_MPANEL, ID_STATUSBAR,
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "Common/Logging/LogManager.h"
|
||||
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/CoreParameter.h"
|
||||
#include "Core/Movie.h"
|
||||
#include "Core/HW/Wiimote.h"
|
||||
|
@ -455,6 +456,7 @@ int DolphinApp::OnExit()
|
|||
VideoBackend::ClearList();
|
||||
SConfig::Shutdown();
|
||||
LogManager::Shutdown();
|
||||
Core::Shutdown();
|
||||
|
||||
delete m_locale;
|
||||
|
||||
|
|
Loading…
Reference in New Issue