User Interface:

* Console Log doesn't force itself into the focus anymore when it opens
 * Suspend/Resume menu item updates more reliably.
 * Changing plugin settings while suspended works nicer; less likely to crash/hang/etc (ironically, changing plugin settings while running was more reliable).

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2976 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2010-05-10 17:59:03 +00:00
parent b552c1fdc2
commit 63f62bf737
24 changed files with 468 additions and 485 deletions

View File

@ -46,15 +46,8 @@ public:
virtual ~SynchronousActionState() throw() {}
void SetException( const BaseException& ex )
{
m_exception = ex.Clone();
}
void SetException( BaseException* ex )
{
m_exception = ex;
}
void SetException( const BaseException& ex );
void SetException( BaseException* ex );
Threading::Semaphore& GetSemaphore() { return m_sema; }
const Threading::Semaphore& GetSemaphore() const { return m_sema; }

View File

@ -41,6 +41,30 @@ void BaseDeletableObject::DoDeletion()
// SynchronousActionState Implementations
// --------------------------------------------------------------------------------------
void SynchronousActionState::SetException( const BaseException& ex )
{
m_exception = ex.Clone();
}
void SynchronousActionState::SetException( BaseException* ex )
{
if( !m_posted )
{
m_exception = ex;
}
else if( wxTheApp )
{
// transport the exception to the main thread, since the message is fully
// asynchronous, or has already entered an asynchronous state. Message is sent
// as a non-blocking action since proper handling of user errors on async messages
// is *usually* to log/ignore it (hah), or to suspend emulation and issue a dialog
// box to the user.
pxExceptionEvent ev( ex );
wxTheApp->AddPendingEvent( ev );
}
}
void SynchronousActionState::RethrowException() const
{
if( m_exception ) m_exception->Rethrow();
@ -67,7 +91,8 @@ void SynchronousActionState::PostResult( int res )
void SynchronousActionState::ClearResult()
{
m_posted = false;
m_posted = false;
m_exception = NULL;
}
void SynchronousActionState::PostResult()

View File

@ -381,9 +381,6 @@ static void intExecute()
{
g_EEFreezeRegs = false;
// Mem protection should be handled by the caller here so that it can be
// done in a more optimized fashion.
try {
if (g_SkipBiosHack) {
do

View File

@ -101,8 +101,6 @@ namespace Exception
};
}
typedef SafeArray<u8> VmStateBuffer;
// --------------------------------------------------------------------------------------
// SaveStateBase class
// --------------------------------------------------------------------------------------

View File

@ -26,6 +26,8 @@ static const int PCSX2_VersionLo = 7;
class SysCoreThread;
class CpuInitializerSet;
typedef SafeArray<u8> VmStateBuffer;
// --------------------------------------------------------------------------------------
// SysCoreAllocations class
// --------------------------------------------------------------------------------------

View File

@ -31,8 +31,6 @@
#include <xmmintrin.h>
static DeclareTls(SysCoreThread*) tls_coreThread( NULL );
// --------------------------------------------------------------------------------------
// SysCoreThread *External Thread* Implementations
// (Called from outside the context of this thread)
@ -45,7 +43,8 @@ SysCoreThread::SysCoreThread()
m_resetProfilers = true;
m_resetVsyncTimers = true;
m_resetVirtualMachine = true;
m_hasValidState = false;
m_hasActiveMachine = false;
}
SysCoreThread::~SysCoreThread() throw()
@ -55,13 +54,13 @@ SysCoreThread::~SysCoreThread() throw()
void SysCoreThread::Cancel( bool isBlocking )
{
m_CoreCancelDamnit = true;
m_hasActiveMachine = false;
_parent::Cancel();
}
bool SysCoreThread::Cancel( const wxTimeSpan& span )
{
m_CoreCancelDamnit = true;
m_hasActiveMachine = false;
if( _parent::Cancel( span ) )
return true;
@ -70,7 +69,6 @@ bool SysCoreThread::Cancel( const wxTimeSpan& span )
void SysCoreThread::OnStart()
{
m_CoreCancelDamnit = false;
_parent::OnStart();
}
@ -93,37 +91,27 @@ void SysCoreThread::Start()
void SysCoreThread::OnResumeReady()
{
if( m_resetVirtualMachine )
m_hasValidState = false;
m_hasActiveMachine = false;
if( !m_hasValidState )
if( !m_hasActiveMachine )
m_resetRecompilers = true;
}
// Tells the thread to recover from the in-memory state copy when it resumes. (thread must be
// resumed manually).
void SysCoreThread::RecoverState()
{
pxAssumeDev( IsPaused(), "Unsafe use of RecoverState function; Corethread is not paused/closed." );
m_resetRecompilers = true;
m_hasValidState = false;
}
void SysCoreThread::Reset()
{
Suspend();
m_resetVirtualMachine = true;
m_hasValidState = false;
}
// This function *will* reset the emulator in order to allow the specified elf file to
// take effect. This is because it really doesn't make sense to change the elf file outside
// the context of a reset/restart.
void SysCoreThread::SetElfOverride( const wxString& elf )
{
pxAssertDev( !m_hasValidState, "Thread synchronization error while assigning ELF override." );
//pxAssertDev( !m_hasValidMachine, "Thread synchronization error while assigning ELF override." );
m_elf_override = elf;
}
void SysCoreThread::Reset()
{
Suspend();
m_resetVirtualMachine = true;
m_hasActiveMachine = false;
}
// Applies a full suite of new settings, which will automatically facilitate the necessary
// resets of the core and components (including plugins, if needed). The scope of resetting
@ -143,40 +131,24 @@ void SysCoreThread::ApplySettings( const Pcsx2Config& src )
const_cast<Pcsx2Config&>(EmuConfig) = src;
}
void SysCoreThread::UploadStateCopy( const VmStateBuffer& copy )
{
if( !pxAssertDev( IsPaused(), "CoreThread is not paused; new VM state cannot be uploaded." ) ) return;
SysClearExecutionCache();
memLoadingState( copy ).FreezeAll();
m_resetVirtualMachine = false;
}
// --------------------------------------------------------------------------------------
// SysCoreThread *Worker* Implementations
// (Called from the context of this thread only)
// --------------------------------------------------------------------------------------
SysCoreThread& SysCoreThread::Get()
{
pxAssertMsg( tls_coreThread != NULL, L"This function must be called from the context of a running SysCoreThread." );
return *tls_coreThread;
}
bool SysCoreThread::HasPendingStateChangeRequest() const
{
return m_CoreCancelDamnit || GetMTGS().HasPendingException() || _parent::HasPendingStateChangeRequest();
return !m_hasActiveMachine || GetMTGS().HasPendingException() || _parent::HasPendingStateChangeRequest();
}
struct ScopedBool_ClearOnError
{
bool& m_target;
bool m_success;
ScopedBool_ClearOnError( bool& target ) :
m_target( target ), m_success( false )
{
m_target = true;
}
virtual ~ScopedBool_ClearOnError()
{
m_target = m_success;
}
void Success() { m_success = true; }
};
void SysCoreThread::_reset_stuff_as_needed()
{
if( m_resetVirtualMachine || m_resetRecompilers || m_resetProfilers )
@ -198,7 +170,7 @@ void SysCoreThread::_reset_stuff_as_needed()
{
UpdateVSyncRate();
frameLimitReset();
m_resetVsyncTimers = false;
m_resetVsyncTimers = false;
}
SetCPUState( EmuConfig.Cpu.sseMXCSR, EmuConfig.Cpu.sseVUMXCSR );
@ -210,17 +182,6 @@ void SysCoreThread::DoCpuReset()
cpuReset();
}
void SysCoreThread::CpuInitializeMess()
{
if( m_hasValidState ) return;
_reset_stuff_as_needed();
ScopedBool_ClearOnError sbcoe( m_hasValidState );
sbcoe.Success();
}
void SysCoreThread::PostVsyncToUI()
{
}
@ -245,32 +206,30 @@ void SysCoreThread::StateCheckInThread()
GetMTGS().RethrowException();
_parent::StateCheckInThread();
if( !m_hasValidState )
throw Exception::RuntimeError( "Invalid emulation state detected; Virtual machine threads have been cancelled." );
_reset_stuff_as_needed(); // kinda redundant but could catch unexpected threaded state changes...
}
// Allows an override point and solves an SEH "exception-type boundary" problem (can't mix
// SEH and C++ exceptions in the same function).
// Runs CPU cycles indefinitely, until the user or another thread requests execution to break.
// Rationale: This very short function allows an override point and solves an SEH
// "exception-type boundary" problem (can't mix SEH and C++ exceptions in the same function).
void SysCoreThread::DoCpuExecute()
{
m_hasActiveMachine = true;
Cpu->Execute();
}
void SysCoreThread::ExecuteTaskInThread()
{
Threading::EnableHiresScheduler();
tls_coreThread = this;
m_sem_event.WaitWithoutYield();
m_mxcsr_saved.bitmask = _mm_getcsr();
PCSX2_PAGEFAULT_PROTECT {
do {
while(true) {
StateCheckInThread();
DoCpuExecute();
} while( true );
}
} PCSX2_PAGEFAULT_EXCEPT;
}
@ -282,23 +241,19 @@ void SysCoreThread::OnSuspendInThread()
void SysCoreThread::OnResumeInThread( bool isSuspended )
{
GetCorePlugins().Open();
CpuInitializeMess();
}
// Invoked by the pthread_exit or pthread_cancel.
void SysCoreThread::OnCleanupInThread()
{
m_hasValidState = false;
_mm_setcsr( m_mxcsr_saved.bitmask );
Threading::DisableHiresScheduler();
m_hasActiveMachine = false;
GetCorePlugins().Close();
GetCorePlugins().Shutdown();
tls_coreThread = NULL;
_mm_setcsr( m_mxcsr_saved.bitmask );
Threading::DisableHiresScheduler();
_parent::OnCleanupInThread();
}

