Core/Movie: Add ability to run code in Host context

EndPlayInput runs on the CPU thread so it can't directly call
UpdateWantDeterminism. PlayController also tries to ChangeDisc
from the CPU Thread which is also invalid. It now just pauses
execution and posts a request to the Host to fix it instead.

The Core itself also did dodgy things like PauseAndLock-ing
from the CPU Thread and SetState from EmuThread which have been
removed.
This commit is contained in:
EmptyChaos 2016-05-12 01:18:30 +00:00
parent e8dfc8e654
commit c1944f623b
12 changed files with 227 additions and 30 deletions

View File

@ -7,6 +7,7 @@
#include <cstdlib> #include <cstdlib>
#include <jni.h> #include <jni.h>
#include <memory> #include <memory>
#include <mutex>
#include <thread> #include <thread>
#include <android/log.h> #include <android/log.h>
#include <android/native_window_jni.h> #include <android/native_window_jni.h>
@ -65,13 +66,23 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved)
void Host_NotifyMapLoaded() {} void Host_NotifyMapLoaded() {}
void Host_RefreshDSPDebuggerWindow() {} void Host_RefreshDSPDebuggerWindow() {}
// The Core only supports using a single Host thread.
// If multiple threads want to call host functions then they need to queue
// sequentially for access.
static std::mutex s_host_identity_lock;
Common::Event updateMainFrameEvent; Common::Event updateMainFrameEvent;
static bool s_have_wm_user_stop = false; static bool s_have_wm_user_stop = false;
void Host_Message(int Id) void Host_Message(int Id)
{ {
if (Id == WM_USER_STOP) if (Id == WM_USER_JOB_DISPATCH)
{
updateMainFrameEvent.Set();
}
else if (Id == WM_USER_STOP)
{ {
s_have_wm_user_stop = true; s_have_wm_user_stop = true;
if (Core::IsRunning())
Core::QueueHostJob(&Core::Stop);
} }
} }
@ -393,15 +404,18 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestr
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_UnPauseEmulation(JNIEnv *env, jobject obj) JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_UnPauseEmulation(JNIEnv *env, jobject obj)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock);
Core::SetState(Core::CORE_RUN); Core::SetState(Core::CORE_RUN);
} }
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_PauseEmulation(JNIEnv *env, jobject obj) JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_PauseEmulation(JNIEnv *env, jobject obj)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock);
Core::SetState(Core::CORE_PAUSE); Core::SetState(Core::CORE_PAUSE);
} }
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_StopEmulation(JNIEnv *env, jobject obj) JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_StopEmulation(JNIEnv *env, jobject obj)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock);
Core::SaveScreenShot("thumb"); Core::SaveScreenShot("thumb");
Renderer::s_screenshotCompleted.WaitFor(std::chrono::seconds(2)); Renderer::s_screenshotCompleted.WaitFor(std::chrono::seconds(2));
Core::Stop(); Core::Stop();
@ -490,6 +504,7 @@ JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Supports
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveScreenShot(JNIEnv *env, jobject obj) JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveScreenShot(JNIEnv *env, jobject obj)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock);
Core::SaveScreenShot(); Core::SaveScreenShot();
} }
@ -534,11 +549,13 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetFilename(
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveState(JNIEnv *env, jobject obj, jint slot) JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveState(JNIEnv *env, jobject obj, jint slot)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock);
State::Save(slot); State::Save(slot);
} }
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_LoadState(JNIEnv *env, jobject obj, jint slot) JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_LoadState(JNIEnv *env, jobject obj, jint slot)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock);
State::Load(slot); State::Load(slot);
} }
@ -565,6 +582,7 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_CreateUserFo
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetUserDirectory(JNIEnv *env, jobject obj, jstring jDirectory) JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetUserDirectory(JNIEnv *env, jobject obj, jstring jDirectory)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock);
std::string directory = GetJString(env, jDirectory); std::string directory = GetJString(env, jDirectory);
g_set_userpath = directory; g_set_userpath = directory;
UICommon::SetUserDirectory(directory); UICommon::SetUserDirectory(directory);
@ -577,6 +595,7 @@ JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetUserDi
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetProfiling(JNIEnv *env, jobject obj, jboolean enable) JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetProfiling(JNIEnv *env, jobject obj, jboolean enable)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock);
Core::SetState(Core::CORE_PAUSE); Core::SetState(Core::CORE_PAUSE);
JitInterface::ClearCache(); JitInterface::ClearCache();
Profiler::g_ProfileBlocks = enable; Profiler::g_ProfileBlocks = enable;
@ -585,6 +604,7 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetProfiling
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteProfileResults(JNIEnv *env, jobject obj) JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteProfileResults(JNIEnv *env, jobject obj)
{ {
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);
JitInterface::WriteProfileResults(filename); JitInterface::WriteProfileResults(filename);
@ -643,6 +663,7 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestr
} }
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_RefreshWiimotes(JNIEnv *env, jobject obj) JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_RefreshWiimotes(JNIEnv *env, jobject obj)
{ {
std::lock_guard<std::mutex> guard(s_host_identity_lock);
WiimoteReal::Refresh(); WiimoteReal::Refresh();
} }
@ -656,6 +677,7 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv *
RegisterMsgAlertHandler(&MsgAlert); RegisterMsgAlertHandler(&MsgAlert);
std::unique_lock<std::mutex> guard(s_host_identity_lock);
UICommon::SetUserDirectory(g_set_userpath); UICommon::SetUserDirectory(g_set_userpath);
UICommon::Init(); UICommon::Init();
@ -676,12 +698,16 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv *
} }
while (Core::IsRunning()) while (Core::IsRunning())
{ {
guard.unlock();
updateMainFrameEvent.Wait(); updateMainFrameEvent.Wait();
guard.lock();
Core::HostDispatchJobs();
} }
} }
Core::Shutdown(); Core::Shutdown();
UICommon::Shutdown(); UICommon::Shutdown();
guard.unlock();
if (surf) if (surf)
{ {

View File

@ -83,6 +83,7 @@ enum HOST_COMM
WM_USER_STOP = 10, WM_USER_STOP = 10,
WM_USER_CREATE, WM_USER_CREATE,
WM_USER_SETCURSOR, WM_USER_SETCURSOR,
WM_USER_JOB_DISPATCH,
}; };
// Used for notification on emulation state // Used for notification on emulation state

