Files
ARMSX2/pcsx2/gui/SysThreadBase.cpp
T

368 lines
10 KiB
C++
Raw Normal View History

2009-09-08 12:08:10 +00:00
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
2009-09-08 12:08:10 +00:00
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
2009-09-08 12:08:10 +00:00
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
2009-09-08 12:08:10 +00:00
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
2009-08-28 01:20:40 +00:00
#include "PrecompiledHeader.h"
#include "System.h"
#include "SysThreads.h"
#include "CDVD/CDVDaccess.h"
// --------------------------------------------------------------------------------------
2009-10-16 03:58:29 +00:00
// SysThreadBase *External Thread* Implementations
// (Called form outside the context of this thread)
// --------------------------------------------------------------------------------------
2020-11-18 16:13:34 +00:00
SysThreadBase::SysThreadBase()
: m_ExecMode(ExecMode_NoThreadYet)
, m_ExecModeMutex()
{
}
2009-10-16 03:58:29 +00:00
void SysThreadBase::Start()
2009-10-09 15:17:53 +00:00
{
_parent::Start();
2020-11-18 16:13:34 +00:00
Sleep(1);
2020-11-18 16:13:34 +00:00
pxAssertDev((m_ExecMode == ExecMode_Closing) || (m_ExecMode == ExecMode_Closed),
"Unexpected thread status during SysThread startup.");
m_sem_event.NotifyOfWork();
2009-10-09 15:17:53 +00:00
}
2009-10-16 03:58:29 +00:00
void SysThreadBase::OnStart()
{
2020-11-18 16:13:34 +00:00
if (!pxAssertDev(m_ExecMode == ExecMode_NoThreadYet, "SysSustainableThread:Start(): Invalid execution mode"))
return;
2010-04-27 13:12:03 +00:00
m_sem_Resume.Reset();
m_sem_ChangingExecMode.Reset();
2020-11-18 16:13:34 +00:00
FrankenMutex(m_ExecModeMutex);
FrankenMutex(m_RunningLock);
2009-10-04 08:27:27 +00:00
_parent::OnStart();
}
2021-05-14 17:05:50 +02:00
// Suspends emulation and closes the emulation state at the next PS2 vsync,
2009-10-16 03:58:29 +00:00
// and returns control to the calling thread; or does nothing if the core is already suspended.
//
// Parameters:
// isNonblocking - if set to true then the function will not block for emulation suspension.
// Defaults to false if parameter is not specified. Performing non-blocking suspension
// is mostly useful for starting certain non-Emu related gui activities (improves gui
// responsiveness).
//
2009-10-09 15:17:53 +00:00
// Returns:
// The previous suspension state; true if the thread was running or false if it was
// suspended.
//
// Exceptions:
2009-10-16 03:58:29 +00:00
// CancelEvent - thrown if the thread is already in a Paused or Closing state. Because
2021-05-14 17:05:50 +02:00
// actions that pause emulation typically rely on subcomponents remaining loaded/active,
2010-04-27 13:12:03 +00:00
// Suspension must cancel itself forcefully or risk crashing whatever other action is
2009-10-16 03:58:29 +00:00
// in progress.
//
2020-11-18 16:13:34 +00:00
void SysThreadBase::Suspend(bool isBlocking)
{
2020-11-18 16:13:34 +00:00
if (!pxAssertDev(!IsSelf(), "Suspend/Resume are not allowed from this thread."))
return;
if (!IsRunning())
return;
2009-10-16 03:58:29 +00:00
// shortcut ExecMode check to avoid deadlocking on redundant calls to Suspend issued
// from Resume or OnResumeReady code.
2020-11-18 16:13:34 +00:00
if (m_ExecMode == ExecMode_Closed)
return;
{
2020-11-18 16:13:34 +00:00
ScopedLock locker(m_ExecModeMutex);
2020-11-18 16:13:34 +00:00
switch (m_ExecMode.load())
2009-10-09 15:17:53 +00:00
{
2015-11-07 10:31:48 +01:00
// Invalid thread state, nothing to suspend
case ExecMode_NoThreadYet:
// Check again -- status could have changed since above.
2020-11-18 16:13:34 +00:00
case ExecMode_Closed:
return;
case ExecMode_Pausing:
case ExecMode_Paused:
2020-11-18 16:13:34 +00:00
if (!isBlocking)
throw Exception::CancelEvent(L"Cannot suspend in non-blocking fashion: Another thread is pausing the VM state.");
2010-04-27 13:12:03 +00:00
m_ExecMode = ExecMode_Closing;
m_sem_Resume.Post();
m_sem_ChangingExecMode.Wait();
2020-11-18 16:13:34 +00:00
break;
case ExecMode_Opened:
m_ExecMode = ExecMode_Closing;
2020-11-18 16:13:34 +00:00
break;
2014-07-12 12:47:35 +02:00
case ExecMode_Closing:
2020-11-18 16:13:34 +00:00
break;
2009-10-09 15:17:53 +00:00
}
2020-11-18 16:13:34 +00:00
pxAssertDev(m_ExecMode == ExecMode_Closing, "ExecMode should be nothing other than Closing...");
m_sem_event.NotifyOfWork();
}
2009-10-16 03:58:29 +00:00
2020-11-18 16:13:34 +00:00
if (isBlocking)
2010-04-27 13:12:03 +00:00
m_RunningLock.Wait();
}
2009-10-16 03:58:29 +00:00
// Returns:
// The previous suspension state; true if the thread was running or false if it was
// closed, not running, or paused.
//
void SysThreadBase::Pause(SystemsMask systemsToTearDown, bool debug)
{
2020-11-18 16:13:34 +00:00
if (IsSelf() || !IsRunning())
return;
2009-10-09 15:17:53 +00:00
2009-10-16 03:58:29 +00:00
// shortcut ExecMode check to avoid deadlocking on redundant calls to Suspend issued
// from Resume or OnResumeReady code.
2020-11-18 16:13:34 +00:00
if ((m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused))
return;
{
2020-11-18 16:13:34 +00:00
ScopedLock locker(m_ExecModeMutex);
2009-10-16 03:58:29 +00:00
// Check again -- status could have changed since above.
2020-11-18 16:13:34 +00:00
if ((m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused))
return;
m_SystemsToTearDown.store(systemsToTearDown, std::memory_order_relaxed);
2020-11-18 16:13:34 +00:00
if (m_ExecMode == ExecMode_Opened)
m_ExecMode = ExecMode_Pausing;
2020-11-18 16:13:34 +00:00
pxAssertDev(m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing...");
2009-10-16 03:58:29 +00:00
if (debug)
OnPauseDebug();
else
OnPause();
m_sem_event.NotifyOfWork();
}
2009-10-16 03:58:29 +00:00
2009-10-17 18:21:30 +00:00
m_RunningLock.Wait();
}
2014-02-21 14:29:13 +00:00
void SysThreadBase::PauseSelf()
{
2020-11-18 16:13:34 +00:00
if (!IsSelf() || !IsRunning())
return;
2014-02-21 14:29:13 +00:00
{
2020-11-18 16:13:34 +00:00
ScopedLock locker(m_ExecModeMutex);
if (m_ExecMode == ExecMode_Opened)
2014-02-21 14:29:13 +00:00
m_ExecMode = ExecMode_Pausing;
2020-11-18 16:13:34 +00:00
2014-02-21 14:29:13 +00:00
OnPause();
m_sem_event.NotifyOfWork();
2014-02-21 14:29:13 +00:00
}
}
2020-05-02 04:53:03 +01:00
void SysThreadBase::PauseSelfDebug()
{
2020-11-18 16:13:34 +00:00
if (!IsSelf() || !IsRunning())
return;
2020-05-02 04:53:03 +01:00
{
ScopedLock locker(m_ExecModeMutex);
if (m_ExecMode == ExecMode_Opened)
m_ExecMode = ExecMode_Pausing;
OnPauseDebug();
m_sem_event.NotifyOfWork();
2020-05-02 04:53:03 +01:00
}
}
// Resumes the core execution state, or does nothing is the core is already running. If
// settings were changed, resets will be performed as needed and emulation state resumed from
// memory savestates.
//
2009-10-16 03:58:29 +00:00
// Note that this is considered a non-blocking action. Most times the state is safely resumed
// on return, but in the case of re-entrant or nested message handling the function may return
// before the thread has resumed. If you need explicit behavior tied to the completion of the
// Resume, you'll need to bind callbacks to either OnResumeReady or OnResumeInThread.
//
2009-10-17 18:21:30 +00:00
// Exceptions:
// ThreadCreationError - Insufficient system resources to create thread.
//
2009-10-16 03:58:29 +00:00
void SysThreadBase::Resume()
{
2020-11-18 16:13:34 +00:00
if (IsSelf())
return;
if (m_ExecMode == ExecMode_Opened)
return;
2009-10-16 03:58:29 +00:00
2020-11-18 16:13:34 +00:00
ScopedLock locker(m_ExecModeMutex);
2009-10-16 03:58:29 +00:00
// Implementation Note:
// The entire state coming out of a Wait is indeterminate because of user input
// and pending messages being handled. So after each call we do some seemingly redundant
// sanity checks against m_ExecMode/m_Running status, and if something doesn't feel
// right, we should abort; the user may have canceled the action before it even finished.
2020-11-18 16:13:34 +00:00
switch (m_ExecMode.load())
{
2020-11-18 16:13:34 +00:00
case ExecMode_Opened:
return;
2009-10-16 03:58:29 +00:00
case ExecMode_NoThreadYet:
{
Start();
2020-11-18 16:13:34 +00:00
if (!m_running || (m_ExecMode == ExecMode_NoThreadYet))
throw Exception::ThreadCreationError(this);
2020-11-18 16:13:34 +00:00
if (m_ExecMode == ExecMode_Opened)
return;
}
[[fallthrough]];
2009-10-16 03:58:29 +00:00
case ExecMode_Closing:
case ExecMode_Pausing:
// we need to make sure and wait for the emuThread to enter a fully suspended
// state before continuing...
2009-10-17 18:21:30 +00:00
m_RunningLock.Wait();
2020-11-18 16:13:34 +00:00
if (!m_running)
return;
if ((m_ExecMode != ExecMode_Closed) && (m_ExecMode != ExecMode_Paused))
return;
break;
2014-07-12 12:47:35 +02:00
case ExecMode_Paused:
case ExecMode_Closed:
2020-11-18 16:13:34 +00:00
break;
}
2020-11-18 16:13:34 +00:00
pxAssertDev((m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused),
"SysThreadBase is not in a closed/paused state? wtf!");
OnResumeReady();
2009-10-16 03:58:29 +00:00
m_ExecMode = ExecMode_Opened;
2010-04-27 13:12:03 +00:00
m_sem_Resume.Post();
}
// --------------------------------------------------------------------------------------
2009-10-16 03:58:29 +00:00
// SysThreadBase *Worker* Implementations
// (Called from the context of this thread only)
// --------------------------------------------------------------------------------------
void SysThreadBase::OnStartInThread()
{
2009-10-31 19:25:46 +00:00
m_RunningLock.Acquire();
_parent::OnStartInThread();
m_ExecMode = ExecMode_Closing;
}
2009-10-16 03:58:29 +00:00
void SysThreadBase::OnCleanupInThread()
{
m_ExecMode = ExecMode_NoThreadYet;
2009-10-09 15:17:53 +00:00
_parent::OnCleanupInThread();
2009-10-31 19:18:29 +00:00
m_RunningLock.Release();
}
2009-10-19 01:50:52 +00:00
void SysThreadBase::OnSuspendInThread() {}
void SysThreadBase::OnResumeInThread(SystemsMask systemsToReinstate) {}
2009-10-19 01:50:52 +00:00
// Tests for Pause and Suspend/Close requests. If the thread is trying to be paused or
// closed, it will enter a wait/holding pattern here in this method until the managing
// thread releases it. Use the return value to detect if changes to the thread's state
// may have been changed (based on the rule that other threads are not allowed to modify
// this thread's state without pausing or closing it first, to prevent race conditions).
//
// Return value:
// TRUE if the thread was paused or closed; FALSE if the thread
// continued execution unimpeded.
bool SysThreadBase::StateCheckInThread()
{
SystemsMask systemsToTearDown {}; // Used for Pausing/Paused
2020-11-18 16:13:34 +00:00
switch (m_ExecMode.load())
{
2020-11-18 16:13:34 +00:00
#ifdef PCSX2_DEVBUILD // optimize out handlers for these cases in release builds.
case ExecMode_NoThreadYet:
// threads should never have this state set while the thread is in any way
// active or alive. (for obvious reasons!!)
2020-11-18 16:13:34 +00:00
pxFailDev("Invalid execution state detected.");
return false;
#endif
2009-10-16 03:58:29 +00:00
case ExecMode_Opened:
// Other cases don't need TestCancel() because its built into the various
// threading wait/signal actions.
TestCancel();
2020-11-18 16:13:34 +00:00
return false;
// -------------------------------------
case ExecMode_Pausing:
{
systemsToTearDown = m_SystemsToTearDown.exchange({}, std::memory_order_relaxed);
OnPauseInThread(systemsToTearDown);
m_ExecMode = ExecMode_Paused;
2009-10-31 19:18:29 +00:00
m_RunningLock.Release();
}
[[fallthrough]];
case ExecMode_Paused:
2020-11-18 16:13:34 +00:00
while (m_ExecMode == ExecMode_Paused)
2010-04-27 13:12:03 +00:00
m_sem_Resume.WaitWithoutYield();
2020-11-18 16:13:34 +00:00
2009-10-31 19:25:46 +00:00
m_RunningLock.Acquire();
2020-11-18 16:13:34 +00:00
if (m_ExecMode != ExecMode_Closing)
2010-04-27 13:12:03 +00:00
{
2022-01-06 17:51:35 +10:00
// AppCoreThread deals with Reseting CDVD
OnResumeInThread(g_CDVDReset ? static_cast<SystemsMask>(systemsToTearDown & ~(System_CDVD)) : systemsToTearDown);
2020-10-04 23:34:06 +02:00
g_CDVDReset = false;
2010-04-27 13:12:03 +00:00
break;
}
m_sem_ChangingExecMode.Post();
2020-11-18 16:13:34 +00:00
[[fallthrough]];
2020-11-18 16:13:34 +00:00
2010-04-27 13:12:03 +00:00
// fallthrough if we're switching to closing state...
// -------------------------------------
2009-10-16 03:58:29 +00:00
case ExecMode_Closing:
{
OnSuspendInThread();
2009-10-16 03:58:29 +00:00
m_ExecMode = ExecMode_Closed;
2009-10-31 19:18:29 +00:00
m_RunningLock.Release();
}
[[fallthrough]];
2009-10-16 03:58:29 +00:00
case ExecMode_Closed:
2020-11-18 16:13:34 +00:00
while (m_ExecMode == ExecMode_Closed)
2010-04-27 13:12:03 +00:00
m_sem_Resume.WaitWithoutYield();
2009-10-31 19:25:46 +00:00
m_RunningLock.Acquire();
OnResumeInThread(static_cast<SystemsMask>(-1)); // All systems
g_CDVDReset = false;
2020-11-18 16:13:34 +00:00
break;
2020-11-18 16:13:34 +00:00
jNO_DEFAULT;
}
2020-11-18 16:13:34 +00:00
return true;
}