View File

@ -15,14 +15,16 @@
#pragma once
#include "System.h"
#include "Utilities/PersistentThread.h"
#include "Utilities/RwMutex.h"
#include "x86emitter/tools.h"
#include "CDVD/CDVDaccess.h"
using namespace Threading;
typedef SafeArray<u8> VmStateBuffer;
// --------------------------------------------------------------------------------------
// SysThreadBase
// --------------------------------------------------------------------------------------
@ -156,52 +158,47 @@ class SysCoreThread : public SysThreadBase
typedef SysThreadBase _parent;
protected:
s32 m_CloseTemporary;
bool m_resetRecompilers;
bool m_resetProfilers;
bool m_resetVsyncTimers;
bool m_resetVirtualMachine;
bool m_hasValidState;
// Used by SETJMP only, but ifdef'ing it out clutters up the code.
bool m_CoreCancelDamnit;
// Indicates if the system has an active virtual machine state. Pretty much always
// true anytime between plugins being initialized and plugins being shutdown. Gets
// set false when plugins are shutdown, the corethread is canceled, or when an error
// occurs while trying to upload a new state into the VM.
volatile bool m_hasActiveMachine;
wxString m_elf_override;
SSE_MXCSR m_mxcsr_saved;
public:
static SysCoreThread& Get();
public:
explicit SysCoreThread();
virtual ~SysCoreThread() throw();
virtual void ApplySettings( const Pcsx2Config& src );
bool HasPendingStateChangeRequest() const;
virtual void OnResumeReady();
virtual void Reset();
virtual void RecoverState();
virtual void Cancel( bool isBlocking=true );
virtual bool Cancel( const wxTimeSpan& timeout );
bool HasValidState()
{
return m_hasValidState;
}
bool HasPendingStateChangeRequest() const;
virtual void StateCheckInThread();
virtual void VsyncInThread();
virtual void PostVsyncToUI()=0;
virtual void ApplySettings( const Pcsx2Config& src );
virtual void UploadStateCopy( const VmStateBuffer& copy );
virtual bool HasActiveMachine() const { return m_hasActiveMachine; }
virtual const wxString& GetElfOverride() const { return m_elf_override; }
virtual void SetElfOverride( const wxString& elf );
protected:
void _reset_stuff_as_needed();
virtual void CpuInitializeMess();
virtual void Start();
virtual void OnStart();
virtual void OnSuspendInThread();

View File

@ -503,7 +503,7 @@ public:
// --------------------------------------------------------------------------
void DetectCpuAndUserMode();
void OpenConsoleLog();
void OpenProgramLog();
void OpenMainFrame();
void PrepForExit();
void CleanupRestartable();

View File

