diff --git a/common/include/Utilities/pxEvents.h b/common/include/Utilities/pxEvents.h index 062f040285..5c3cfbea5b 100644 --- a/common/include/Utilities/pxEvents.h +++ b/common/include/Utilities/pxEvents.h @@ -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; } diff --git a/common/src/Utilities/wxAppWithHelpers.cpp b/common/src/Utilities/wxAppWithHelpers.cpp index c53d204492..d0b242ea42 100644 --- a/common/src/Utilities/wxAppWithHelpers.cpp +++ b/common/src/Utilities/wxAppWithHelpers.cpp @@ -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() diff --git a/pcsx2/Interpreter.cpp b/pcsx2/Interpreter.cpp index c4ff7f04cd..0c6f5cd522 100644 --- a/pcsx2/Interpreter.cpp +++ b/pcsx2/Interpreter.cpp @@ -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 diff --git a/pcsx2/SaveState.h b/pcsx2/SaveState.h index 920f5385c7..0f47078e89 100644 --- a/pcsx2/SaveState.h +++ b/pcsx2/SaveState.h @@ -101,8 +101,6 @@ namespace Exception }; } -typedef SafeArray VmStateBuffer; - // -------------------------------------------------------------------------------------- // SaveStateBase class // -------------------------------------------------------------------------------------- diff --git a/pcsx2/System.h b/pcsx2/System.h index 178634d3af..37a3ed7a26 100644 --- a/pcsx2/System.h +++ b/pcsx2/System.h @@ -26,6 +26,8 @@ static const int PCSX2_VersionLo = 7; class SysCoreThread; class CpuInitializerSet; +typedef SafeArray VmStateBuffer; + // -------------------------------------------------------------------------------------- // SysCoreAllocations class // -------------------------------------------------------------------------------------- diff --git a/pcsx2/System/SysCoreThread.cpp b/pcsx2/System/SysCoreThread.cpp index c418bf09a1..63259ab691 100644 --- a/pcsx2/System/SysCoreThread.cpp +++ b/pcsx2/System/SysCoreThread.cpp @@ -31,8 +31,6 @@ #include -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(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(); } diff --git a/pcsx2/System/SysThreads.h b/pcsx2/System/SysThreads.h index d616f749fa..6fe663ed92 100644 --- a/pcsx2/System/SysThreads.h +++ b/pcsx2/System/SysThreads.h @@ -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 VmStateBuffer; + // -------------------------------------------------------------------------------------- // SysThreadBase // -------------------------------------------------------------------------------------- @@ -156,44 +158,40 @@ 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 ); @@ -201,7 +199,6 @@ public: protected: void _reset_stuff_as_needed(); - virtual void CpuInitializeMess(); virtual void Start(); virtual void OnStart(); virtual void OnSuspendInThread(); diff --git a/pcsx2/gui/App.h b/pcsx2/gui/App.h index 989da14574..b51ecacb66 100644 --- a/pcsx2/gui/App.h +++ b/pcsx2/gui/App.h @@ -503,7 +503,7 @@ public: // -------------------------------------------------------------------------- void DetectCpuAndUserMode(); - void OpenConsoleLog(); + void OpenProgramLog(); void OpenMainFrame(); void PrepForExit(); void CleanupRestartable(); diff --git a/pcsx2/gui/AppCorePlugins.cpp b/pcsx2/gui/AppCorePlugins.cpp index 2ab90832e7..a57089b1d1 100644 --- a/pcsx2/gui/AppCorePlugins.cpp +++ b/pcsx2/gui/AppCorePlugins.cpp @@ -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 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" )) ); + + GetCorePlugins().Close( m_pid ); + _post_and_wait( paused_core ); - //Console.Error( "Unhandled std::exception in %s (ignored!):", __pxFUNCTION__ ); - //Console.Error( ex.what() ); + 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(); } diff --git a/pcsx2/gui/AppCoreThread.cpp b/pcsx2/gui/AppCoreThread.cpp index 64b64d968d..72db3ad345 100644 --- a/pcsx2/gui/AppCoreThread.cpp +++ b/pcsx2/gui/AppCoreThread.cpp @@ -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; } diff --git a/pcsx2/gui/AppCoreThread.h b/pcsx2/gui/AppCoreThread.h index 8c2141a080..22fe1cdc0c 100644 --- a/pcsx2/gui/AppCoreThread.h +++ b/pcsx2/gui/AppCoreThread.h @@ -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 ); @@ -86,8 +176,11 @@ public: virtual ~BaseScopedCoreThread() throw()=0; virtual void AllowResume(); virtual void DisallowResume(); - + + virtual bool PostToSysExec( BaseSysExecEvent_ScopedCore* msg ); + protected: + // Called from destructors -- do not make virtual!! void DoResume(); }; @@ -116,16 +209,16 @@ class ScopedCoreThreadClose : public BaseScopedCoreThread public: ScopedCoreThreadClose(); virtual ~ScopedCoreThreadClose() throw(); - + void LoadPlugins(); }; -struct ScopedCoreThreadPause : public BaseScopedCoreThread +struct ScopedCoreThreadPause : public BaseScopedCoreThread { typedef BaseScopedCoreThread _parent; public: - ScopedCoreThreadPause(); + ScopedCoreThreadPause( BaseSysExecEvent_ScopedCore* abuse_me=NULL ); virtual ~ScopedCoreThreadPause() throw(); }; diff --git a/pcsx2/gui/AppInit.cpp b/pcsx2/gui/AppInit.cpp index 8e5d0bb3be..545f50b6dd 100644 --- a/pcsx2/gui/AppInit.cpp +++ b/pcsx2/gui/AppInit.cpp @@ -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() diff --git a/pcsx2/gui/AppMain.cpp b/pcsx2/gui/AppMain.cpp index 9fe54151c0..a78c8ed2da 100644 --- a/pcsx2/gui/AppMain.cpp +++ b/pcsx2/gui/AppMain.cpp @@ -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. diff --git a/pcsx2/gui/AppSaveStates.h b/pcsx2/gui/AppSaveStates.h index a14e3f2e23..91b634367f 100644 --- a/pcsx2/gui/AppSaveStates.h +++ b/pcsx2/gui/AppSaveStates.h @@ -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(); diff --git a/pcsx2/gui/ConsoleLogger.cpp b/pcsx2/gui/ConsoleLogger.cpp index e8b6da9b5a..aa341e10c2 100644 --- a/pcsx2/gui/ConsoleLogger.cpp +++ b/pcsx2/gui/ConsoleLogger.cpp @@ -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 diff --git a/pcsx2/gui/Dialogs/FirstTimeWizard.cpp b/pcsx2/gui/Dialogs/FirstTimeWizard.cpp index a3d9baae8f..8c62fffb0c 100644 --- a/pcsx2/gui/Dialogs/FirstTimeWizard.cpp +++ b/pcsx2/gui/Dialogs/FirstTimeWizard.cpp @@ -184,7 +184,7 @@ FirstTimeWizard::~FirstTimeWizard() throw() static void _OpenConsole() { g_Conf->ProgLogBox.Visible = true; - wxGetApp().OpenConsoleLog(); + wxGetApp().OpenProgramLog(); } int FirstTimeWizard::ShowModal() diff --git a/pcsx2/gui/ExecutorThread.cpp b/pcsx2/gui/ExecutorThread.cpp index cbfbcefc3a..7408b2d554 100644 --- a/pcsx2/gui/ExecutorThread.cpp +++ b/pcsx2/gui/ExecutorThread.cpp @@ -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(); } diff --git a/pcsx2/gui/GlobalCommands.cpp b/pcsx2/gui/GlobalCommands.cpp index 97fac36ca2..2de663f83d 100644 --- a/pcsx2/gui/GlobalCommands.cpp +++ b/pcsx2/gui/GlobalCommands.cpp @@ -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() diff --git a/pcsx2/gui/MainFrame.cpp b/pcsx2/gui/MainFrame.cpp index ff1606e2d2..327d6bf3f6 100644 --- a/pcsx2/gui/MainFrame.cpp +++ b/pcsx2/gui/MainFrame.cpp @@ -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() ); diff --git a/pcsx2/gui/MainMenuClicks.cpp b/pcsx2/gui/MainMenuClicks.cpp index fc85885e2d..57baa6109f 100644 --- a/pcsx2/gui/MainMenuClicks.cpp +++ b/pcsx2/gui/MainMenuClicks.cpp @@ -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 ); } diff --git a/pcsx2/gui/Panels/PluginSelectorPanel.cpp b/pcsx2/gui/Panels/PluginSelectorPanel.cpp index b2402f3474..c21c469568 100644 --- a/pcsx2/gui/Panels/PluginSelectorPanel.cpp +++ b/pcsx2/gui/Panels/PluginSelectorPanel.cpp @@ -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(); } } diff --git a/pcsx2/gui/SysState.cpp b/pcsx2/gui/SysState.cpp index 3447535d21..24f1c88676 100644 --- a/pcsx2/gui/SysState.cpp +++ b/pcsx2/gui/SysState.cpp @@ -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(); -} - diff --git a/pcsx2/gui/UpdateUI.cpp b/pcsx2/gui/UpdateUI.cpp index 388e3e18dc..e6788042ef 100644 --- a/pcsx2/gui/UpdateUI.cpp +++ b/pcsx2/gui/UpdateUI.cpp @@ -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. >_< diff --git a/pcsx2/gui/pxEventThread.h b/pcsx2/gui/pxEventThread.h index 6db9f0b5ab..fb3d394a00 100644 --- a/pcsx2/gui/pxEventThread.h +++ b/pcsx2/gui/pxEventThread.h @@ -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