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:
commit
2d56daf1bb
|
@ -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());
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in New Issue