@ -245,6 +245,8 @@ void AppPluginManager::Unload()
void AppPluginManager::Init( PluginsEnum_t pid )
{
if( !wxTheApp ) return;
if( !wxThread::IsMain() )
{
SinglePluginMethodEvent evt(&AppPluginManager::Init, pid);
@ -258,7 +260,7 @@ void AppPluginManager::Init( PluginsEnum_t pid )
void AppPluginManager::Shutdown( PluginsEnum_t pid )
{
if( !wxThread::IsMain() )
if( !wxThread::IsMain() && wxTheApp )
{
SinglePluginMethodEvent evt( &AppPluginManager::Shutdown, pid );
wxGetApp().ProcessAction( evt );
@ -280,6 +282,9 @@ void AppPluginManager::Shutdown()
{
_parent::Shutdown();
PostPluginStatus( CorePlugins_Shutdown );
// Precautionary, in case an error occurs during saving a single plugin.
sApp.CloseGsPanel();
}
void AppPluginManager::Close()
@ -502,62 +507,39 @@ void ShutdownPlugins()
GetSysExecutorThread().PostEvent( new SysExecEvent_ShutdownPlugins() );
}
// --------------------------------------------------------------------------------------
// SaveSinglePluginHelper (Implementations)
// --------------------------------------------------------------------------------------
SaveSinglePluginHelper::SaveSinglePluginHelper( PluginsEnum_t pid )
: m_plugstore( L"PluginConf Savestate" )
void SysExecEvent_SaveSinglePlugin::InvokeEvent()
{
s_DisableGsWindow = true;
m_pid = pid;
m_validstate = SysHasValidState();
s_DisableGsWindow = true; // keeps the GS window smooth by avoiding closing the window
ScopedCoreThreadPause paused_core;
_LoadPluginsImmediate();
if( !CorePlugins.AreLoaded() ) return;
if( !m_validstate ) return;
Console.WriteLn( Color_Green, L"Suspending single plugin: " + tbl_PluginInfo[m_pid].GetShortname() );
ScopedPtr<VmStateBuffer> plugstore;
memSavingState save( m_plugstore );
GetCorePlugins().Freeze( m_pid, save );
GetCorePlugins().Close( pid );
}
SaveSinglePluginHelper::~SaveSinglePluginHelper() throw()
{
bool allowResume = true;
try {
if( m_validstate )
{
Console.WriteLn( Color_Green, L"Recovering single plugin: " + tbl_PluginInfo[m_pid].GetShortname() );
memLoadingState load( m_plugstore );
//if( m_plugstore.IsDisposed() ) load.SeekToSection( m_pid );
GetCorePlugins().Open( m_pid );
GetCorePlugins().Freeze( m_pid, load );
}
s_DisableGsWindow = false;
}
catch( BaseException& ex )
if( CoreThread.HasActiveMachine() )
{
allowResume = false;
wxGetApp().PostEvent( pxExceptionEvent( ex ) );
//Console.Error( "Unhandled BaseException in %s (ignored!):", __pxFUNCTION__ );
//Console.Error( ex.FormatDiagnosticMessage() );
Console.WriteLn( Color_Green, L"Suspending single plugin: " + tbl_PluginInfo[m_pid].GetShortname() );
memSavingState save( plugstore=new VmStateBuffer(L"StateCopy_SinglePlugin") );
GetCorePlugins().Freeze( m_pid, save );
}
catch( std::exception& ex )
{
allowResume = false;
wxGetApp().PostEvent( pxExceptionEvent(new Exception::RuntimeError( ex, L"SaveSinglePlugin" )) );
//Console.Error( "Unhandled std::exception in %s (ignored!):", __pxFUNCTION__ );
//Console.Error( ex.what() );
GetCorePlugins().Close( m_pid );
_post_and_wait( paused_core );
if( plugstore )
{
Console.WriteLn( Color_Green, L"Recovering single plugin: " + tbl_PluginInfo[m_pid].GetShortname() );
memLoadingState load( plugstore );
GetCorePlugins().Freeze( m_pid, load );
GetCorePlugins().Close( m_pid ); // hack for stupid GS plugins.
}
s_DisableGsWindow = false;
if( allowResume ) m_scoped_pause.AllowResume();
paused_core.AllowResume();
}
void SysExecEvent_SaveSinglePlugin::CleanupEvent()
{
s_DisableGsWindow = false;
_parent::CleanupEvent();
}

View File

@ -25,41 +25,6 @@
__aligned16 SysMtgsThread mtgsThread;
__aligned16 AppCoreThread CoreThread;
static void PostCoreStatus( CoreThreadStatus pevt )
{
sApp.PostAction( CoreThreadStatusEvent( pevt ) );
}
// --------------------------------------------------------------------------------------
// AppCoreThread Implementations
// --------------------------------------------------------------------------------------
AppCoreThread::AppCoreThread() : SysCoreThread()
{
}
AppCoreThread::~AppCoreThread() throw()
{
_parent::Cancel(); // use parent's, skips thread affinity check.
}
void AppCoreThread::Cancel( bool isBlocking )
{
AffinityAssert_AllowFrom_SysExecutor();
_parent::Cancel( wxTimeSpan(0, 0, 2, 0) );
}
void AppCoreThread::Shutdown()
{
AffinityAssert_AllowFrom_SysExecutor();
_parent::Reset();
CorePlugins.Shutdown();
}
ExecutorThread& GetSysExecutorThread()
{
return wxGetApp().SysExecutorThread;
}
typedef void (AppCoreThread::*FnPtr_CoreThreadMethod)();
// --------------------------------------------------------------------------------------
@ -95,6 +60,41 @@ bool ProcessingMethodViaThread( FnPtr_CoreThreadMethod method )
return false;
}
static void PostCoreStatus( CoreThreadStatus pevt )
{
sApp.PostAction( CoreThreadStatusEvent( pevt ) );
}
// --------------------------------------------------------------------------------------
// AppCoreThread Implementations
// --------------------------------------------------------------------------------------
AppCoreThread::AppCoreThread() : SysCoreThread()
{
}
AppCoreThread::~AppCoreThread() throw()
{
_parent::Cancel(); // use parent's, skips thread affinity check.
}
void AppCoreThread::Cancel( bool isBlocking )
{
AffinityAssert_AllowFrom_SysExecutor();
_parent::Cancel( wxTimeSpan(0, 0, 2, 0) );
}
void AppCoreThread::Shutdown()
{
AffinityAssert_AllowFrom_SysExecutor();
_parent::Reset();
CorePlugins.Shutdown();
}
ExecutorThread& GetSysExecutorThread()
{
return wxGetApp().SysExecutorThread;
}
static void _Suspend()
{
GetCoreThread().Suspend(true);
@ -106,8 +106,6 @@ void AppCoreThread::Suspend( bool isBlocking )
_parent::Suspend(true);
}
static int resume_tries = 0;
void AppCoreThread::Resume()
{
//if( !AffinityAssert_AllowFrom_SysExecutor() ) return;
@ -117,31 +115,11 @@ void AppCoreThread::Resume()
return;
}
if( m_ExecMode == ExecMode_Opened || (m_CloseTemporary > 0) ) return;
if( !pxAssert( CorePlugins.AreLoaded() ) ) return;
//if( m_ExecMode == ExecMode_Opened ) return;
//if( !pxAssert( CorePlugins.AreLoaded() ) ) return;
_parent::Resume();
if( m_ExecMode != ExecMode_Opened )
{
// Resume failed for some reason, so update GUI statuses and post a message to
// try again on the resume.
PostCoreStatus( CoreThread_Suspended );
if( (m_ExecMode != ExecMode_Closing) || (m_ExecMode != ExecMode_Pausing) )
{
if( ++resume_tries <= 2 )
{
sApp.SysExecute();
}
else
Console.WriteLn( Color_Orange, "SysResume: Multiple resume retries failed. Giving up..." );
}
}
resume_tries = 0;
}
void AppCoreThread::ChangeCdvdSource()
@ -247,151 +225,69 @@ void AppCoreThread::StateCheckInThread()
_parent::StateCheckInThread();
}
// Thread Affinity: This function is called from the SysCoreThread. :)
void AppCoreThread::CpuInitializeMess()
void AppCoreThread::UploadStateCopy( const VmStateBuffer& copy )
{
if( m_hasValidState ) return;
if( StateCopy_IsValid() )
{
// Automatic recovery system if a state exists in memory. This is executed here
// in order to ensure the plugins are in the proper (loaded/opened) state.
SysClearExecutionCache();
memLoadingState( StateCopy_GetBuffer() ).FreezeAll();
StateCopy_Clear();
m_hasValidState = true;
m_resetVirtualMachine = false;
return;
}
_parent::CpuInitializeMess();
ScopedCoreThreadPause paused_core;
_parent::UploadStateCopy( copy );
paused_core.AllowResume();
}
void AppCoreThread::ExecuteTaskInThread()
{
PostCoreStatus( CoreThread_Started );
_parent::ExecuteTaskInThread();
}
enum
{
FullStop_BlockingResume
, FullStop_NonblockingResume
, FullStop_SkipResume
};
// --------------------------------------------------------------------------------------
// BaseSysExecEvent_ScopedCore
// BaseSysExecEvent_ScopedCore / SysExecEvent_CoreThreadClose / SysExecEvent_CoreThreadPause
// --------------------------------------------------------------------------------------
class BaseSysExecEvent_ScopedCore : public SysExecEvent
void BaseSysExecEvent_ScopedCore::_post_and_wait( IScopedCoreThread& core )
{
protected:
SynchronousActionState* m_resume;
Threading::Mutex* m_mtx_resume;
DoScopedTask();
public:
virtual ~BaseSysExecEvent_ScopedCore() throw() {}
ScopedLock lock( m_mtx_resume );
PostResult();
protected:
BaseSysExecEvent_ScopedCore( SynchronousActionState* sync=NULL, SynchronousActionState* resume_sync=NULL, Threading::Mutex* mtx_resume=NULL )
: SysExecEvent( sync )
if( m_resume )
{
m_resume = resume_sync;
m_mtx_resume = mtx_resume;
}
void _post_and_wait( IScopedCoreThread& core )
{
ScopedLock lock( m_mtx_resume );
PostResult();
if( m_resume )
// If the sender of the message requests a non-blocking resume, then we need
// to deallocate the m_sync object, since the sender will likely leave scope and
// invalidate it.
switch( m_resume->WaitForResult() )
{
// If the sender of the message requests a non-blocking resume, then we need
// to deallocate the m_sync object, since the sender will likely leave scope and
// invalidate it.
switch( m_resume->WaitForResult() )
{
case FullStop_BlockingResume:
if( m_sync ) m_sync->ClearResult();
core.AllowResume();
break;
case ScopedCore_BlockingResume:
if( m_sync ) m_sync->ClearResult();
core.AllowResume();
break;
case FullStop_NonblockingResume:
m_sync = NULL;
core.AllowResume();
break;
case ScopedCore_NonblockingResume:
m_sync = NULL;
core.AllowResume();
break;
case FullStop_SkipResume:
m_sync = NULL;
break;
}
case ScopedCore_SkipResume:
m_sync = NULL;
break;
}
}
}
};
// --------------------------------------------------------------------------------------
// SysExecEvent_CoreThreadClose
// --------------------------------------------------------------------------------------
class SysExecEvent_CoreThreadClose : public BaseSysExecEvent_ScopedCore
void SysExecEvent_CoreThreadClose::InvokeEvent()
{
public:
wxString GetEventName() const { return L"CloseCoreThread"; }
ScopedCoreThreadClose closed_core;
_post_and_wait(closed_core);
closed_core.AllowResume();
}
virtual ~SysExecEvent_CoreThreadClose() throw() {}
SysExecEvent_CoreThreadClose* Clone() const
{
return new SysExecEvent_CoreThreadClose( *this );
}
SysExecEvent_CoreThreadClose( SynchronousActionState* sync=NULL, SynchronousActionState* resume_sync=NULL, Threading::Mutex* mtx_resume=NULL )
: BaseSysExecEvent_ScopedCore( sync, resume_sync, mtx_resume ) { }
SysExecEvent_CoreThreadClose( SynchronousActionState& sync, SynchronousActionState& resume_sync, Threading::Mutex& mtx_resume )
: BaseSysExecEvent_ScopedCore( &sync, &resume_sync, &mtx_resume ) { }
protected:
void InvokeEvent()
{
ScopedCoreThreadClose closed_core;
_post_and_wait(closed_core);
closed_core.AllowResume();
}
};
// --------------------------------------------------------------------------------------
// SysExecEvent_CoreThreadPause
// --------------------------------------------------------------------------------------
class SysExecEvent_CoreThreadPause : public BaseSysExecEvent_ScopedCore
void SysExecEvent_CoreThreadPause::InvokeEvent()
{
public:
wxString GetEventName() const { return L"PauseCoreThread"; }
ScopedCoreThreadPause paused_core;
_post_and_wait(paused_core);
paused_core.AllowResume();
}
virtual ~SysExecEvent_CoreThreadPause() throw() {}
SysExecEvent_CoreThreadPause* Clone() const
{
return new SysExecEvent_CoreThreadPause( *this );
}
SysExecEvent_CoreThreadPause( SynchronousActionState* sync=NULL, SynchronousActionState* resume_sync=NULL, Threading::Mutex* mtx_resume=NULL )
: BaseSysExecEvent_ScopedCore( sync, resume_sync, mtx_resume ) { }
SysExecEvent_CoreThreadPause( SynchronousActionState& sync, SynchronousActionState& resume_sync, Threading::Mutex& mtx_resume )
: BaseSysExecEvent_ScopedCore( &sync, &resume_sync, &mtx_resume ) { }
protected:
void InvokeEvent()
{
ScopedCoreThreadPause paused_core;
_post_and_wait(paused_core);
paused_core.AllowResume();
}
};
// --------------------------------------------------------------------------------------
// ScopedCoreThreadClose / ScopedCoreThreadPause
@ -432,13 +328,29 @@ void BaseScopedCoreThread::DoResume()
if( !GetSysExecutorThread().IsSelf() )
{
//DbgCon.WriteLn("(ScopedCoreThreadPause) Threaded Scope Created!");
m_sync_resume.PostResult( m_allowResume ? FullStop_NonblockingResume : FullStop_SkipResume );
m_sync_resume.PostResult( m_allowResume ? ScopedCore_NonblockingResume : ScopedCore_SkipResume );
m_mtx_resume.Wait();
}
else
CoreThread.Resume();
}
// Returns TRUE if the event is posted to the SysExecutor.
// Returns FALSE if the thread *is* the SysExecutor (no message is posted, calling code should
// handle the code directly).
bool BaseScopedCoreThread::PostToSysExec( BaseSysExecEvent_ScopedCore* msg )
{
if( !msg || GetSysExecutorThread().IsSelf()) return false;
msg->SetSyncState(m_sync);
msg->SetResumeStates(m_sync_resume, m_mtx_resume);
GetSysExecutorThread().PostEvent( msg );
m_sync.WaitForResult();
m_sync.RethrowException();
return true;
}
ScopedCoreThreadClose::ScopedCoreThreadClose()
{
@ -449,16 +361,11 @@ ScopedCoreThreadClose::ScopedCoreThreadClose()
return;
}
if( !GetSysExecutorThread().IsSelf() )
if( !PostToSysExec(new SysExecEvent_CoreThreadClose()) )
{
//DbgCon.WriteLn("(ScopedCoreThreadClose) Threaded Scope Created!");
GetSysExecutorThread().PostEvent( SysExecEvent_CoreThreadClose(m_sync, m_sync_resume, m_mtx_resume) );
m_sync.WaitForResult();
m_sync.RethrowException();
if( !(m_alreadyStopped = CoreThread.IsClosed()) )
CoreThread.Suspend();
}
else if( !(m_alreadyStopped = CoreThread.IsClosed()) )
CoreThread.Suspend();
ScopedCore_IsFullyClosed = true;
}
@ -470,7 +377,7 @@ ScopedCoreThreadClose::~ScopedCoreThreadClose() throw()
ScopedCore_IsFullyClosed = false;
}
ScopedCoreThreadPause::ScopedCoreThreadPause()
ScopedCoreThreadPause::ScopedCoreThreadPause( BaseSysExecEvent_ScopedCore* abuse_me )
{
if( ScopedCore_IsFullyClosed || ScopedCore_IsPaused )
{
@ -479,16 +386,12 @@ ScopedCoreThreadPause::ScopedCoreThreadPause()
return;
}
if( !GetSysExecutorThread().IsSelf() )
if( !abuse_me ) abuse_me = new SysExecEvent_CoreThreadPause();
if( !PostToSysExec( abuse_me ) )
{
//DbgCon.WriteLn("(ScopedCoreThreadPause) Threaded Scope Created!");
GetSysExecutorThread().PostEvent( SysExecEvent_CoreThreadPause(m_sync, m_sync_resume, m_mtx_resume) );
m_sync.WaitForResult();
m_sync.RethrowException();
if( !(m_alreadyStopped = CoreThread.IsPaused()) )
CoreThread.Pause();
}
else if( !(m_alreadyStopped = CoreThread.IsPaused()) )
CoreThread.Pause();
ScopedCore_IsPaused = true;
}

