Qt: Restructured to remove unnecessary shutdown event and various cleanups.

This commit is contained in:
bunnei 2015-04-28 19:03:01 -04:00
parent 3dd2688785
commit e4ea133717
4 changed files with 40 additions and 90 deletions

View file

@ -28,9 +28,8 @@
#define COPYRIGHT "Copyright (C) 2013-2014 Citra Team" #define COPYRIGHT "Copyright (C) 2013-2014 Citra Team"
EmuThread::EmuThread(GRenderWindow* render_window) : EmuThread::EmuThread(GRenderWindow* render_window) :
exec_cpu_step(false), cpu_running(false), stop_run(false), render_window(render_window) { exec_step(false), running(false), stop_run(false), render_window(render_window) {
shutdown_event.Reset();
connect(this, SIGNAL(started()), render_window, SLOT(moveContext())); connect(this, SIGNAL(started()), render_window, SLOT(moveContext()));
} }
@ -42,20 +41,20 @@ void EmuThread::run() {
// next execution step // next execution step
bool was_active = false; bool was_active = false;
while (!stop_run) { while (!stop_run) {
if (cpu_running) { if (running) {
if (!was_active) if (!was_active)
emit DebugModeLeft(); emit DebugModeLeft();
Core::RunLoop(); Core::RunLoop();
was_active = cpu_running || exec_cpu_step; was_active = running || exec_step;
if (!was_active) if (!was_active)
emit DebugModeEntered(); emit DebugModeEntered();
} else if (exec_cpu_step) { } else if (exec_step) {
if (!was_active) if (!was_active)
emit DebugModeLeft(); emit DebugModeLeft();
exec_cpu_step = false; exec_step = false;
Core::SingleStep(); Core::SingleStep();
emit DebugModeEntered(); emit DebugModeEntered();
yieldCurrentThread(); yieldCurrentThread();
@ -65,40 +64,8 @@ void EmuThread::run() {
} }
render_window->moveContext(); render_window->moveContext();
shutdown_event.Set();
} }
void EmuThread::Stop() {
if (!isRunning()) {
LOG_WARNING(Frontend, "EmuThread::Stop called while emu thread wasn't running, returning...");
return;
}
stop_run = true;
// Release emu threads from any breakpoints, so that this doesn't hang forever.
Pica::g_debug_context->ClearBreakpoints();
//core::g_state = core::SYS_DIE;
// TODO: Waiting here is just a bad workaround for retarded shutdown logic.
wait(1000);
if (isRunning()) {
LOG_WARNING(Frontend, "EmuThread still running, terminating...");
quit();
// TODO: Waiting 50 seconds can be necessary if the logging subsystem has a lot of spam
// queued... This should be fixed.
wait(50000);
if (isRunning()) {
LOG_CRITICAL(Frontend, "EmuThread STILL running, something is wrong here...");
terminate();
}
}
LOG_INFO(Frontend, "EmuThread stopped");
}
// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL context. // This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL context.
// The corresponding functionality is handled in EmuThread instead // The corresponding functionality is handled in EmuThread instead
class GGLWidgetInternal : public QGLWidget class GGLWidgetInternal : public QGLWidget

View file

@ -25,66 +25,46 @@ public:
/** /**
* Start emulation (on new thread) * Start emulation (on new thread)
*
* @warning Only call when not running! * @warning Only call when not running!
*/ */
void run() override; void run() override;
/** /**
* Allow the CPU to process a single instruction (if cpu is not running) * Steps the emulation thread by a single CPU instruction (if the CPU is not already running)
*
* @note This function is thread-safe * @note This function is thread-safe
*/ */
void ExecStep() { exec_cpu_step = true; } void ExecStep() { exec_step = true; }
/** /**
* Sets whether the CPU is running * Sets whether the emulation thread is running or not
* * @param running Boolean value, set the emulation thread to running if true
* @note This function is thread-safe * @note This function is thread-safe
*/ */
void SetCpuRunning(bool running) { cpu_running = running; } void SetRunning(bool running) { this->running = running; }
/** /**
* Allow the CPU to continue processing instructions without interruption * Check if the emulation thread is running or not
* * @return True if the emulation thread is running, otherwise false
* @note This function is thread-safe * @note This function is thread-safe
*/ */
bool IsCpuRunning() { return cpu_running; } bool IsRunning() { return running; }
/** /**
* Shutdown (permantently stops) the CPU * Shutdown (permanently stops) the emulation thread
*/ */
void ShutdownCpu() { stop_run = true; }; void Shutdown() { stop_run = true; };
/**
* Waits for the CPU shutdown to complete
*/
void WaitForCpuShutdown() { shutdown_event.Wait(); }
public slots:
/**
* Stop emulation and wait for the thread to finish.
*
* @details: This function will wait a second for the thread to finish; if it hasn't finished until then, we'll terminate() it and wait another second, hoping that it will be terminated by then.
* @note: This function is thread-safe.
*/
void Stop();
private: private:
friend class GMainWindow; friend class GMainWindow;
EmuThread(GRenderWindow* render_window); EmuThread(GRenderWindow* render_window);
bool exec_cpu_step; bool exec_step;
bool cpu_running; bool running;
std::atomic<bool> stop_run; std::atomic<bool> stop_run;
GRenderWindow* render_window; GRenderWindow* render_window;
Common::Event shutdown_event;
signals: signals:
/** /**
* Emitted when the CPU has halted execution * Emitted when the CPU has halted execution

View file

@ -201,7 +201,7 @@ void DisassemblerWidget::Init()
void DisassemblerWidget::OnContinue() void DisassemblerWidget::OnContinue()
{ {
main_window.GetEmuThread()->SetCpuRunning(true); main_window.GetEmuThread()->SetRunning(true);
} }
void DisassemblerWidget::OnStep() void DisassemblerWidget::OnStep()
@ -211,13 +211,13 @@ void DisassemblerWidget::OnStep()
void DisassemblerWidget::OnStepInto() void DisassemblerWidget::OnStepInto()
{ {
main_window.GetEmuThread()->SetCpuRunning(false); main_window.GetEmuThread()->SetRunning(false);
main_window.GetEmuThread()->ExecStep(); main_window.GetEmuThread()->ExecStep();
} }
void DisassemblerWidget::OnPause() void DisassemblerWidget::OnPause()
{ {
main_window.GetEmuThread()->SetCpuRunning(false); main_window.GetEmuThread()->SetRunning(false);
// TODO: By now, the CPU might not have actually stopped... // TODO: By now, the CPU might not have actually stopped...
if (Core::g_app_core) { if (Core::g_app_core) {
@ -227,7 +227,7 @@ void DisassemblerWidget::OnPause()
void DisassemblerWidget::OnToggleStartStop() void DisassemblerWidget::OnToggleStartStop()
{ {
main_window.GetEmuThread()->SetCpuRunning(!main_window.GetEmuThread()->IsCpuRunning()); main_window.GetEmuThread()->SetRunning(!main_window.GetEmuThread()->IsRunning());
} }
void DisassemblerWidget::OnDebugModeEntered() void DisassemblerWidget::OnDebugModeEntered()
@ -235,7 +235,7 @@ void DisassemblerWidget::OnDebugModeEntered()
ARMword next_instr = Core::g_app_core->GetPC(); ARMword next_instr = Core::g_app_core->GetPC();
if (model->GetBreakPoints().IsAddressBreakPoint(next_instr)) if (model->GetBreakPoints().IsAddressBreakPoint(next_instr))
main_window.GetEmuThread()->SetCpuRunning(false); main_window.GetEmuThread()->SetRunning(false);
model->SetNextInstruction(next_instr); model->SetNextInstruction(next_instr);

View file

@ -199,10 +199,6 @@ void GMainWindow::OnDisplayTitleBars(bool show)
void GMainWindow::BootGame(std::string filename) { void GMainWindow::BootGame(std::string filename) {
LOG_INFO(Frontend, "Citra starting...\n"); LOG_INFO(Frontend, "Citra starting...\n");
// Shutdown previous session if the emu thread is still active...
if (emu_thread != nullptr)
ShutdownGame();
System::Init(render_window); System::Init(render_window);
// Load a game or die... // Load a game or die...
@ -222,30 +218,37 @@ void GMainWindow::BootGame(std::string filename) {
} }
void GMainWindow::ShutdownGame() { void GMainWindow::ShutdownGame() {
emu_thread->SetCpuRunning(false); // Shutdown the emulation thread and wait for it to complete
emu_thread->SetRunning(false);
emu_thread->ShutdownCpu(); emu_thread->Shutdown();
emu_thread->WaitForCpuShutdown(); emu_thread->wait();
emu_thread->Stop();
delete emu_thread; delete emu_thread;
emu_thread = nullptr; emu_thread = nullptr;
// Release emu threads from any breakpoints
Pica::g_debug_context->ClearBreakpoints();
// Shutdown the core emulation
System::Shutdown(); System::Shutdown();
// Update the GUI
ui.action_Start->setEnabled(true); ui.action_Start->setEnabled(true);
ui.action_Pause->setEnabled(false); ui.action_Pause->setEnabled(false);
ui.action_Stop->setEnabled(false); ui.action_Stop->setEnabled(false);
render_window->hide(); render_window->hide();
} }
void GMainWindow::OnMenuLoadFile() void GMainWindow::OnMenuLoadFile()
{ {
QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), QString(), tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.bin *.cci *.cxi)")); QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), QString(), tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.bin *.cci *.cxi)"));
if (filename.size()) if (filename.size()) {
// Shutdown previous session if the emu thread is still active...
if (emu_thread != nullptr)
ShutdownGame();
BootGame(filename.toLatin1().data()); BootGame(filename.toLatin1().data());
} }
}
void GMainWindow::OnMenuLoadSymbolMap() { void GMainWindow::OnMenuLoadSymbolMap() {
QString filename = QFileDialog::getOpenFileName(this, tr("Load Symbol Map"), QString(), tr("Symbol map (*)")); QString filename = QFileDialog::getOpenFileName(this, tr("Load Symbol Map"), QString(), tr("Symbol map (*)"));
@ -255,7 +258,7 @@ void GMainWindow::OnMenuLoadSymbolMap() {
void GMainWindow::OnStartGame() void GMainWindow::OnStartGame()
{ {
emu_thread->SetCpuRunning(true); emu_thread->SetRunning(true);
ui.action_Start->setEnabled(false); ui.action_Start->setEnabled(false);
ui.action_Pause->setEnabled(true); ui.action_Pause->setEnabled(true);
@ -264,7 +267,7 @@ void GMainWindow::OnStartGame()
void GMainWindow::OnPauseGame() void GMainWindow::OnPauseGame()
{ {
emu_thread->SetCpuRunning(false); emu_thread->SetRunning(false);
ui.action_Start->setEnabled(true); ui.action_Start->setEnabled(true);
ui.action_Pause->setEnabled(false); ui.action_Pause->setEnabled(false);