View File

@ -5,6 +5,9 @@
#include <atomic> #include <atomic>
#include <cctype> #include <cctype>
#include <cstring> #include <cstring>
#include <mutex>
#include <queue>
#include <utility>
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
@ -102,6 +105,7 @@ void EmuThread();
static bool s_is_stopping = false; static bool s_is_stopping = false;
static bool s_hardware_initialized = false; static bool s_hardware_initialized = false;
static bool s_is_started = false; static bool s_is_started = false;
static std::atomic<bool> s_is_booting{ false };
static void* s_window_handle = nullptr; static void* s_window_handle = nullptr;
static std::string s_state_filename; static std::string s_state_filename;
static std::thread s_emu_thread; static std::thread s_emu_thread;
@ -112,6 +116,14 @@ static bool s_request_refresh_info = false;
static int s_pause_and_lock_depth = 0; static int s_pause_and_lock_depth = 0;
static bool s_is_throttler_temp_disabled = false; static bool s_is_throttler_temp_disabled = false;
struct HostJob
{
std::function<void()> job;
bool run_after_stop;
};
static std::mutex s_host_jobs_lock;
static std::queue<HostJob> s_host_jobs_queue;
#ifdef ThreadLocalStorage #ifdef ThreadLocalStorage
static ThreadLocalStorage bool tls_is_cpu_thread = false; static ThreadLocalStorage bool tls_is_cpu_thread = false;
#else #else
@ -225,6 +237,9 @@ bool Init()
s_emu_thread.join(); s_emu_thread.join();
} }
// Drain any left over jobs
HostDispatchJobs();
Core::UpdateWantDeterminism(/*initial*/ true); Core::UpdateWantDeterminism(/*initial*/ true);
INFO_LOG(OSREPORT, "Starting core = %s mode", INFO_LOG(OSREPORT, "Starting core = %s mode",
@ -260,6 +275,9 @@ void Stop() // - Hammertime!
s_is_stopping = true; s_is_stopping = true;
// Dump left over jobs
HostDispatchJobs();
Fifo::EmulatorState(false); Fifo::EmulatorState(false);
INFO_LOG(CONSOLE, "Stop [Main Thread]\t\t---- Shutting down ----"); INFO_LOG(CONSOLE, "Stop [Main Thread]\t\t---- Shutting down ----");
@ -310,6 +328,16 @@ void UndeclareAsCPUThread()
#endif #endif
} }
// For the CPU Thread only.
static void CPUSetInitialExecutionState()
{
QueueHostJob([]
{
SetState(SConfig::GetInstance().bBootToPause ? CORE_PAUSE : CORE_RUN);
Host_UpdateMainFrame();
});
}
// Create the CPU thread, which is a CPU + Video thread in Single Core mode. // Create the CPU thread, which is a CPU + Video thread in Single Core mode.
static void CpuThread() static void CpuThread()
{ {
@ -331,10 +359,20 @@ static void CpuThread()
EMM::InstallExceptionHandler(); // Let's run under memory watch EMM::InstallExceptionHandler(); // Let's run under memory watch
if (!s_state_filename.empty()) if (!s_state_filename.empty())
State::LoadAs(s_state_filename); {
// Needs to PauseAndLock the Core
// NOTE: EmuThread should have left us in CPU_STEPPING so nothing will happen
// until after the job is serviced.
QueueHostJob([]
{
// Recheck in case Movie cleared it since.
if (!s_state_filename.empty())
State::LoadAs(s_state_filename);
});
}
s_is_started = true; s_is_started = true;
CPUSetInitialExecutionState();
#ifdef USE_GDBSTUB #ifdef USE_GDBSTUB
#ifndef _WIN32 #ifndef _WIN32
@ -393,7 +431,10 @@ static void FifoPlayerThread()
{ {
PowerPC::InjectExternalCPUCore(cpu_core.get()); PowerPC::InjectExternalCPUCore(cpu_core.get());
s_is_started = true; s_is_started = true;
CPUSetInitialExecutionState();
CPU::Run(); CPU::Run();
s_is_started = false; s_is_started = false;
PowerPC::InjectExternalCPUCore(nullptr); PowerPC::InjectExternalCPUCore(nullptr);
} }
@ -427,6 +468,7 @@ static void FifoPlayerThread()
void EmuThread() void EmuThread()
{ {
const SConfig& core_parameter = SConfig::GetInstance(); const SConfig& core_parameter = SConfig::GetInstance();
s_is_booting.store(true);
Common::SetCurrentThreadName("Emuthread - Starting"); Common::SetCurrentThreadName("Emuthread - Starting");
@ -445,6 +487,7 @@ void EmuThread()
if (!g_video_backend->Initialize(s_window_handle)) if (!g_video_backend->Initialize(s_window_handle))
{ {
s_is_booting.store(false);
PanicAlert("Failed to initialize video backend!"); PanicAlert("Failed to initialize video backend!");
Host_Message(WM_USER_STOP); Host_Message(WM_USER_STOP);
return; return;
@ -459,6 +502,7 @@ void EmuThread()
if (!DSP::GetDSPEmulator()->Initialize(core_parameter.bWii, core_parameter.bDSPThread)) if (!DSP::GetDSPEmulator()->Initialize(core_parameter.bWii, core_parameter.bDSPThread))
{ {
s_is_booting.store(false);
HW::Shutdown(); HW::Shutdown();
g_video_backend->Shutdown(); g_video_backend->Shutdown();
PanicAlert("Failed to initialize DSP emulation!"); PanicAlert("Failed to initialize DSP emulation!");
@ -499,12 +543,10 @@ void EmuThread()
// The hardware is initialized. // The hardware is initialized.
s_hardware_initialized = true; s_hardware_initialized = true;
s_is_booting.store(false);
// Boot to pause or not // Set execution state to known values (CPU/FIFO/Audio Paused)
// NOTE: This violates the Host Thread requirement for SetState but we should CPU::Break();
// not race the Host because the UI should have the buttons disabled until
// Host_UpdateMainFrame enables them.
Core::SetState(core_parameter.bBootToPause ? Core::CORE_PAUSE : Core::CORE_RUN);
// Load GCM/DOL/ELF whatever ... we boot with the interpreter core // Load GCM/DOL/ELF whatever ... we boot with the interpreter core
PowerPC::SetMode(PowerPC::MODE_INTERPRETER); PowerPC::SetMode(PowerPC::MODE_INTERPRETER);
@ -640,6 +682,10 @@ void EmuThread()
void SetState(EState state) void SetState(EState state)
{ {
// State cannot be controlled until the CPU Thread is operational
if (!IsRunningAndStarted())
return;
switch (state) switch (state)
{ {
case CORE_PAUSE: case CORE_PAUSE:
@ -904,6 +950,9 @@ void Shutdown()
// on MSDN. // on MSDN.
if (s_emu_thread.joinable()) if (s_emu_thread.joinable())
s_emu_thread.join(); s_emu_thread.join();
// Make sure there's nothing left over in case we're about to exit.
HostDispatchJobs();
} }
void SetOnStoppedCallback(StoppedCallbackFunc callback) void SetOnStoppedCallback(StoppedCallbackFunc callback)
@ -937,4 +986,45 @@ void UpdateWantDeterminism(bool initial)
} }
} }
void QueueHostJob(std::function<void()> job, bool run_during_stop)
{
if (!job)
return;
bool send_message = false;
{
std::lock_guard<std::mutex> guard(s_host_jobs_lock);
send_message = s_host_jobs_queue.empty();
s_host_jobs_queue.emplace(HostJob{ std::move(job), run_during_stop });
}
// If the the queue was empty then kick the Host to come and get this job.
if (send_message)
Host_Message(WM_USER_JOB_DISPATCH);
}
void HostDispatchJobs()
{
// WARNING: This should only run on the Host Thread.
// NOTE: This function is potentially re-entrant. If a job calls
// Core::Stop for instance then we'll enter this a second time.
std::unique_lock<std::mutex> guard(s_host_jobs_lock);
while (!s_host_jobs_queue.empty())
{
HostJob job = std::move(s_host_jobs_queue.front());
s_host_jobs_queue.pop();
// NOTE: Memory ordering is important. The booting flag needs to be
// checked first because the state transition is:
// CORE_UNINITIALIZED: s_is_booting -> s_hardware_initialized
// We need to check variables in the same order as the state
// transition, otherwise we race and get transient failures.
if (!job.run_after_stop && !s_is_booting.load() && !IsRunning())
continue;
guard.unlock();
job.job();
guard.lock();
}
}
} // Core } // Core

View File

@ -11,6 +11,7 @@
#pragma once #pragma once
#include <functional>
#include <string> #include <string>
#include <vector> #include <vector>
@ -91,4 +92,19 @@ void SetOnStoppedCallback(StoppedCallbackFunc callback);
// Run on the Host thread when the factors change. [NOT THREADSAFE] // Run on the Host thread when the factors change. [NOT THREADSAFE]
void UpdateWantDeterminism(bool initial = false); void UpdateWantDeterminism(bool initial = false);
// Queue an arbitrary function to asynchronously run once on the Host thread later.
// Threadsafe. Can be called by any thread, including the Host itself.
// Jobs will be executed in RELATIVE order. If you queue 2 jobs from the same thread
// then they will be executed in the order they were queued; however, there is no
// global order guarantee across threads - jobs from other threads may execute in
// between.
// NOTE: Make sure the jobs check the global state instead of assuming everything is
// still the same as when the job was queued.
// NOTE: Jobs that are not set to run during stop will be discarded instead.
void QueueHostJob(std::function<void()> job, bool run_during_stop = false);
// Should be called periodically by the Host to run pending jobs.
// WM_USER_JOB_DISPATCH will be sent when something is added to the queue.
void HostDispatchJobs();
} // namespace } // namespace

View File

@ -478,8 +478,8 @@ static void InsertDiscCallback(u64 userdata, s64 cyclesLate)
void ChangeDisc(const std::string& newFileName) void ChangeDisc(const std::string& newFileName)
{ {
bool is_cpu = Core::IsCPUThread(); // WARNING: Can only run on Host Thread
bool was_unpaused = is_cpu ? false : Core::PauseAndLock(true); bool was_unpaused = Core::PauseAndLock(true);
std::string* _FileName = new std::string(newFileName); std::string* _FileName = new std::string(newFileName);
CoreTiming::ScheduleEvent(0, s_eject_disc); CoreTiming::ScheduleEvent(0, s_eject_disc);
CoreTiming::ScheduleEvent(500000000, s_insert_disc, (u64)_FileName); CoreTiming::ScheduleEvent(500000000, s_insert_disc, (u64)_FileName);
@ -495,8 +495,7 @@ void ChangeDisc(const std::string& newFileName)
} }
Movie::g_discChange = fileName.substr(sizeofpath); Movie::g_discChange = fileName.substr(sizeofpath);
} }
if (!is_cpu) Core::PauseAndLock(false, was_unpaused);
Core::PauseAndLock(false, was_unpaused);
} }
void SetLidOpen(bool open) void SetLidOpen(bool open)