View File

@ -18,6 +18,7 @@
#include "System/SysThreads.h"
#include "AppCommon.h"
#include "AppCorePlugins.h"
#include "SaveState.h"
#define AffinityAssert_AllowFrom_CoreThread() \
pxAssertMsg( GetCoreThread().IsSelf(), "Thread affinity violation: Call allowed from SysCoreThread only." )
@ -25,6 +26,93 @@
#define AffinityAssert_DisallowFrom_CoreThread() \
pxAssertMsg( !GetCoreThread().IsSelf(), "Thread affinity violation: Call is *not* allowed from SysCoreThread." )
class IScopedCoreThread;
class BaseScopedCoreThread;
enum ScopedCoreResumeType
{
ScopedCore_BlockingResume
, ScopedCore_NonblockingResume
, ScopedCore_SkipResume
};
// --------------------------------------------------------------------------------------
// BaseSysExecEvent_ScopedCore
// --------------------------------------------------------------------------------------
class BaseSysExecEvent_ScopedCore : public SysExecEvent
{
protected:
SynchronousActionState* m_resume;
Threading::Mutex* m_mtx_resume;
public:
virtual ~BaseSysExecEvent_ScopedCore() throw() {}
BaseSysExecEvent_ScopedCore& SetResumeStates( SynchronousActionState* sync, Threading::Mutex* mutex )
{
m_resume = sync;
m_mtx_resume = mutex;
return *this;
}
BaseSysExecEvent_ScopedCore& SetResumeStates( SynchronousActionState& sync, Threading::Mutex& mutex )
{
m_resume = &sync;
m_mtx_resume = &mutex;
return *this;
}
protected:
BaseSysExecEvent_ScopedCore( SynchronousActionState* sync=NULL, SynchronousActionState* resume_sync=NULL, Threading::Mutex* mtx_resume=NULL )
: SysExecEvent( sync )
{
m_resume = resume_sync;
m_mtx_resume = mtx_resume;
}
void _post_and_wait( IScopedCoreThread& core );
virtual void DoScopedTask() {}
};
// --------------------------------------------------------------------------------------
// SysExecEvent_CoreThreadClose
// --------------------------------------------------------------------------------------
class SysExecEvent_CoreThreadClose : public BaseSysExecEvent_ScopedCore
{
public:
wxString GetEventName() const { return L"CloseCoreThread"; }
virtual ~SysExecEvent_CoreThreadClose() throw() {}
SysExecEvent_CoreThreadClose* Clone() const { return new SysExecEvent_CoreThreadClose( *this ); }
SysExecEvent_CoreThreadClose( SynchronousActionState* sync=NULL, SynchronousActionState* resume_sync=NULL, Threading::Mutex* mtx_resume=NULL )
: BaseSysExecEvent_ScopedCore( sync, resume_sync, mtx_resume ) { }
protected:
void InvokeEvent();
};
// --------------------------------------------------------------------------------------
// SysExecEvent_CoreThreadPause
// --------------------------------------------------------------------------------------
class SysExecEvent_CoreThreadPause : public BaseSysExecEvent_ScopedCore
{
public:
wxString GetEventName() const { return L"PauseCoreThread"; }
virtual ~SysExecEvent_CoreThreadPause() throw() {}
SysExecEvent_CoreThreadPause* Clone() const { return new SysExecEvent_CoreThreadPause( *this ); }
SysExecEvent_CoreThreadPause( SynchronousActionState* sync=NULL, SynchronousActionState* resume_sync=NULL, Threading::Mutex* mtx_resume=NULL )
: BaseSysExecEvent_ScopedCore( sync, resume_sync, mtx_resume ) { }
protected:
void InvokeEvent();
};
// --------------------------------------------------------------------------------------
// AppCoreThread class
// --------------------------------------------------------------------------------------
@ -41,9 +129,11 @@ public:
virtual void Shutdown();
virtual void Cancel( bool isBlocking=true );
virtual void StateCheckInThread();
virtual void ApplySettings( const Pcsx2Config& src );
virtual void ChangeCdvdSource();
virtual void ApplySettings( const Pcsx2Config& src );
virtual void UploadStateCopy( const VmStateBuffer& copy );
protected:
virtual void OnResumeReady();
virtual void OnResumeInThread( bool IsSuspended );
@ -52,10 +142,11 @@ protected:
virtual void PostVsyncToUI();
virtual void ExecuteTaskInThread();
virtual void DoCpuReset();
virtual void CpuInitializeMess();
};
// --------------------------------------------------------------------------------------
// IScopedCoreThread / BaseScopedCoreThread
// --------------------------------------------------------------------------------------
class IScopedCoreThread
{
protected:
@ -67,7 +158,6 @@ public:
virtual void DisallowResume()=0;
};
class BaseScopedCoreThread : public IScopedCoreThread
{
DeclareNoncopyableObject( BaseScopedCoreThread );
@ -87,7 +177,10 @@ public:
virtual void AllowResume();
virtual void DisallowResume();
virtual bool PostToSysExec( BaseSysExecEvent_ScopedCore* msg );
protected:
// Called from destructors -- do not make virtual!!
void DoResume();
};
@ -120,12 +213,12 @@ public:
void LoadPlugins();
};
struct ScopedCoreThreadPause : public BaseScopedCoreThread
struct ScopedCoreThreadPause : public BaseScopedCoreThread
{
typedef BaseScopedCoreThread _parent;
public:
ScopedCoreThreadPause();
ScopedCoreThreadPause( BaseSysExecEvent_ScopedCore* abuse_me=NULL );
virtual ~ScopedCoreThreadPause() throw();
};

