Merge pull request #11873 from AdmiralCurtiss/pause-and-lock-host

Core: Assert that only the Host thread may call PauseAndLock().
This commit is contained in:
JosJuice 2023-06-06 13:50:20 +02:00 committed by GitHub
commit 2d56daf1bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 65 additions and 32 deletions

View File

@ -74,6 +74,21 @@ ANativeWindow* s_surf;
// If multiple threads want to call host functions then they need to queue // If multiple threads want to call host functions then they need to queue
// sequentially for access. // sequentially for access.
std::mutex s_host_identity_lock; std::mutex s_host_identity_lock;
template <typename T>
struct HostThreadWrapper
{
T lock;
explicit HostThreadWrapper(auto&&... args) : lock(std::forward<decltype(args)>(args)...)
{
Core::DeclareAsHostThread();
}
HostThreadWrapper(const HostThreadWrapper& other) = delete;
HostThreadWrapper(HostThreadWrapper&& other) = delete;
HostThreadWrapper& operator=(const HostThreadWrapper& other) = delete;
HostThreadWrapper& operator=(HostThreadWrapper&& other) = delete;
~HostThreadWrapper() { Core::UndeclareAsHostThread(); }
};
Common::Event s_update_main_frame_event; Common::Event s_update_main_frame_event;
// This exists to prevent surfaces from being destroyed during the boot process, // This exists to prevent surfaces from being destroyed during the boot process,
@ -248,19 +263,19 @@ extern "C" {
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_UnPauseEmulation(JNIEnv*, JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_UnPauseEmulation(JNIEnv*,
jclass) jclass)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock); HostThreadWrapper<std::lock_guard<std::mutex>> guard(s_host_identity_lock);
Core::SetState(Core::State::Running); Core::SetState(Core::State::Running);
} }
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_PauseEmulation(JNIEnv*, jclass) JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_PauseEmulation(JNIEnv*, jclass)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock); HostThreadWrapper<std::lock_guard<std::mutex>> guard(s_host_identity_lock);
Core::SetState(Core::State::Paused); Core::SetState(Core::State::Paused);
} }
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_StopEmulation(JNIEnv*, jclass) JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_StopEmulation(JNIEnv*, jclass)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock); HostThreadWrapper<std::lock_guard<std::mutex>> guard(s_host_identity_lock);
Core::Stop(); Core::Stop();
// Kick the waiting event // Kick the waiting event
@ -303,7 +318,7 @@ JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetGitRev
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveScreenShot(JNIEnv*, jclass) JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveScreenShot(JNIEnv*, jclass)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock); HostThreadWrapper<std::lock_guard<std::mutex>> guard(s_host_identity_lock);
Core::SaveScreenShot(); Core::SaveScreenShot();
} }
@ -317,7 +332,7 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveState(JN
jint slot, jint slot,
jboolean wait) jboolean wait)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock); HostThreadWrapper<std::lock_guard<std::mutex>> guard(s_host_identity_lock);
State::Save(slot, wait); State::Save(slot, wait);
} }
@ -325,21 +340,21 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveStateAs(
jstring path, jstring path,
jboolean wait) jboolean wait)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock); HostThreadWrapper<std::lock_guard<std::mutex>> guard(s_host_identity_lock);
State::SaveAs(GetJString(env, path), wait); State::SaveAs(GetJString(env, path), wait);
} }
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_LoadState(JNIEnv*, jclass, JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_LoadState(JNIEnv*, jclass,
jint slot) jint slot)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock); HostThreadWrapper<std::lock_guard<std::mutex>> guard(s_host_identity_lock);
State::Load(slot); State::Load(slot);
} }
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_LoadStateAs(JNIEnv* env, jclass, JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_LoadStateAs(JNIEnv* env, jclass,
jstring path) jstring path)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock); HostThreadWrapper<std::lock_guard<std::mutex>> guard(s_host_identity_lock);
State::LoadAs(GetJString(env, path)); State::LoadAs(GetJString(env, path));
} }
@ -359,7 +374,7 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_utils_DirectoryInitializat
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetUserDirectory( JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetUserDirectory(
JNIEnv* env, jclass, jstring jDirectory) JNIEnv* env, jclass, jstring jDirectory)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock); HostThreadWrapper<std::lock_guard<std::mutex>> guard(s_host_identity_lock);
UICommon::SetUserDirectory(GetJString(env, jDirectory)); UICommon::SetUserDirectory(GetJString(env, jDirectory));
} }
@ -372,7 +387,7 @@ JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetUserDi
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetCacheDirectory( JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetCacheDirectory(
JNIEnv* env, jclass, jstring jDirectory) JNIEnv* env, jclass, jstring jDirectory)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock); HostThreadWrapper<std::lock_guard<std::mutex>> guard(s_host_identity_lock);
File::SetUserPath(D_CACHE_IDX, GetJString(env, jDirectory)); File::SetUserPath(D_CACHE_IDX, GetJString(env, jDirectory));
} }
@ -395,7 +410,7 @@ JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetMaxLogLev
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetProfiling(JNIEnv*, jclass, JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetProfiling(JNIEnv*, jclass,
jboolean enable) jboolean enable)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock); HostThreadWrapper<std::lock_guard<std::mutex>> guard(s_host_identity_lock);
Core::SetState(Core::State::Paused); Core::SetState(Core::State::Paused);
auto& jit_interface = Core::System::GetInstance().GetJitInterface(); auto& jit_interface = Core::System::GetInstance().GetJitInterface();
jit_interface.ClearCache(); jit_interface.ClearCache();
@ -407,7 +422,7 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetProfiling
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteProfileResults(JNIEnv*, JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteProfileResults(JNIEnv*,
jclass) jclass)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock); HostThreadWrapper<std::lock_guard<std::mutex>> guard(s_host_identity_lock);
std::string filename = File::GetUserPath(D_DUMP_IDX) + "Debug/profiler.txt"; std::string filename = File::GetUserPath(D_DUMP_IDX) + "Debug/profiler.txt";
File::CreateFullPath(filename); File::CreateFullPath(filename);
auto& jit_interface = Core::System::GetInstance().GetJitInterface(); auto& jit_interface = Core::System::GetInstance().GetJitInterface();
@ -436,14 +451,14 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestr
// If emulation continues running without a valid surface, we will probably crash, // If emulation continues running without a valid surface, we will probably crash,
// so pause emulation until we get a valid surface again. EmulationFragment handles resuming. // so pause emulation until we get a valid surface again. EmulationFragment handles resuming.
std::unique_lock host_identity_guard(s_host_identity_lock); HostThreadWrapper<std::unique_lock<std::mutex>> host_identity_guard(s_host_identity_lock);
while (s_is_booting.IsSet()) while (s_is_booting.IsSet())
{ {
// Need to wait for boot to finish before we can pause // Need to wait for boot to finish before we can pause
host_identity_guard.unlock(); host_identity_guard.lock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(1));
host_identity_guard.lock(); host_identity_guard.lock.lock();
} }
if (Core::GetState() == Core::State::Running) if (Core::GetState() == Core::State::Running)
@ -477,7 +492,7 @@ JNIEXPORT jfloat JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetGameAsp
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_RefreshWiimotes(JNIEnv*, jclass) JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_RefreshWiimotes(JNIEnv*, jclass)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock); HostThreadWrapper<std::lock_guard<std::mutex>> guard(s_host_identity_lock);
WiimoteReal::Refresh(); WiimoteReal::Refresh();
} }
@ -535,7 +550,7 @@ static float GetRenderSurfaceScale(JNIEnv* env)
static void Run(JNIEnv* env, std::unique_ptr<BootParameters>&& boot, bool riivolution) static void Run(JNIEnv* env, std::unique_ptr<BootParameters>&& boot, bool riivolution)
{ {
std::unique_lock<std::mutex> host_identity_guard(s_host_identity_lock); HostThreadWrapper<std::unique_lock<std::mutex>> host_identity_guard(s_host_identity_lock);
if (riivolution && std::holds_alternative<BootParameters::Disc>(boot->parameters)) if (riivolution && std::holds_alternative<BootParameters::Disc>(boot->parameters))
{ {
@ -566,15 +581,15 @@ static void Run(JNIEnv* env, std::unique_ptr<BootParameters>&& boot, bool riivol
while (Core::IsRunning()) while (Core::IsRunning())
{ {
host_identity_guard.unlock(); host_identity_guard.lock.unlock();
s_update_main_frame_event.Wait(); s_update_main_frame_event.Wait();
host_identity_guard.lock(); host_identity_guard.lock.lock();
Core::HostDispatchJobs(); Core::HostDispatchJobs();
} }
s_game_metadata_is_valid = false; s_game_metadata_is_valid = false;
Core::Shutdown(); Core::Shutdown();
host_identity_guard.unlock(); host_identity_guard.lock.unlock();
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),
IDCache::GetFinishEmulationActivity()); IDCache::GetFinishEmulationActivity());

View File

@ -130,6 +130,7 @@ static Common::Event s_cpu_thread_job_finished;
static thread_local bool tls_is_cpu_thread = false; static thread_local bool tls_is_cpu_thread = false;
static thread_local bool tls_is_gpu_thread = false; static thread_local bool tls_is_gpu_thread = false;
static thread_local bool tls_is_host_thread = false;
static void EmuThread(std::unique_ptr<BootParameters> boot, WindowSystemInfo wsi); static void EmuThread(std::unique_ptr<BootParameters> boot, WindowSystemInfo wsi);
@ -224,6 +225,11 @@ bool IsGPUThread()
return tls_is_gpu_thread; return tls_is_gpu_thread;
} }
bool IsHostThread()
{
return tls_is_host_thread;
}
bool WantsDeterminism() bool WantsDeterminism()
{ {
return s_wants_determinism; return s_wants_determinism;
@ -338,6 +344,16 @@ void UndeclareAsGPUThread()
tls_is_gpu_thread = false; tls_is_gpu_thread = false;
} }
void DeclareAsHostThread()
{
tls_is_host_thread = true;
}
void UndeclareAsHostThread()
{
tls_is_host_thread = false;
}
// For the CPU Thread only. // For the CPU Thread only.
static void CPUSetInitialExecutionState(bool force_paused = false) static void CPUSetInitialExecutionState(bool force_paused = false)
{ {
@ -777,6 +793,8 @@ void SaveScreenShot(std::string_view name)
static bool PauseAndLock(Core::System& system, bool do_lock, bool unpause_on_unlock) static bool PauseAndLock(Core::System& system, bool do_lock, bool unpause_on_unlock)
{ {
// WARNING: PauseAndLock is not fully threadsafe so is only valid on the Host Thread // WARNING: PauseAndLock is not fully threadsafe so is only valid on the Host Thread
ASSERT(IsHostThread());
if (!IsRunningAndStarted()) if (!IsRunningAndStarted())
return true; return true;

View File

@ -132,6 +132,8 @@ void DeclareAsCPUThread();
void UndeclareAsCPUThread(); void UndeclareAsCPUThread();
void DeclareAsGPUThread(); void DeclareAsGPUThread();
void UndeclareAsGPUThread(); void UndeclareAsGPUThread();
void DeclareAsHostThread();
void UndeclareAsHostThread();
std::string StopMessage(bool main_thread, std::string_view message); std::string StopMessage(bool main_thread, std::string_view message);
@ -140,6 +142,7 @@ bool IsRunningAndStarted(); // is running and the CPU loop has been entere
bool IsRunningInCurrentThread(); // this tells us whether we are running in the CPU thread. bool IsRunningInCurrentThread(); // this tells us whether we are running in the CPU thread.
bool IsCPUThread(); // this tells us whether we are the CPU thread. bool IsCPUThread(); // this tells us whether we are the CPU thread.
bool IsGPUThread(); bool IsGPUThread();
bool IsHostThread();
bool WantsDeterminism(); bool WantsDeterminism();

View File

@ -185,6 +185,8 @@ static std::unique_ptr<Platform> GetPlatform(const optparse::Values& options)
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
Core::DeclareAsHostThread();
auto parser = CommandLineParse::CreateParser(CommandLineParse::ParserOptions::OmitGUIOptions); auto parser = CommandLineParse::CreateParser(CommandLineParse::ParserOptions::OmitGUIOptions);
parser->add_option("-p", "--platform") parser->add_option("-p", "--platform")
.action("store") .action("store")

View File

@ -59,16 +59,6 @@ Host* Host::GetInstance()
return s_instance; return s_instance;
} }
void Host::DeclareAsHostThread()
{
tls_is_host_thread = true;
}
bool Host::IsHostThread()
{
return tls_is_host_thread;
}
void Host::SetRenderHandle(void* handle) void Host::SetRenderHandle(void* handle)
{ {
m_render_to_main = Config::Get(Config::MAIN_RENDER_TO_MAIN); m_render_to_main = Config::Get(Config::MAIN_RENDER_TO_MAIN);

View File

@ -123,7 +123,7 @@ int main(int argc, char* argv[])
} }
#endif #endif
Host::GetInstance()->DeclareAsHostThread(); Core::DeclareAsHostThread();
#ifdef __APPLE__ #ifdef __APPLE__
// On macOS, a command line option matching the format "-psn_X_XXXXXX" is passed when // On macOS, a command line option matching the format "-psn_X_XXXXXX" is passed when

View File

@ -71,7 +71,7 @@ Settings::Settings()
}); });
m_hotplug_callback_handle = g_controller_interface.RegisterDevicesChangedCallback([this] { m_hotplug_callback_handle = g_controller_interface.RegisterDevicesChangedCallback([this] {
if (Host::GetInstance()->IsHostThread()) if (Core::IsHostThread())
{ {
emit DevicesChanged(); emit DevicesChanged();
} }

View File

@ -8,6 +8,7 @@
#include <vector> #include <vector>
#include "Common/Version.h" #include "Common/Version.h"
#include "Core/Core.h"
#include "DolphinTool/Command.h" #include "DolphinTool/Command.h"
#include "DolphinTool/ConvertCommand.h" #include "DolphinTool/ConvertCommand.h"
#include "DolphinTool/HeaderCommand.h" #include "DolphinTool/HeaderCommand.h"
@ -27,6 +28,8 @@ static int PrintUsage(int code)
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
Core::DeclareAsHostThread();
if (argc < 2) if (argc < 2)
return PrintUsage(1); return PrintUsage(1);

View File

@ -6,6 +6,7 @@
#include <fmt/format.h> #include <fmt/format.h>
#include "Common/MsgHandler.h" #include "Common/MsgHandler.h"
#include "Core/Core.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
@ -24,6 +25,7 @@ int main(int argc, char** argv)
{ {
fmt::print(stderr, "Running main() from UnitTestsMain.cpp\n"); fmt::print(stderr, "Running main() from UnitTestsMain.cpp\n");
Common::RegisterMsgAlertHandler(TestMsgHandler); Common::RegisterMsgAlertHandler(TestMsgHandler);
Core::DeclareAsHostThread();
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();