Skip to content

Commit

Permalink
Merge pull request #515 from Armada651/threading
Browse files Browse the repository at this point in the history
Make the emulation stop asynchronous to prevent deadlocks.
  • Loading branch information
dolphin-emu-bot committed Jul 10, 2014
2 parents 15c1250 + 4df00ae commit bc655d1
Show file tree
Hide file tree
Showing 15 changed files with 174 additions and 117 deletions.
81 changes: 49 additions & 32 deletions Source/Core/Core/Core.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -155,7 +156,7 @@ bool IsRunning()

bool IsRunningAndStarted()
{
return g_bStarted;
return g_bStarted && !g_bStopping;
}

bool IsRunningInCurrentThread()
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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.
Expand Down Expand Up @@ -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());

Expand Down Expand Up @@ -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
Expand All @@ -534,15 +538,17 @@ void SetState(EState _State)

EState GetState()
{
if (g_bStopping)
return CORE_STOPPING;

if (g_bHwInit)
{
if (CCPU::IsStepping())
return CORE_PAUSE;
else if (g_bStopping)
return CORE_STOPPING;
else
return CORE_RUN;
}

return CORE_UNINITIALIZED;
}

Expand Down Expand Up @@ -740,4 +746,15 @@ void UpdateTitle()
}
}

void Shutdown()
{
if (g_EmuThread.joinable())
g_EmuThread.join();
}

void SetOnStoppedCallback(StoppedCallbackFunc callback)
{
s_onStoppedCb = callback;
}

} // Core
5 changes: 5 additions & 0 deletions Source/Core/Core/Core.h
Expand Up @@ -39,6 +39,7 @@ enum EState

bool Init();
void Stop();
void Shutdown();

std::string StopMessage(bool, std::string);