View File

@ -102,7 +102,7 @@ bool VolumeIsValid();
// Disc detection and swapping // Disc detection and swapping
void SetDiscInside(bool _DiscInside); void SetDiscInside(bool _DiscInside);
bool IsDiscInside(); bool IsDiscInside();
void ChangeDisc(const std::string& fileName); void ChangeDisc(const std::string& fileName); // [NOT THREADSAFE] Host only
// DVD Access Functions // DVD Access Functions
bool ChangePartition(u64 offset); bool ChangePartition(u64 offset);

View File

@ -415,7 +415,7 @@ bool IsNetPlayRecording()
return s_bNetPlay; return s_bNetPlay;
} }
// NOTE: Host / CPU Thread // NOTE: Host Thread
void ChangePads(bool instantly) void ChangePads(bool instantly)
{ {
if (!Core::IsRunning()) if (!Core::IsRunning())
@ -824,7 +824,7 @@ void RecordWiimote(int wiimote, u8 *data, u8 size)
s_totalBytes = s_currentByte; s_totalBytes = s_currentByte;
} }
// NOTE: CPU / EmuThread / Host Thread // NOTE: EmuThread / Host Thread
void ReadHeader() void ReadHeader()
{ {
s_numPads = tmpHeader.numControllers; s_numPads = tmpHeader.numControllers;
@ -934,7 +934,7 @@ void DoState(PointerWrap &p)
// other variables (such as s_totalBytes and g_totalFrames) are set in LoadInput // other variables (such as s_totalBytes and g_totalFrames) are set in LoadInput
} }
// NOTE: Host / CPU Thread // NOTE: Host Thread
void LoadInput(const std::string& filename) void LoadInput(const std::string& filename)
{ {
File::IOFile t_record; File::IOFile t_record;
@ -1152,7 +1152,7 @@ void PlayController(GCPadStatus* PadStatus, int controllerID)
{ {
// This implementation assumes the disc change will only happen once. Trying to change more than that will cause // This implementation assumes the disc change will only happen once. Trying to change more than that will cause
// it to load the last disc every time. As far as i know though, there are no 3+ disc games, so this should be fine. // it to load the last disc every time. As far as i know though, there are no 3+ disc games, so this should be fine.
Core::SetState(Core::CORE_PAUSE); CPU::Break();
bool found = false; bool found = false;
std::string path; std::string path;
for (size_t i = 0; i < SConfig::GetInstance().m_ISOFolder.size(); ++i) for (size_t i = 0; i < SConfig::GetInstance().m_ISOFolder.size(); ++i)
@ -1166,8 +1166,16 @@ void PlayController(GCPadStatus* PadStatus, int controllerID)
} }
if (found) if (found)
{ {
DVDInterface::ChangeDisc(path + '/' + g_discChange); path += '/' + g_discChange;
Core::SetState(Core::CORE_RUN);
Core::QueueHostJob([=]
{
if (!Movie::IsPlayingInput())
return;
DVDInterface::ChangeDisc(path);
CPU::EnableStepping(false);
});
} }
else else
{ {
@ -1235,10 +1243,13 @@ void EndPlayInput(bool cont)
} }
else if (s_playMode != MODE_NONE) else if (s_playMode != MODE_NONE)
{ {
// We can be called by EmuThread during boot (CPU_POWERDOWN)
bool was_running = Core::IsRunningAndStarted() && !CPU::IsStepping();
if (was_running)
CPU::Break();
s_rerecords = 0; s_rerecords = 0;
s_currentByte = 0; s_currentByte = 0;
s_playMode = MODE_NONE; s_playMode = MODE_NONE;
Core::UpdateWantDeterminism();
Core::DisplayMessage("Movie End.", 2000); Core::DisplayMessage("Movie End.", 2000);
s_bRecordingFromSaveState = false; s_bRecordingFromSaveState = false;
// we don't clear these things because otherwise we can't resume playback if we load a movie state later // we don't clear these things because otherwise we can't resume playback if we load a movie state later
@ -1246,8 +1257,12 @@ void EndPlayInput(bool cont)
//delete tmpInput; //delete tmpInput;
//tmpInput = nullptr; //tmpInput = nullptr;
if (SConfig::GetInstance().m_PauseMovie) Core::QueueHostJob([=]
Core::SetState(Core::CORE_PAUSE); {
Core::UpdateWantDeterminism();
if (was_running && !SConfig::GetInstance().m_PauseMovie)
CPU::EnableStepping(false);
});
} }
} }
@ -1353,7 +1368,7 @@ void SetGraphicsConfig()
g_Config.bUseRealXFB = tmpHeader.bUseRealXFB; g_Config.bUseRealXFB = tmpHeader.bUseRealXFB;
} }
// NOTE: CPU / EmuThread / Host Thread // NOTE: EmuThread / Host Thread
void GetSettings() void GetSettings()
{ {
s_bSaveConfig = true; s_bSaveConfig = true;

View File

@ -2,6 +2,8 @@
// Licensed under GPLv2+ // Licensed under GPLv2+
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QAbstractEventDispatcher>
#include <QApplication>
#include <QMutexLocker> #include <QMutexLocker>
#include "Common/Common.h" #include "Common/Common.h"
@ -57,7 +59,15 @@ void Host::SetRenderFullscreen(bool fullscreen)
void Host_Message(int id) void Host_Message(int id)
{ {
if (id == WM_USER_STOP) if (id == WM_USER_STOP)
{
emit Host::GetInstance()->RequestStop(); emit Host::GetInstance()->RequestStop();
}
else if (id == WM_USER_JOB_DISPATCH)
{
// Just poke the main thread to get it to wake up, job dispatch
// will happen automatically before it goes back to sleep again.
QAbstractEventDispatcher::instance(qApp->thread())->wakeUp();
}
} }
void Host_UpdateTitle(const std::string& title) void Host_UpdateTitle(const std::string& title)

View File

@ -2,6 +2,7 @@
// Licensed under GPLv2+ // Licensed under GPLv2+
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <QAbstractEventDispatcher>
#include <QApplication> #include <QApplication>
#include "Core/BootManager.h" #include "Core/BootManager.h"
@ -20,6 +21,12 @@ int main(int argc, char* argv[])
UICommon::Init(); UICommon::Init();
Resources::Init(); Resources::Init();
// Whenever the event loop is about to go to sleep, dispatch the jobs
// queued in the Core first.
QObject::connect(QAbstractEventDispatcher::instance(),
&QAbstractEventDispatcher::aboutToBlock,
&app, &Core::HostDispatchJobs);
MainWindow win; MainWindow win;
win.show(); win.show();
int retval = app.exec(); int retval = app.exec();

View File

@ -101,6 +101,7 @@ bool DolphinApp::OnInit()
Bind(wxEVT_QUERY_END_SESSION, &DolphinApp::OnEndSession, this); Bind(wxEVT_QUERY_END_SESSION, &DolphinApp::OnEndSession, this);
Bind(wxEVT_END_SESSION, &DolphinApp::OnEndSession, this); Bind(wxEVT_END_SESSION, &DolphinApp::OnEndSession, this);
Bind(wxEVT_IDLE, &DolphinApp::OnIdle, this);
// Register message box and translation handlers // Register message box and translation handlers
RegisterMsgAlertHandler(&wxMsgAlert); RegisterMsgAlertHandler(&wxMsgAlert);
@ -359,6 +360,12 @@ void DolphinApp::OnFatalException()
WiimoteReal::Shutdown(); WiimoteReal::Shutdown();
} }
void DolphinApp::OnIdle(wxIdleEvent& ev)
{
ev.Skip();
Core::HostDispatchJobs();
}
// ------------ // ------------
// Talk to GUI // Talk to GUI
@ -395,6 +402,12 @@ CFrame* DolphinApp::GetCFrame()
void Host_Message(int Id) void Host_Message(int Id)
{ {
if (Id == WM_USER_JOB_DISPATCH)
{
// Trigger a wxEVT_IDLE
wxWakeUpIdle();
return;
}
wxCommandEvent event(wxEVT_HOST_COMMAND, Id); wxCommandEvent event(wxEVT_HOST_COMMAND, Id);
main_frame->GetEventHandler()->AddPendingEvent(event); main_frame->GetEventHandler()->AddPendingEvent(event);
} }

