From 3266394dfbb564f69e3cd6470ad608a4db314916 Mon Sep 17 00:00:00 2001 From: Jules Blok Date: Fri, 20 Jun 2014 02:43:57 +0200 Subject: [PATCH] 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. --- Source/Core/Core/Core.cpp | 75 ++++++++++-------- Source/Core/Core/Core.h | 5 ++ Source/Core/DolphinWX/Frame.cpp | 15 ++++ Source/Core/DolphinWX/Frame.h | 2 + Source/Core/DolphinWX/FrameTools.cpp | 109 ++++++++++++++------------- Source/Core/DolphinWX/Globals.h | 1 + Source/Core/DolphinWX/Main.cpp | 2 + 7 files changed, 127 insertions(+), 82 deletions(-) diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 3fffedb7fb..245b489663 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -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 diff --git a/Source/Core/Core/Core.h b/Source/Core/Core/Core.h index 862160daf0..655c5f9301 100644 --- a/Source/Core/Core/Core.h +++ b/Source/Core/Core/Core.h @@ -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 diff --git a/Source/Core/DolphinWX/Frame.cpp b/Source/Core/DolphinWX/Frame.cpp index 5284eefd57..1807c5e53b 100644 --- a/Source/Core/DolphinWX/Frame.cpp +++ b/Source/Core/DolphinWX/Frame.cpp @@ -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) diff --git a/Source/Core/DolphinWX/Frame.h b/Source/Core/DolphinWX/Frame.h index 32292c1cb4..e32b91f918 100644 --- a/Source/Core/DolphinWX/Frame.h +++ b/Source/Core/DolphinWX/Frame.h @@ -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); diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp index 6548deb5f0..7c72aad691 100644 --- a/Source/Core/DolphinWX/FrameTools.cpp +++ b/Source/Core/DolphinWX/FrameTools.cpp @@ -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() diff --git a/Source/Core/DolphinWX/Globals.h b/Source/Core/DolphinWX/Globals.h index d51ee2c135..401f4265ad 100644 --- a/Source/Core/DolphinWX/Globals.h +++ b/Source/Core/DolphinWX/Globals.h @@ -254,6 +254,7 @@ enum IDM_PANIC, IDM_KEYSTATE, IDM_WINDOWSIZEREQUEST, + IDM_STOPPED, IDM_HOST_MESSAGE, IDM_MPANEL, ID_STATUSBAR, diff --git a/Source/Core/DolphinWX/Main.cpp b/Source/Core/DolphinWX/Main.cpp index 3643d6e5ec..0559e9b7bf 100644 --- a/Source/Core/DolphinWX/Main.cpp +++ b/Source/Core/DolphinWX/Main.cpp @@ -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;