View File

@ -200,7 +200,7 @@ void Pcsx2App::DetectCpuAndUserMode()
AppConfig_OnChangedSettingsFolder();
PostAppMethod( &Pcsx2App::OpenMainFrame );
PostAppMethod( &Pcsx2App::OpenConsoleLog );
PostAppMethod( &Pcsx2App::OpenProgramLog );
PostAppMethod( &Pcsx2App::AllocateCoreStuffs );
}
@ -216,7 +216,7 @@ void Pcsx2App::OpenMainFrame()
deleteme->Destroy();
g_Conf->ProgLogBox.Visible = true;
m_id_ProgramLogBox = wxID_ANY;
PostIdleAppMethod( &Pcsx2App::OpenConsoleLog );
PostIdleAppMethod( &Pcsx2App::OpenProgramLog );
}
SetTopWindow( mainFrame ); // not really needed...
@ -224,11 +224,14 @@ void Pcsx2App::OpenMainFrame()
mainFrame->Show();
}
void Pcsx2App::OpenConsoleLog()
void Pcsx2App::OpenProgramLog()
{
if( GetProgramLog() != NULL ) return;
wxWindow* m_current_focus = wxGetActiveWindow();
m_id_ProgramLogBox = (new ConsoleLogFrame( GetMainFramePtr(), L"PCSX2 Program Log", g_Conf->ProgLogBox ))->GetId();
EnableAllLogging();
if( m_current_focus ) m_current_focus->SetFocus();
}
void Pcsx2App::AllocateCoreStuffs()

