From 1760b3bdad8ff18fa577263648f0622ec3b713e1 Mon Sep 17 00:00:00 2001 From: Aestek Date: Mon, 25 Jul 2016 00:40:15 +0200 Subject: [PATCH 1/2] Rewrite NetPlayClient input wait logic to use std::condition_variable's Instead of sleeping in NetPlayClient::GetNetPads and NetPlayClient::WiimoteUpdate, now use std::condition_variable. This allows for finer control over these blocking areas. --- Source/Core/Core/NetPlayClient.cpp | 50 +++++++++++++++++++++--------- Source/Core/Core/NetPlayClient.h | 3 ++ 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index 19c073c020..04c3c801fa 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -326,6 +326,7 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) // Trusting server for good map value (>=0 && <4) // add to pad buffer m_pad_buffer.at(map).Push(pad); + m_gc_pad_event.Set(); } break; @@ -344,6 +345,7 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet) // Trusting server for good map value (>=0 && <4) // add to Wiimote buffer m_wiimote_buffer.at(map).Push(nw); + m_wii_pad_event.Set(); } break; @@ -996,15 +998,18 @@ bool NetPlayClient::GetNetPads(const u8 pad_nb, GCPadStatus* pad_status) // Now, we either use the data pushed earlier, or wait for the // other clients to send it to us - while (!m_pad_buffer[pad_nb].Pop(*pad_status)) + while (m_pad_buffer[pad_nb].Size() == 0) { if (!m_is_running.load()) + { return false; + } - // TODO: use a condition instead of sleeping - Common::SleepCurrentThread(1); + m_gc_pad_event.Wait(); } + m_pad_buffer[pad_nb].Pop(*pad_status); + if (Movie::IsRecordingInput()) { Movie::RecordInput(pad_status, pad_nb); @@ -1042,14 +1047,19 @@ bool NetPlayClient::WiimoteUpdate(int _number, u8* data, const u8 size) } // unlock players - while (!m_wiimote_buffer[_number].Pop(nw)) + while (m_wiimote_buffer[_number].Size() == 0) { - // wait for receiving thread to push some data - Common::SleepCurrentThread(1); if (!m_is_running.load()) + { return false; + } + + // wait for receiving thread to push some data + m_wii_pad_event.Wait(); } + m_wiimote_buffer[_number].Pop(nw); + // If the reporting mode has changed, we just need to pop through the buffer, // until we reach a good input if (nw.size() != size) @@ -1057,12 +1067,19 @@ bool NetPlayClient::WiimoteUpdate(int _number, u8* data, const u8 size) u32 tries = 0; while (nw.size() != size) { - while (!m_wiimote_buffer[_number].Pop(nw)) + while (m_wiimote_buffer[_number].Size() == 0) { - Common::SleepCurrentThread(1); if (!m_is_running.load()) + { return false; + } + + // wait for receiving thread to push some data + m_wii_pad_event.Wait(); } + + m_wiimote_buffer[_number].Pop(nw); + ++tries; if (tries > m_target_buffer_size * 200 / 120) break; @@ -1083,13 +1100,12 @@ bool NetPlayClient::WiimoteUpdate(int _number, u8* data, const u8 size) // called from ---GUI--- thread and ---NETPLAY--- thread (client side) bool NetPlayClient::StopGame() { - if (!m_is_running.load()) - { - PanicAlertT("Game isn't running!"); - return false; - } - m_is_running.store(false); + + // stop waiting for input + m_gc_pad_event.Set(); + m_wii_pad_event.Set(); + NetPlay_Disable(); // stop game @@ -1104,6 +1120,12 @@ void NetPlayClient::Stop() if (!m_is_running.load()) return; + m_is_running.store(false); + + // stop waiting for input + m_gc_pad_event.Set(); + m_wii_pad_event.Set(); + // Tell the server to stop if we have a pad mapped in game. if (LocalPlayerHasControllerMapped()) SendStopGamePacket(); diff --git a/Source/Core/Core/NetPlayClient.h b/Source/Core/Core/NetPlayClient.h index 4eddc36adf..57d60c978c 100644 --- a/Source/Core/Core/NetPlayClient.h +++ b/Source/Core/Core/NetPlayClient.h @@ -14,6 +14,7 @@ #include #include #include "Common/CommonTypes.h" +#include "Common/Event.h" #include "Common/FifoQueue.h" #include "Common/TraversalClient.h" #include "Core/NetPlayProto.h" @@ -175,6 +176,8 @@ private: TraversalClient* m_traversal_client = nullptr; std::thread m_MD5_thread; bool m_should_compute_MD5 = false; + Common::Event m_gc_pad_event; + Common::Event m_wii_pad_event; u32 m_timebase_frame = 0; }; From d906462deea61e8e9285a39385e886908c1f1527 Mon Sep 17 00:00:00 2001 From: Aestek Date: Mon, 25 Jul 2016 00:43:09 +0200 Subject: [PATCH 2/2] Do not pause emulation when confirming stop when using NetPlay Pausing emulation requires to access the CPU thread, which might be blocked waiting for inputs by netplay. Accessing it in this state would cause the whole GUI to hang for set timeout (10s). --- Source/Core/DolphinWX/FrameTools.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp index 0ea5685a17..b3689e9ab5 100644 --- a/Source/Core/DolphinWX/FrameTools.cpp +++ b/Source/Core/DolphinWX/FrameTools.cpp @@ -1132,10 +1132,17 @@ void CFrame::DoStop() // Pause the state during confirmation and restore it afterwards Core::EState state = Core::GetState(); + // Do not pause if netplay is running as CPU thread might be blocked + // waiting on inputs + bool should_pause = !NetPlayDialog::GetNetPlayClient(); + // If exclusive fullscreen is not enabled then we can pause the emulation // before we've exited fullscreen. If not then we need to exit fullscreen first. - if (!RendererIsFullscreen() || !g_Config.ExclusiveFullscreenEnabled() || - SConfig::GetInstance().bRenderToMain) + should_pause = + should_pause && (!RendererIsFullscreen() || !g_Config.ExclusiveFullscreenEnabled() || + SConfig::GetInstance().bRenderToMain); + + if (should_pause) { Core::SetState(Core::CORE_PAUSE); } @@ -1149,7 +1156,9 @@ void CFrame::DoStop() HotkeyManagerEmu::Enable(true); if (Ret != wxID_YES) { - Core::SetState(state); + if (should_pause) + Core::SetState(state); + m_confirmStop = false; return; }