View File

@ -33,6 +33,7 @@ private:
void OnEndSession(wxCloseEvent& event); void OnEndSession(wxCloseEvent& event);
void InitLanguageSupport(); void InitLanguageSupport();
void AfterInit(); void AfterInit();
void OnIdle(wxIdleEvent&);
bool m_batch_mode = false; bool m_batch_mode = false;
bool m_confirm_stop = false; bool m_confirm_stop = false;

View File

@ -7,6 +7,7 @@
#include <cstring> #include <cstring>
#include <getopt.h> #include <getopt.h>
#include <string> #include <string>
#include <thread>
#include <unistd.h> #include <unistd.h>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
@ -36,7 +37,14 @@ class Platform
public: public:
virtual void Init() {} virtual void Init() {}
virtual void SetTitle(const std::string &title) {} virtual void SetTitle(const std::string &title) {}
virtual void MainLoop() { while(running) {} } virtual void MainLoop()
{
while (running)
{
Core::HostDispatchJobs();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
virtual void Shutdown() {} virtual void Shutdown() {}
virtual ~Platform() {} virtual ~Platform() {}
}; };
@ -50,7 +58,10 @@ static Common::Event updateMainFrameEvent;
void Host_Message(int Id) void Host_Message(int Id)
{ {
if (Id == WM_USER_STOP) if (Id == WM_USER_STOP)
{
running = false; running = false;
updateMainFrameEvent.Set();
}
} }
static void* s_window_handle = nullptr; static void* s_window_handle = nullptr;
@ -101,10 +112,13 @@ void Host_ConnectWiimote(int wm_idx, bool connect)
{ {
if (Core::IsRunning() && SConfig::GetInstance().bWii) if (Core::IsRunning() && SConfig::GetInstance().bWii)
{ {
bool was_unpaused = Core::PauseAndLock(true); Core::QueueHostJob([=]
GetUsbPointer()->AccessWiiMote(wm_idx | 0x100)->Activate(connect); {
Host_UpdateMainFrame(); bool was_unpaused = Core::PauseAndLock(true);
Core::PauseAndLock(false, was_unpaused); GetUsbPointer()->AccessWiiMote(wm_idx | 0x100)->Activate(connect);
Host_UpdateMainFrame();
Core::PauseAndLock(false, was_unpaused);
});
} }
} }
@ -270,6 +284,7 @@ class PlatformX11 : public Platform
&borderDummy, &depthDummy); &borderDummy, &depthDummy);
rendererIsFullscreen = false; rendererIsFullscreen = false;
} }
Core::HostDispatchJobs();
usleep(100000); usleep(100000);
} }
} }
@ -353,10 +368,14 @@ int main(int argc, char* argv[])
return 1; return 1;
} }
while (!Core::IsRunning()) while (!Core::IsRunning() && running)
{
Core::HostDispatchJobs();
updateMainFrameEvent.Wait(); updateMainFrameEvent.Wait();
}
platform->MainLoop(); if (running)
platform->MainLoop();
Core::Stop(); Core::Stop();
Core::Shutdown(); Core::Shutdown();