View File

@ -404,12 +404,20 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
// ----------------------------------------------------------------------------
catch( Exception::SaveStateLoadError& ex)
{
// Saved state load failed.
// Saved state load failed prior to the system getting corrupted (ie, file not found
// or some zipfile error) -- so log it and resume emulation.
Console.Warning( ex.FormatDiagnosticMessage() );
StateCopy_Clear();
CoreThread.Resume();
}
// ----------------------------------------------------------------------------
catch( Exception::PluginOpenError& ex )
{
// Should need to do much here -- all systems should be in an inert and (sorta safe!) state.
Console.Error( ex.FormatDiagnosticMessage() );
AddIdleEvent( PluginInitErrorEvent(ex) );
}
// ----------------------------------------------------------------------------
catch( Exception::PluginInitError& ex )
{
ShutdownPlugins();
@ -417,6 +425,7 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
Console.Error( ex.FormatDiagnosticMessage() );
AddIdleEvent( PluginInitErrorEvent(ex) );
}
// ----------------------------------------------------------------------------
catch( Exception::PluginError& ex )
{
UnloadPlugins();
@ -885,7 +894,7 @@ protected:
else if( CDVD == NULL )
CDVDsys_ChangeSource( CDVDsrc_NoDisc );
if( m_UseELFOverride && !CoreThread.HasValidState() )
if( m_UseELFOverride && !CoreThread.HasActiveMachine() )
CoreThread.SetElfOverride( m_elf_override );
CoreThread.Resume();
@ -926,7 +935,6 @@ public:
protected:
void InvokeEvent()
{
StateCopy_Clear();
CoreThread.Shutdown();
}
@ -945,7 +953,7 @@ void Pcsx2App::SysShutdown()
// state (such as saving it), you *must* suspend the Corethread first!
__forceinline bool SysHasValidState()
{
return CoreThread.HasValidState() || StateCopy_IsValid();
return CoreThread.HasActiveMachine();
}
// Writes text to console and updates the window status bar and/or HUD or whateverness.

View File

@ -19,35 +19,44 @@
#include "SaveState.h"
// --------------------------------------------------------------------------------------
// SaveSinglePluginHelper
// SysExecEvent_SaveSinglePlugin
// --------------------------------------------------------------------------------------
// A scoped convenience class for closing a single plugin and saving its state to memory.
// Emulation is suspended as needed, and is restored when the object leaves scope. Within
// the scope of the object, code is free to call plugin re-configurations or even unload
// a plugin entirely and re-load a different plugin in its place.
// fixme : Ideally this should use either Close or Pause depending on if the system is in
// Fullscreen Exclusive mode or regular mode. But since we don't yet support Fullscreen
// Exclusive mode, and since I'm too lazy to make some third suspend class for that, we're
// just using CoreThreadPause. --air
//
class SaveSinglePluginHelper
class SysExecEvent_SaveSinglePlugin : public BaseSysExecEvent_ScopedCore
{
typedef BaseSysExecEvent_ScopedCore _parent;
protected:
VmStateBuffer m_plugstore;
bool m_validstate;
PluginsEnum_t m_pid;
ScopedCoreThreadPause m_scoped_pause;
public:
SaveSinglePluginHelper( PluginsEnum_t pid );
virtual ~SaveSinglePluginHelper() throw();
wxString GetEventName() const { return L"SaveSinglePlugin"; }
virtual ~SysExecEvent_SaveSinglePlugin() throw() {}
SysExecEvent_SaveSinglePlugin* Clone() const { return new SysExecEvent_SaveSinglePlugin( *this ); }
SysExecEvent_SaveSinglePlugin( PluginsEnum_t pid=PluginId_GS )
{
m_pid = pid;
}
SysExecEvent_SaveSinglePlugin& SetPluginId( PluginsEnum_t pid )
{
m_pid = pid;
return *this;
}
protected:
void InvokeEvent();
void CleanupEvent();
};
extern VmStateBuffer& StateCopy_GetBuffer();
extern bool StateCopy_IsValid();
extern void StateCopy_FreezeToMem();
extern void StateCopy_SaveToFile( const wxString& file );
extern void StateCopy_LoadFromFile( const wxString& file );
extern void StateCopy_SaveToSlot( uint num );
extern void StateCopy_LoadFromSlot( uint slot );
extern void StateCopy_Clear();

View File

@ -283,13 +283,13 @@ ConsoleLogFrame::ConsoleLogFrame( MainEmuFrame *parent, const wxString& title, A
_("When checked the log window will be visible over other foreground windows."), wxITEM_CHECK );
//menuAppear.Append( wxID_ANY, _("Font Size"), &menuFontSizes );
menuLog.Append(wxID_SAVE, _("&Save..."), _("Save log contents to file"));
menuLog.Append(wxID_CLEAR, _("C&lear"), _("Clear the log window contents"));
menuLog.Append(wxID_SAVE, _("&Save..."), _("Save log contents to file"));
menuLog.Append(wxID_CLEAR, _("C&lear"), _("Clear the log window contents"));
menuLog.AppendSeparator();
menuLog.AppendSubMenu( &menuAppear, _("Appearance") );
menuLog.Append(wxID_ANY, _("Show Legend"), _("Displays the console color legend.") );
menuLog.AppendSeparator();
menuLog.Append(wxID_CLOSE, _("&Close"), _("Close this log window; contents are preserved"));
menuLog.Append(wxID_CLOSE, _("&Close"), _("Close this log window; contents are preserved"));
// Source Selection/Toggle menu

View File

@ -184,7 +184,7 @@ FirstTimeWizard::~FirstTimeWizard() throw()
static void _OpenConsole()
{
g_Conf->ProgLogBox.Visible = true;
wxGetApp().OpenConsoleLog();
wxGetApp().OpenProgramLog();
}
int FirstTimeWizard::ShowModal()

View File

@ -37,7 +37,7 @@ void SysExecEvent::InvokeEvent()
// This is called by _DoInvokeEvent *always* -- even when exceptions occur during InvokeEvent(),
// making this function a bit like a C# 'finally' block (try/catch/finally -- a nice feature lacking
// from C++ prior to the new C++0x10 standard).
// from C++ prior to the new C++0x standard).
//
// This function calls PostResult by default, and should be invoked by derived classes overriding
// CleanupEvent(), unless you want to change the PostResult behavior.
@ -53,8 +53,7 @@ void SysExecEvent::SetException( BaseException* ex )
{
if( !ex ) return;
const wxString& prefix( wxsFormat(L"(%s) ", GetEventName().c_str()) );
ex->DiagMsg() = prefix + ex->DiagMsg();
ex->DiagMsg() += wxsFormat(L"(%s) ", GetEventName().c_str());
//ex->UserMsg() = prefix + ex->UserMsg();
if( m_sync )
@ -66,9 +65,10 @@ void SysExecEvent::SetException( BaseException* ex )
else
{
// transport the exception to the main thread, since the message is fully
// asynchronous. Message is sent as a non-blocking action since proper handling
// of user errors on async messages is *usually* to log/ignore it (hah), or to
// suspend emulation and issue a dialog box to the user.
// asynchronous, or has already entered an asynchronous state. Message is sent
// as a non-blocking action since proper handling of user errors on async messages
// is *usually* to log/ignore it (hah), or to suspend emulation and issue a dialog
// box to the user.
wxGetApp().PostEvent( pxExceptionEvent( ex ) );
}
@ -85,8 +85,8 @@ void SysExecEvent::SetException( const BaseException& ex )
// instead, which is the intended method of implementing derived class invocation.
void SysExecEvent::_DoInvokeEvent()
{
//pxAssumeDev( !IsBeingDeleted(), "Attempted to process a deleted SysExecutor event." );
AffinityAssert_AllowFrom_SysExecutor();
try {
InvokeEvent();
}
@ -99,7 +99,18 @@ void SysExecEvent::_DoInvokeEvent()
SetException( new Exception::RuntimeError(ex) );
}
CleanupEvent();
// Cleanup Execution -- performed regardless of exception or not above.
try {
CleanupEvent();
}
catch( BaseException& ex )
{
SetException( ex );
}
catch( std::runtime_error& ex )
{
SetException( new Exception::RuntimeError(ex) );
}
}
// Posts an empty result to the invoking context/thread of this message, if one exists.
@ -399,8 +410,11 @@ ExecutorThread::ExecutorThread( pxEvtHandler* evthandler )
// Exposes the internal pxEvtHandler::ShutdownQueue API. See pxEvtHandler for details.
void ExecutorThread::ShutdownQueue()
{
if( !m_EvtHandler || m_EvtHandler->IsShuttingDown() ) return;
m_EvtHandler->ShutdownQueue();
if( !m_EvtHandler ) return;
if( !m_EvtHandler->IsShuttingDown() )
m_EvtHandler->ShutdownQueue();
Block();
}

View File

@ -149,8 +149,9 @@ namespace Implementations
void Sys_RenderToggle()
{
SaveSinglePluginHelper helper( PluginId_GS );
ScopedCoreThreadPause paused_core( new SysExecEvent_SaveSinglePlugin(PluginId_GS) );
renderswitch = !renderswitch;
paused_core.AllowResume();
}
void Sys_LoggingToggle()

View File

@ -524,62 +524,80 @@ void MainEmuFrame::ApplyCoreStatus()
{
wxMenuBar& menubar( *GetMenuBar() );
wxMenuItem& susres (*menubar.FindItem( MenuId_Sys_SuspendResume ));
wxMenuItem& cdvd (*menubar.FindItem( MenuId_Boot_CDVD ));
wxMenuItem& cdvd2 (*menubar.FindItem( MenuId_Boot_CDVD2 ));
if( !pxAssertMsg( (&susres) && (&cdvd) && (&cdvd2), "Unexpected NULL Menubar Item!" ) ) return;
wxMenuItem* susres = menubar.FindItem( MenuId_Sys_SuspendResume );
wxMenuItem* cdvd = menubar.FindItem( MenuId_Boot_CDVD );
wxMenuItem* cdvd2 = menubar.FindItem( MenuId_Boot_CDVD2 );
wxMenuItem* restart = menubar.FindItem( MenuId_Sys_Restart );
if( SysHasValidState() )
// [TODO] : Ideally each of these items would bind a listener instance to the AppCoreThread
// dispatcher, and modify their states accordingly. This is just a hack (for now) -- air
bool vm = SysHasValidState();
if( susres )
{
susres.Enable();
if( CoreThread.IsOpen() )
{
susres.SetText(_("Suspend"));
susres.SetHelp(_("Safely pauses emulation and preserves the PS2 state."));
susres->Enable();
susres->SetText(_("Suspend"));
susres->SetHelp(_("Safely pauses emulation and preserves the PS2 state."));
}
else
{
susres.SetText(_("Resume"));
susres.SetHelp(_("Resumes the suspended emulation state."));
susres->Enable(vm);
if( vm )
{
susres->SetText(_("Resume"));
susres->SetHelp(_("Resumes the suspended emulation state."));
}
else
{
susres->SetText(_("Suspend/Resume"));
susres->SetHelp(_("No emulation state is active; cannot suspend or resume."));
}
}
}
if( restart )
if( restart )
{
if( vm )
{
restart->SetText(_("Restart"));
restart->SetHelp(_("Simulates hardware reset of the PS2 virtual machine."));
}
cdvd.SetText(_("Reboot CDVD (full)"));
cdvd.SetHelp(_("Hard reset of the active VM."));
cdvd2.SetText(_("Reboot CDVD (fast)"));
cdvd2.SetHelp(_("Reboot using BOOT2 injection (skips splash screens)"));
}
else
{
susres.Enable( false );
susres.SetText(_("Suspend/Resume"));
susres.SetHelp( _("No emulation state is active; cannot suspend or resume.") );
if( restart )
else
{
restart->Enable( false );
restart->SetHelp( _("No emulation state is active; boot something first.") );
restart->SetHelp(_("No emulation state is active; boot something first."));
}
}
cdvd.SetText(_("Boot CDVD (full)"));
cdvd.SetHelp(_("Boot the VM using the current DVD or Iso source media"));
if( cdvd )
{
if( vm )
{
cdvd->SetText(_("Reboot CDVD (full)"));
cdvd->SetHelp(_("Hard reset of the active VM."));
}
else
{
cdvd->SetText(_("Boot CDVD (full)"));
cdvd->SetHelp(_("Boot the VM using the current DVD or Iso source media"));
}
}
cdvd2.SetText(_("Boot CDVD (fast)"));
cdvd2.SetHelp(_("Use BOOT2 injection to skip PS2 startup and splash screens"));
// Old style...
//restart.SetHelp(_("Starts execution of the PS2 virtual machine."));
//restart.SetText(_("Start"));
if( cdvd2 )
{
if( vm )
{
cdvd2->SetText(_("Reboot CDVD (fast)"));
cdvd2->SetHelp(_("Reboot using BOOT2 injection (skips splash screens)"));
}
else
{
cdvd2->SetText(_("Boot CDVD (fast)"));
cdvd2->SetHelp(_("Use BOOT2 injection to skip PS2 startup and splash screens"));
}
}
menubar.Enable( MenuId_Sys_Shutdown, SysHasValidState() || CorePlugins.AreAnyInitialized() );

View File

@ -498,12 +498,8 @@ void MainEmuFrame::Menu_ConfigPlugin_Click(wxCommandEvent &event)
if( !pxAssertDev( (eventId >= 0) || (pid < PluginId_Count), "Invalid plugin identifier passed to ConfigPlugin event handler." ) ) return;
// This could probably just load a single plugin as needed now, but for design safety
// I'm leaving it force-load everything until the individual plugin management is
// better tested.
wxWindowDisabler disabler;
SaveSinglePluginHelper helper( pid );
ScopedCoreThreadPause paused_core( new SysExecEvent_SaveSinglePlugin(pid) );
GetCorePlugins().Configure( pid );
}

View File

@ -251,6 +251,8 @@ void SysExecEvent_ApplyPlugins::InvokeEvent()
{
ScopedCoreThreadPause paused_core;
ScopedPtr< VmStateBuffer > buffer;
if( SysHasValidState() )
{
paused_core.AllowResume();
@ -261,7 +263,7 @@ void SysExecEvent_ApplyPlugins::InvokeEvent()
// FIXME : We only actually have to save plugins here, except the recovery code
// in SysCoreThread isn't quite set up yet to handle that (I think...) --air
memSavingState( StateCopy_GetBuffer() ).FreezeAll();
memSavingState( *(buffer.Reassign(new VmStateBuffer(L"StateBuffer_ApplyNewPlugins"))) ).FreezeAll();
}
ScopedCoreThreadClose closed_core;
@ -269,7 +271,8 @@ void SysExecEvent_ApplyPlugins::InvokeEvent()
CorePlugins.Shutdown();
CorePlugins.Unload();
LoadPluginsImmediate();
CoreThread.RecoverState();
if( buffer ) CoreThread.UploadStateCopy( *buffer );
PostFinishToDialog();
@ -639,7 +642,7 @@ void Panels::PluginSelectorPanel::OnConfigure_Clicked( wxCommandEvent& evt )
if( ConfigureFnptr configfunc = (ConfigureFnptr)dynlib.GetSymbol( tbl_PluginInfo[pid].GetShortname() + L"configure" ) )
{
wxWindowDisabler disabler;
SaveSinglePluginHelper helper( pid );
ScopedCoreThreadPause paused_core( new SysExecEvent_SaveSinglePlugin(pid) );
configfunc();
}
}

View File

@ -22,7 +22,7 @@
#include "ZipTools/ThreadedZipTools.h"
// Used to hold the current state backup (fullcopy of PS2 memory and plugin states).
static VmStateBuffer state_buffer( L"Public Savestate Buffer" );
//static VmStateBuffer state_buffer( L"Public Savestate Buffer" );
static const char SavestateIdentString[] = "PCSX2 Savestate";
static const uint SavestateIdentLen = sizeof(SavestateIdentString);
@ -120,7 +120,7 @@ public:
virtual ~SysExecEvent_DownloadState() throw() {}
SysExecEvent_DownloadState* Clone() const { return new SysExecEvent_DownloadState( *this ); }
SysExecEvent_DownloadState( VmStateBuffer* dest=&state_buffer )
SysExecEvent_DownloadState( VmStateBuffer* dest=NULL )
{
m_dest_buffer = dest;
}
@ -238,9 +238,9 @@ protected:
// --------------------------------------------------------------------------------------
// SysExecEvent_UnzipFromDisk
// --------------------------------------------------------------------------------------
// Note: Unzipping always goes directly into the state_buffer, and is always a blocking
// action on the SysExecutor thread (the system cannot execute other commands while states
// are unzipping or uplading into the system).
// Note: Unzipping always goes directly into the SysCoreThread's static VM state, and is
// always a blocking action on the SysExecutor thread (the system cannot execute other
// commands while states are unzipping or uploading into the system).
//
class SysExecEvent_UnzipFromDisk : public SysExecEvent
{
@ -263,7 +263,6 @@ protected:
void InvokeEvent()
{
ScopedLock lock( mtx_CompressToDisk );
gzipReader m_gzreader(m_filename );
SaveStateFile_ReadHeader( m_gzreader );
@ -275,13 +274,14 @@ protected:
// fixme: should start initially with the file size, and then grow from there.
static const int BlockSize = 0x100000;
state_buffer.MakeRoomFor( 0x800000 ); // start with an 8 meg buffer to avoid frequent reallocation.
VmStateBuffer buffer( 0x800000, L"StateBuffer_UnzipFromDisk" ); // start with an 8 meg buffer to avoid frequent reallocation.
int curidx = 0;
try {
while(true) {
state_buffer.MakeRoomFor( curidx+BlockSize );
m_gzreader.Read( state_buffer.GetPtr(curidx), BlockSize );
buffer.MakeRoomFor( curidx+BlockSize );
m_gzreader.Read( buffer.GetPtr(curidx), BlockSize );
curidx += BlockSize;
Threading::pxTestCancel();
}
@ -295,9 +295,10 @@ protected:
// Optional shutdown of plugins when loading states? I'm not implementing it yet because some
// things, like the SPU2-recovery trick, rely on not resetting the plugins prior to loading
// the new savestate data.
//if( ShutdownOnStateLoad ) GetCoreThread().Cancel();
GetCoreThread().RecoverState();
GetCoreThread().UploadStateCopy( buffer );
GetCoreThread().Resume(); // force resume regardless of emulation state earlier.
}
};
@ -306,21 +307,6 @@ protected:
// StateCopy Public Interface
// =====================================================================================================
VmStateBuffer& StateCopy_GetBuffer()
{
return state_buffer;
}
bool StateCopy_IsValid()
{
return !state_buffer.IsDisposed();
}
void StateCopy_FreezeToMem()
{
GetSysExecutorThread().PostEvent( new SysExecEvent_DownloadState() );
}
void StateCopy_SaveToFile( const wxString& file )
{
UI_DisableStateActions();
@ -365,9 +351,3 @@ void StateCopy_LoadFromSlot( uint slot )
StateCopy_LoadFromFile( file );
}
void StateCopy_Clear()
{
state_buffer.Dispose();
}

View File

@ -17,6 +17,12 @@
#include "MainFrame.h"
#include "GSFrame.h"
// General Notes:
// * It's very important that we re-discover menu items by ID every time we change them,
// because the modern era of configurable GUIs means that we can't be assured the IDs
// exist anymore.
// This is necessary because this stupid wxWidgets thing has implicit debug errors
// in the FindItem call that asserts if the menu options are missing. This is bad
// mojo for configurable/dynamic menus. >_<

View File

@ -63,8 +63,8 @@ public:
const SynchronousActionState* GetSyncState() const { return m_sync; }
SynchronousActionState* GetSyncState() { return m_sync; }
void SetSyncState( SynchronousActionState* obj ) { m_sync = obj; }
void SetSyncState( SynchronousActionState& obj ) { m_sync = &obj; }
SysExecEvent& SetSyncState( SynchronousActionState* obj ) { m_sync = obj; return *this; }
SysExecEvent& SetSyncState( SynchronousActionState& obj ) { m_sync = &obj; return *this; }
// Tells the Event Handler whether or not this event can be skipped when the system
// is being quit or reset. Typically set this to true for events which shut down the