Expand Down Expand Up @@ -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
3 changes: 1 addition & 2 deletions Source/Core/Core/Debugger/Debugger_SymbolMap.cpp
Expand Up @@ -69,8 +69,7 @@ void WalkTheStack(const std::function<void(u32)>& stack_step)
// instead of "pointing ahead"
bool GetCallstack(std::vector<CallstackEntry> &output)
{
if (Core::GetState() == Core::CORE_UNINITIALIZED ||
!Memory::IsRAMAddress(PowerPC::ppcState.gpr[1]))
if (!Core::IsRunning() || !Memory::IsRAMAddress(PowerPC::ppcState.gpr[1]))
return false;

if (LR == 0)
Expand Down
4 changes: 2 additions & 2 deletions Source/Core/Core/Movie.cpp
Expand Up @@ -382,7 +382,7 @@ bool IsNetPlayRecording()

void ChangePads(bool instantly)
{
if (Core::GetState() == Core::CORE_UNINITIALIZED)
if (!Core::IsRunning())
return;

int controllers = 0;
Expand Down Expand Up @@ -444,7 +444,7 @@ bool BeginRecordingInput(int controllers)
if (SConfig::GetInstance().m_SIDevice[i] == SIDEVICE_GC_TARUKONGA)
bongos |= (1 << i);

if (Core::IsRunning())
if (Core::IsRunningAndStarted())
{
if (File::Exists(tmpStateFilename))
File::Delete(tmpStateFilename);
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/Core/State.cpp
Expand Up @@ -424,7 +424,7 @@ void LoadFileStateData(const std::string& filename, std::vector<u8>& ret_data)

void LoadAs(const std::string& filename)
{
if (Core::GetState() == Core::CORE_UNINITIALIZED)
if (!Core::IsRunning())
return;

// Stop the core while we load the state
Expand Down
12 changes: 6 additions & 6 deletions Source/Core/DolphinWX/ConfigMain.cpp
Expand Up @@ -230,7 +230,7 @@ void CConfigMain::SetSelectedTab(int tab)
// Used to restrict changing of some options while emulator is running
void CConfigMain::UpdateGUI()
{
if (Core::GetState() != Core::CORE_UNINITIALIZED)
if (Core::IsRunning())
{
// Disable the Core stuff on GeneralPage
CPUThread->Disable();
Expand Down Expand Up @@ -669,7 +669,7 @@ void CConfigMain::CreateGUIControls()

Latency->Bind(wxEVT_SPINCTRL, &CConfigMain::AudioSettingsChanged, this);

if (Core::GetState() != Core::CORE_UNINITIALIZED)
if (Core::IsRunning())
{
Latency->Disable();
BackendSelection->Disable();
Expand Down Expand Up @@ -888,7 +888,7 @@ void CConfigMain::CoreSettingsChanged(wxCommandEvent& event)
{
// Core - Basic
case ID_CPUTHREAD:
if (Core::GetState() != Core::CORE_UNINITIALIZED)
if (Core::IsRunning())
return;
SConfig::GetInstance().m_LocalCoreStartupParameter.bCPUThread = CPUThread->IsChecked();
break;
Expand Down Expand Up @@ -1099,7 +1099,7 @@ void CConfigMain::ChooseMemcardPath(std::string& strMemcard, bool isSlotA)
{
strMemcard = filename;

if (Core::GetState() != Core::CORE_UNINITIALIZED)
if (Core::IsRunning())
{
// Change memcard to the new file
ExpansionInterface::ChangeDevice(
Expand Down Expand Up @@ -1136,7 +1136,7 @@ void CConfigMain::ChooseSIDevice(wxString deviceName, int deviceNum)

SConfig::GetInstance().m_SIDevice[deviceNum] = tempType;

if (Core::GetState() != Core::CORE_UNINITIALIZED)
if (Core::IsRunning())
{
// Change plugged device! :D
SerialInterface::ChangeDevice(tempType, deviceNum);
Expand Down Expand Up @@ -1172,7 +1172,7 @@ void CConfigMain::ChooseEXIDevice(wxString deviceName, int deviceNum)

SConfig::GetInstance().m_EXIDevice[deviceNum] = tempType;

if (Core::GetState() != Core::CORE_UNINITIALIZED)
if (Core::IsRunning())
{
// Change plugged device! :D
ExpansionInterface::ChangeDevice(
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/DolphinWX/Debugger/CodeWindowFunctions.cpp
Expand Up @@ -219,7 +219,7 @@ void CCodeWindow::OnSymbolsMenu(wxCommandEvent& event)
{
Parent->ClearStatusBar();

if (Core::GetState() == Core::CORE_UNINITIALIZED) return;
if (!Core::IsRunning()) return;

std::string existing_map_file, writable_map_file;
bool map_exists = CBoot::FindMapFile(&existing_map_file,
Expand Down
35 changes: 26 additions & 9 deletions Source/Core/DolphinWX/Frame.cpp
Expand Up @@ -217,7 +217,7 @@ WXLRESULT CRenderFrame::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPa

case WM_CLOSE:
// Let Core finish initializing before accepting any WM_CLOSE messages
if (Core::GetState() == Core::CORE_UNINITIALIZED) break;
if (!Core::IsRunning()) break;
// Use default action otherwise

default:
Expand Down Expand Up @@ -352,7 +352,7 @@ CFrame::CFrame(wxFrame* parent,
, m_LogWindow(nullptr), m_LogConfigWindow(nullptr)
, m_FifoPlayerDlg(nullptr), UseDebugger(_UseDebugger)
, m_bBatchMode(_BatchMode), m_bEdit(false), m_bTabSplit(false), m_bNoDocking(false)
, m_bGameLoading(false)
, m_bGameLoading(false), m_bClosing(false)
{
for (int i = 0; i <= IDM_CODEWINDOW - IDM_LOGWINDOW; i++)
bFloatWindow[i] = false;
Expand Down Expand Up @@ -425,6 +425,7 @@ CFrame::CFrame(wxFrame* parent,
Movie::SetInputManip(TASManipFunction);

State::SetOnAfterLoadCallback(OnAfterLoadCallback);
Core::SetOnStoppedCallback(OnStoppedCallback);

// Setup perspectives
if (g_pCodeWindow)
Expand Down Expand Up @@ -535,15 +536,18 @@ void CFrame::OnActive(wxActivateEvent& event)

void CFrame::OnClose(wxCloseEvent& event)
{
m_bClosing = true;

// Before closing the window we need to shut down the emulation core.
// We'll try to close this window again once that is done.
if (Core::GetState() != Core::CORE_UNINITIALIZED)
{
DoStop();
if (Core::GetState() != Core::CORE_UNINITIALIZED)
return;
UpdateGUI();
event.Veto();
return;
}

//Stop Dolphin from saving the minimized Xpos and Ypos
// Stop Dolphin from saving the minimized Xpos and Ypos
if (main_frame->IsIconized())
main_frame->Iconize(false);

Expand Down Expand Up @@ -692,6 +696,10 @@ void CFrame::OnHostMessage(wxCommandEvent& event)
case WM_USER_STOP:
DoStop();
break;

case IDM_STOPPED:
OnStopped();
break;
}
}

Expand All @@ -714,7 +722,7 @@ void CFrame::GetRenderWindowSize(int& x, int& y, int& width, int& height)

void CFrame::OnRenderWindowSizeRequest(int width, int height)
{
if (Core::GetState() == Core::CORE_UNINITIALIZED ||
if (!Core::IsRunning() ||
!SConfig::GetInstance().m_LocalCoreStartupParameter.bRenderWindowAutoSize ||
RendererIsFullscreen() || m_RenderFrame->IsMaximized())
return;
Expand Down Expand Up @@ -904,6 +912,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)
Expand Down Expand Up @@ -1091,8 +1109,7 @@ void CFrame::OnKeyDown(wxKeyEvent& event)

void CFrame::OnKeyUp(wxKeyEvent& event)
{
if(Core::GetState() != Core::CORE_UNINITIALIZED &&
(RendererHasFocus() || TASInputHasFocus()))
if(Core::IsRunning() && (RendererHasFocus() || TASInputHasFocus()))
{
if (IsHotkey(event, HK_TOGGLE_THROTTLE))
{
Expand Down
3 changes: 3 additions & 0 deletions Source/Core/DolphinWX/Frame.h
Expand Up @@ -123,6 +123,7 @@ class CFrame : public CRenderFrame
void InitBitmaps();
void DoPause();
void DoStop();
void OnStopped();
void DoRecordingSave();
void UpdateGUI();
void UpdateGameList();
Expand Down Expand Up @@ -185,6 +186,7 @@ class CFrame : public CRenderFrame
bool m_bTabSplit;
bool m_bNoDocking;
bool m_bGameLoading;
bool m_bClosing;

std::vector<std::string> drives;

Expand Down Expand Up @@ -353,6 +355,7 @@ class CFrame : public CRenderFrame
int GetCmdForHotkey(unsigned int key);

void OnAfterLoadCallback();
void OnStoppedCallback();

// For TASInputDlg
void TASManipFunction(SPADStatus *PadStatus, int controllerID);
Expand Down

2 comments on commit bc655d1

@delroth
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This causes aborts on exit when using FifoCI:

(gdb) bt
#0  0x00007f154117dd67 in raise () from /usr/lib/libc.so.6
#1  0x00007f154117f118 in abort () from /usr/lib/libc.so.6
#2  0x00007f1541c891f5 in __gnu_cxx::__verbose_terminate_handler() ()
   from /usr/lib/libstdc++.so.6
#3  0x00007f1541c87076 in ?? () from /usr/lib/libstdc++.so.6
#4  0x00007f1541c870c1 in std::terminate() ()
   from /usr/lib/libstdc++.so.6
#5  0x000000000044459e in std::thread::~thread() ()
#6  0x00007f1541180882 in __run_exit_handlers ()
   from /usr/lib/libc.so.6
#7  0x00007f15411808d5 in exit () from /usr/lib/libc.so.6
#8  0x00007f154116a007 in __libc_start_main () from /usr/lib/libc.so.6
#9  0x0000000000431c7f in _start ()

Dolphin in NoGUI mode, using FifoPlayer's LoopReplay=false option (that might be important for shutdown purposes).

@CrossVR
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 6480100.

Please sign in to comment.