Add some new checks for blocking VM reset/shutdown while savestates are saving (avoids accidental corruption). Renamed a lot of the new event listener code so that it's consistent... ish.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2497 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2010-01-23 17:13:03 +00:00
parent bb3eda1524
commit fa04244375
26 changed files with 412 additions and 246 deletions

View File

@ -53,6 +53,13 @@ public:
bool IsReentrant() const { return Counter > 1; }
};
class IActionInvocation
{
public:
virtual ~IActionInvocation() throw() {}
virtual void InvokeAction()=0;
};
// --------------------------------------------------------------------------------------
// IDeletableObject
// --------------------------------------------------------------------------------------

View File

@ -54,7 +54,6 @@ public:
extern StartupParams g_Startup;
extern void States_Save( const wxString& file );
extern bool States_isSlotUsed(int num);
extern void States_FreezeCurrentSlot();

View File

@ -565,7 +565,7 @@ void memClearPageAddr(u32 vaddr)
///////////////////////////////////////////////////////////////////////////
// PS2 Memory Init / Reset / Shutdown
class mmap_PageFaultHandler : public IEventListener_PageFault
class mmap_PageFaultHandler : public EventListener_PageFault
{
protected:
void OnPageFaultEvent( const PageFaultInfo& info, bool& handled );

View File

@ -19,9 +19,14 @@
#include "App.h"
#include "HostGui.h"
#include "AppSaveStates.h"
#include "Utilities/EventSource.inl"
class _BaseStateThread;
template EventSource<IEventListener_SaveStateThread>;
static EventSource<IEventListener_SaveStateThread> m_evtsrc_SaveState;
// Used to hold the current state backup (fullcopy of PS2 memory and plugin states).
static SafeArray<u8> state_buffer;
@ -30,6 +35,10 @@ _BaseStateThread* current_state_thread = NULL;
// Simple lock boolean for the state buffer being in use by a thread.
static NonblockingMutex state_buffer_lock;
// This boolean tracks if a savestate is actively saving. When a state is saving we
// typically delay program termination to allow th state time to finish it's work.
static bool state_is_saving = false;
// This boolean is to keep the system from resuming emulation until the current state has completely
// uploaded or downloaded itself. It is only modified from the main thread, and should only be read
// from the main thread.
@ -44,32 +53,8 @@ static bool StateCopy_ForceClear()
state_buffer.Dispose();
}
class EventListener_AppExiting : public IEventListener_AppStatus
{
protected:
PersistentThread& m_thread;
public:
EventListener_AppExiting( PersistentThread& thr )
: m_thread( thr )
{
}
virtual ~EventListener_AppExiting() throw() {}
};
enum
{
StateThreadAction_None = 0,
StateThreadAction_Create,
StateThreadAction_Restore,
StateThreadAction_ZipToDisk,
StateThreadAction_UnzipFromDisk,
};
class _BaseStateThread : public PersistentThread,
public virtual IEventListener_AppStatus,
public virtual EventListener_AppStatus,
public virtual IDeletableObject
{
typedef PersistentThread _parent;
@ -93,6 +78,8 @@ public:
current_state_thread = NULL;
state_buffer_lock.Release(); // just in case;
}
virtual bool IsFreezing() const=0;
protected:
_BaseStateThread( const char* name, FnType_OnThreadComplete* onFinished )
@ -121,10 +108,7 @@ protected:
void AppStatusEvent_OnExit()
{
Cancel();
Pcsx2App& myapp( wxGetApp() );
myapp.RemoveListener( *this );
myapp.DeleteObject( *this );
wxGetApp().DeleteObject( this );
}
};
@ -142,6 +126,8 @@ public:
throw Exception::RuntimeError( L"Cannot complete state freeze request; the virtual machine state is reset.", _("You'll need to start a new virtual machine before you can save its state.") );
}
bool IsFreezing() const { return true; }
protected:
void OnStart()
{
@ -157,7 +143,7 @@ protected:
void OnCleanupInThread()
{
SendFinishEvent( StateThreadAction_Create );
SendFinishEvent( SaveStateAction_CreateFinished );
_parent::OnCleanupInThread();
}
};
@ -186,6 +172,8 @@ public:
{
if( m_gzfp != NULL ) gzclose( m_gzfp );
}
bool IsFreezing() const { return true; }
protected:
void OnStart()
@ -208,13 +196,15 @@ protected:
if( gzwrite( m_gzfp, state_buffer.GetPtr(curidx), thisBlockSize ) < thisBlockSize )
throw Exception::BadStream( m_filename );
curidx += thisBlockSize;
Yield( 1 );
Yield( 10 );
} while( curidx < state_buffer.GetSizeInBytes() );
Console.WriteLn( "State saved to disk without error." );
}
void OnCleanupInThread()
{
SendFinishEvent( StateThreadAction_ZipToDisk );
SendFinishEvent( SaveStateAction_ZipToDiskFinished );
_parent::OnCleanupInThread();
}
};
@ -250,6 +240,8 @@ public:
if( m_gzfp != NULL ) gzclose( m_gzfp );
}
bool IsFreezing() const { return false; }
protected:
void OnStart()
{
@ -281,11 +273,12 @@ protected:
void OnCleanupInThread()
{
SendFinishEvent( StateThreadAction_UnzipFromDisk );
SendFinishEvent( SaveStateAction_UnzipFromDiskFinished );
_parent::OnCleanupInThread();
}
};
void Pcsx2App::OnFreezeThreadFinished( wxCommandEvent& evt )
{
// clear the OnFreezeFinished to NULL now, in case of error.
@ -296,8 +289,13 @@ void Pcsx2App::OnFreezeThreadFinished( wxCommandEvent& evt )
{
ScopedPtr<PersistentThread> thr( (PersistentThread*)evt.GetClientData() );
if( !pxAssertDev( thr != NULL, "NULL thread handle on freeze finished?" ) ) return;
current_state_thread = NULL;
state_buffer_lock.Release();
--sys_resume_lock;
m_evtsrc_SaveState.Dispatch( (SaveStateActionType)evt.GetInt() );
thr->RethrowException();
}
@ -314,7 +312,7 @@ static wxString zip_dest_filename;
static void OnFinished_ZipToDisk( const wxCommandEvent& evt )
{
if( !pxAssertDev( evt.GetInt() == StateThreadAction_Create, "Unexpected StateThreadAction value, aborting save." ) ) return;
if( !pxAssertDev( evt.GetInt() == SaveStateAction_CreateFinished, "Unexpected StateThreadAction value, aborting save." ) ) return;
if( zip_dest_filename.IsEmpty() )
{
@ -328,11 +326,80 @@ static void OnFinished_ZipToDisk( const wxCommandEvent& evt )
CoreThread.Resume();
}
class InvokeAction_WhenSaveComplete :
public IEventListener_SaveStateThread,
public IDeletableObject
{
protected:
IActionInvocation* m_action;
public:
InvokeAction_WhenSaveComplete( IActionInvocation* action )
{
m_action = action;
}
virtual ~InvokeAction_WhenSaveComplete() throw() {}
void SaveStateAction_OnZipToDiskFinished()
{
if( m_action )
{
m_action->InvokeAction();
safe_delete( m_action );
}
wxGetApp().DeleteObject( this );
}
};
class InvokeAction_WhenStateCopyComplete : public InvokeAction_WhenSaveComplete
{
public:
InvokeAction_WhenStateCopyComplete( IActionInvocation* action )
: InvokeAction_WhenSaveComplete( action )
{
}
virtual ~InvokeAction_WhenStateCopyComplete() throw() {}
void SaveStateAction_OnCreateFinished()
{
SaveStateAction_OnZipToDiskFinished();
}
};
// =====================================================================================================
// StateCopy Public Interface
// =====================================================================================================
bool StateCopy_InvokeOnSaveComplete( IActionInvocation* sst )
{
AffinityAssert_AllowFromMain();
if( current_state_thread == NULL || !current_state_thread->IsFreezing() )
{
delete sst;
return false;
}
m_evtsrc_SaveState.Add( new InvokeAction_WhenSaveComplete( sst ) );
return true;
}
bool StateCopy_InvokeOnCopyComplete( IActionInvocation* sst )
{
AffinityAssert_AllowFromMain();
if( current_state_thread == NULL || !current_state_thread->IsFreezing() )
{
delete sst;
return false;
}
m_evtsrc_SaveState.Add( new InvokeAction_WhenStateCopyComplete( sst ) );
return true;
}
void StateCopy_SaveToFile( const wxString& file )
{
if( state_buffer_lock.IsLocked() ) return;

View File

@ -15,7 +15,6 @@
#pragma once
// This shouldn't break Win compiles, but it does.
#include "PS2Edefs.h"
#include "System.h"
@ -253,17 +252,3 @@ public:
bool IsFinished() const { return m_idx >= m_memory.GetSizeInBytes(); }
};
extern bool StateCopy_IsValid();
extern void StateCopy_FreezeToMem();
extern void StateCopy_FreezeToMem_Blocking();
extern void StateCopy_ThawFromMem_Blocking();
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();
extern bool StateCopy_IsBusy();
extern const SafeArray<u8>* StateCopy_GetBuffer();

View File

@ -26,12 +26,12 @@ template class EventSource< IEventListener_PageFault >;
SrcType_PageFault Source_PageFault;
IEventListener_PageFault::IEventListener_PageFault()
EventListener_PageFault::EventListener_PageFault()
{
Source_PageFault.Add( *this );
}
IEventListener_PageFault::~IEventListener_PageFault() throw()
EventListener_PageFault::~EventListener_PageFault() throw()
{
Source_PageFault.Remove( *this );
}

View File

@ -42,8 +42,7 @@ public:
typedef PageFaultInfo EvtParams;
public:
IEventListener_PageFault();
virtual ~IEventListener_PageFault() throw();
virtual ~IEventListener_PageFault() throw() {}
virtual void DispatchEvent( const PageFaultInfo& evtinfo, bool& handled )
{
@ -59,6 +58,13 @@ protected:
virtual void OnPageFaultEvent( const PageFaultInfo& evtinfo, bool& handled ) {}
};
class EventListener_PageFault : public IEventListener_PageFault
{
public:
EventListener_PageFault();
virtual ~EventListener_PageFault() throw();
};
class SrcType_PageFault : public EventSource<IEventListener_PageFault>
{
protected:

View File

@ -269,19 +269,6 @@ void SysCoreThread::CpuInitializeMess()
{
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();
StateCopy_ThawFromMem_Blocking();
m_hasValidState = true;
m_resetVirtualMachine = false;
return;
}
_reset_stuff_as_needed();
ScopedBool_ClearOnError sbcoe( m_hasValidState );

View File

@ -215,9 +215,9 @@ public:
virtual void ChangeCdvdSource( CDVD_SourceType type );
protected:
void CpuInitializeMess();
void _reset_stuff_as_needed();
virtual void CpuInitializeMess();
virtual void Start();
virtual void OnSuspendInThread();
virtual void OnPauseInThread() {}

View File

@ -510,7 +510,7 @@ public:
void DetectCpuAndUserMode();
void OpenConsoleLog();
void OpenMainFrame();
bool PrepForExit( bool canCancel );
void PrepForExit();
void CleanupRestartable();
void CleanupResources();
void WipeUserModeSettings();
@ -635,6 +635,8 @@ protected:
virtual void DoCpuReset();
virtual void DoThreadDeadlocked();
virtual void CpuInitializeMess();
};
DECLARE_APP(Pcsx2App)

View File

@ -48,7 +48,6 @@ void AppCoreThread::Reset()
void AppCoreThread::DoThreadDeadlocked()
{
//wxGetApp().PostCommand( );
wxGetApp().DoStuckThread( *this );
}
@ -94,7 +93,7 @@ void AppCoreThread::Resume()
// Resume failed for some reason, so update GUI statuses and post a message to
// try again on the resume.
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreStatus_Suspended );
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreThread_Suspended );
if( (m_ExecMode != ExecMode_Closing) || (m_ExecMode != ExecMode_Pausing) )
{
@ -121,7 +120,7 @@ void AppCoreThread::ChangeCdvdSource( CDVD_SourceType type )
void AppCoreThread::DoCpuReset()
{
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreStatus_Reset );
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreThread_Reset );
_parent::DoCpuReset();
}
@ -143,13 +142,13 @@ void AppCoreThread::OnResumeReady()
void AppCoreThread::OnResumeInThread( bool isSuspended )
{
_parent::OnResumeInThread( isSuspended );
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreStatus_Resumed );
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreThread_Resumed );
}
void AppCoreThread::OnSuspendInThread()
{
_parent::OnSuspendInThread();
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreStatus_Suspended );
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreThread_Suspended );
}
// Called whenever the thread has terminated, for either regular or irregular reasons.
@ -158,7 +157,7 @@ void AppCoreThread::OnSuspendInThread()
// the new (lack of) thread status, so this posts a message to the App to do so.
void AppCoreThread::OnCleanupInThread()
{
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreStatus_Stopped );
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreThread_Stopped );
_parent::OnCleanupInThread();
}
@ -194,9 +193,30 @@ void AppCoreThread::ApplySettings( const Pcsx2Config& src )
_parent::ApplySettings( fixup );
}
void AppCoreThread::CpuInitializeMess()
{
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();
StateCopy_ThawFromMem_Blocking();
m_hasValidState = true;
m_resetVirtualMachine = false;
return;
}
_parent::CpuInitializeMess();
}
void AppCoreThread::ExecuteTaskInThread()
{
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreStatus_Started );
wxGetApp().PostCommand( pxEvt_CoreThreadStatus, CoreThread_Started );
_parent::ExecuteTaskInThread();
// ----------------------------------------------------------------------------

View File

@ -19,12 +19,12 @@
enum CoreThreadStatus
{
CoreStatus_Indeterminate,
CoreStatus_Started,
CoreStatus_Resumed,
CoreStatus_Suspended,
CoreStatus_Reset,
CoreStatus_Stopped,
CoreThread_Indeterminate,
CoreThread_Started,
CoreThread_Resumed,
CoreThread_Suspended,
CoreThread_Reset,
CoreThread_Stopped,
};
enum AppEventType
@ -37,14 +37,14 @@ enum AppEventType
enum PluginEventType
{
PluginsEvt_Loaded,
PluginsEvt_Init,
PluginsEvt_Opening, // dispatched prior to plugins being opened
PluginsEvt_Opened, // dispatched after plugins are opened
PluginsEvt_Closing, // dispatched prior to plugins being closed
PluginsEvt_Closed, // dispatched after plugins are closed
PluginsEvt_Shutdown,
PluginsEvt_Unloaded,
CorePlugins_Loaded,
CorePlugins_Init,
CorePlugins_Opening, // dispatched prior to plugins being opened
CorePlugins_Opened, // dispatched after plugins are opened
CorePlugins_Closing, // dispatched prior to plugins being closed
CorePlugins_Closed, // dispatched after plugins are closed
CorePlugins_Shutdown,
CorePlugins_Unloaded,
};
struct AppEventInfo
@ -78,29 +78,23 @@ public:
typedef CoreThreadStatus EvtParams;
public:
IEventListener_CoreThread();
virtual ~IEventListener_CoreThread() throw();
virtual ~IEventListener_CoreThread() throw() {}
virtual void DispatchEvent( const CoreThreadStatus& status )
{
switch( status )
{
case CoreStatus_Indeterminate: OnCoreStatus_Indeterminate(); break;
case CoreStatus_Started: OnCoreStatus_Started(); break;
case CoreStatus_Resumed: OnCoreStatus_Resumed(); break;
case CoreStatus_Suspended: OnCoreStatus_Suspended(); break;
case CoreStatus_Reset: OnCoreStatus_Reset(); break;
case CoreStatus_Stopped: OnCoreStatus_Stopped(); break;
}
}
virtual void DispatchEvent( const CoreThreadStatus& status );
protected:
virtual void OnCoreStatus_Indeterminate() {}
virtual void OnCoreStatus_Started() {}
virtual void OnCoreStatus_Resumed() {}
virtual void OnCoreStatus_Suspended() {}
virtual void OnCoreStatus_Reset() {}
virtual void OnCoreStatus_Stopped() {}
virtual void CoreThread_OnStarted() {}
virtual void CoreThread_OnResumed() {}
virtual void CoreThread_OnSuspended() {}
virtual void CoreThread_OnReset() {}
virtual void CoreThread_OnStopped() {}
};
class EventListener_CoreThread : public IEventListener_CoreThread
{
public:
EventListener_CoreThread();
virtual ~EventListener_CoreThread() throw();
};
// --------------------------------------------------------------------------------------
@ -112,34 +106,26 @@ public:
typedef PluginEventType EvtParams;
public:
IEventListener_Plugins();
virtual ~IEventListener_Plugins() throw();
virtual ~IEventListener_Plugins() throw() {}
virtual void DispatchEvent( const PluginEventType& pevt )
{
switch( pevt )
{
case PluginsEvt_Loaded: OnPluginsEvt_Loaded(); break;
case PluginsEvt_Init: OnPluginsEvt_Init(); break;
case PluginsEvt_Opening: OnPluginsEvt_Opening(); break;
case PluginsEvt_Opened: OnPluginsEvt_Opened(); break;
case PluginsEvt_Closing: OnPluginsEvt_Closing(); break;
case PluginsEvt_Closed: OnPluginsEvt_Closed(); break;
case PluginsEvt_Shutdown: OnPluginsEvt_Shutdown(); break;
case PluginsEvt_Unloaded: OnPluginsEvt_Unloaded(); break;
}
}
virtual void DispatchEvent( const PluginEventType& pevt );
protected:
virtual void OnPluginsEvt_Loaded() {}
virtual void OnPluginsEvt_Init() {}
virtual void OnPluginsEvt_Opening() {} // dispatched prior to plugins being opened
virtual void OnPluginsEvt_Opened() {} // dispatched after plugins are opened
virtual void OnPluginsEvt_Closing() {} // dispatched prior to plugins being closed
virtual void OnPluginsEvt_Closed() {} // dispatched after plugins are closed
virtual void OnPluginsEvt_Shutdown() {}
virtual void OnPluginsEvt_Unloaded() {}
virtual void CorePlugins_OnLoaded() {}
virtual void CorePlugins_OnInit() {}
virtual void CorePlugins_OnOpening() {} // dispatched prior to plugins being opened
virtual void CorePlugins_OnOpened() {} // dispatched after plugins are opened
virtual void CorePlugins_OnClosing() {} // dispatched prior to plugins being closed
virtual void CorePlugins_OnClosed() {} // dispatched after plugins are closed
virtual void CorePlugins_OnShutdown() {}
virtual void CorePlugins_OnUnloaded() {}
};
class EventListener_Plugins : public IEventListener_Plugins
{
public:
EventListener_Plugins();
virtual ~EventListener_Plugins() throw();
};
// --------------------------------------------------------------------------------------
@ -151,8 +137,7 @@ public:
typedef AppEventInfo EvtParams;
public:
IEventListener_AppStatus();
virtual ~IEventListener_AppStatus() throw();
virtual ~IEventListener_AppStatus() throw() {}
virtual void DispatchEvent( const AppEventInfo& evtinfo );
@ -162,6 +147,13 @@ protected:
virtual void AppStatusEvent_OnExit() {}
};
class EventListener_AppStatus : public IEventListener_AppStatus
{
public:
EventListener_AppStatus();
virtual ~EventListener_AppStatus() throw();
};
// --------------------------------------------------------------------------------------
// EventListenerHelpers (CoreThread / Plugins / AppStatus)
// --------------------------------------------------------------------------------------
@ -172,7 +164,7 @@ protected:
//
template< typename TypeToDispatchTo >
class EventListenerHelper_CoreThread : public IEventListener_CoreThread
class EventListenerHelper_CoreThread : public EventListener_CoreThread
{
public:
TypeToDispatchTo& Owner;
@ -190,16 +182,16 @@ public:
virtual ~EventListenerHelper_CoreThread() throw() {}
protected:
void OnCoreStatus_Indeterminate() { Owner.OnCoreStatus_Indeterminate(); }
void OnCoreStatus_Started() { Owner.OnCoreStatus_Started(); }
void OnCoreStatus_Resumed() { Owner.OnCoreStatus_Resumed(); }
void OnCoreStatus_Suspended() { Owner.OnCoreStatus_Suspended(); }
void OnCoreStatus_Reset() { Owner.OnCoreStatus_Reset(); }
void OnCoreStatus_Stopped() { Owner.OnCoreStatus_Stopped(); }
void OnCoreThread_Indeterminate() { Owner.OnCoreThread_Indeterminate(); }
void CoreThread_OnStarted() { Owner.OnCoreThread_Started(); }
void CoreThread_OnResumed() { Owner.OnCoreThread_Resumed(); }
void CoreThread_OnSuspended() { Owner.OnCoreThread_Suspended(); }
void CoreThread_OnReset() { Owner.OnCoreThread_Reset(); }
void CoreThread_OnStopped() { Owner.OnCoreThread_Stopped(); }
};
template< typename TypeToDispatchTo >
class EventListenerHelper_Plugins : public IEventListener_Plugins
class EventListenerHelper_Plugins : public EventListener_Plugins
{
public:
TypeToDispatchTo& Owner;
@ -217,18 +209,18 @@ public:
virtual ~EventListenerHelper_Plugins() throw() {}
protected:
void OnPluginsEvt_Loaded() { Owner.OnPluginsEvt_Loaded(); }
void OnPluginsEvt_Init() { Owner.OnPluginsEvt_Init(); }
void OnPluginsEvt_Opening() { Owner.OnPluginsEvt_Opening(); }
void OnPluginsEvt_Opened() { Owner.OnPluginsEvt_Opened(); }
void OnPluginsEvt_Closing() { Owner.OnPluginsEvt_Closing(); }
void OnPluginsEvt_Closed() { Owner.OnPluginsEvt_Closed(); }
void OnPluginsEvt_Shutdown() { Owner.OnPluginsEvt_Shutdown(); }
void OnPluginsEvt_Unloaded() { Owner.OnPluginsEvt_Unloaded(); }
void CorePlugins_OnLoaded() { Owner.OnCorePlugins_Loaded(); }
void CorePlugins_OnInit() { Owner.OnCorePlugins_Init(); }
void CorePlugins_OnOpening() { Owner.OnCorePlugins_Opening(); }
void CorePlugins_OnOpened() { Owner.OnCorePlugins_Opened(); }
void CorePlugins_OnClosing() { Owner.OnCorePlugins_Closing(); }
void CorePlugins_OnClosed() { Owner.OnCorePlugins_Closed(); }
void CorePlugins_OnShutdown() { Owner.OnCorePlugins_Shutdown(); }
void CorePlugins_OnUnloaded() { Owner.OnCorePlugins_Unloaded(); }
};
template< typename TypeToDispatchTo >
class EventListenerHelper_AppStatus : public IEventListener_AppStatus
class EventListenerHelper_AppStatus : public EventListener_AppStatus
{
public:
TypeToDispatchTo& Owner;

View File

@ -28,32 +28,63 @@ AppSettingsEventInfo::AppSettingsEventInfo( IniInterface& ini )
{
}
IEventListener_CoreThread::IEventListener_CoreThread()
EventListener_CoreThread::EventListener_CoreThread()
{
wxGetApp().AddListener( this );
}
IEventListener_CoreThread::~IEventListener_CoreThread() throw()
EventListener_CoreThread::~EventListener_CoreThread() throw()
{
wxGetApp().RemoveListener( this );
}
IEventListener_Plugins::IEventListener_Plugins()
void IEventListener_CoreThread::DispatchEvent( const CoreThreadStatus& status )
{
switch( status )
{
case CoreThread_Started: CoreThread_OnStarted(); break;
case CoreThread_Resumed: CoreThread_OnResumed(); break;
case CoreThread_Suspended: CoreThread_OnSuspended(); break;
case CoreThread_Reset: CoreThread_OnReset(); break;
case CoreThread_Stopped: CoreThread_OnStopped(); break;
jNO_DEFAULT;
}
}
EventListener_Plugins::EventListener_Plugins()
{
wxGetApp().AddListener( this );
}
IEventListener_Plugins::~IEventListener_Plugins() throw()
EventListener_Plugins::~EventListener_Plugins() throw()
{
wxGetApp().RemoveListener( this );
}
IEventListener_AppStatus::IEventListener_AppStatus()
void IEventListener_Plugins::DispatchEvent( const PluginEventType& pevt )
{
switch( pevt )
{
case CorePlugins_Loaded: CorePlugins_OnLoaded(); break;
case CorePlugins_Init: CorePlugins_OnInit(); break;
case CorePlugins_Opening: CorePlugins_OnOpening(); break;
case CorePlugins_Opened: CorePlugins_OnOpened(); break;
case CorePlugins_Closing: CorePlugins_OnClosing(); break;
case CorePlugins_Closed: CorePlugins_OnClosed(); break;
case CorePlugins_Shutdown: CorePlugins_OnShutdown(); break;
case CorePlugins_Unloaded: CorePlugins_OnUnloaded(); break;
jNO_DEFAULT;
}
}
EventListener_AppStatus::EventListener_AppStatus()
{
wxGetApp().AddListener( this );
}
IEventListener_AppStatus::~IEventListener_AppStatus() throw()
EventListener_AppStatus::~EventListener_AppStatus() throw()
{
wxGetApp().RemoveListener( this );
}
@ -68,6 +99,6 @@ void IEventListener_AppStatus::DispatchEvent( const AppEventInfo& evtinfo )
break;
case AppStatus_SettingsApplied: AppStatusEvent_OnSettingsApplied(); break;
case AppStatus_Exiting: AppStatusEvent_OnExit(); break;
case AppStatus_Exiting: AppStatusEvent_OnExit(); break;
}
}

View File

@ -17,7 +17,7 @@
#include "IniInterface.h"
#include "MainFrame.h"
#include "Plugins.h"
#include "SaveState.h"
#include "AppSaveStates.h"
#include "ps2/BiosTools.h"
#include "Dialogs/ModalPopups.h"
@ -41,7 +41,6 @@ DEFINE_EVENT_TYPE( pxEvt_InvokeMethod );
DEFINE_EVENT_TYPE( pxEvt_LogicalVsync );
DEFINE_EVENT_TYPE( pxEvt_OpenModalDialog );
//DEFINE_EVENT_TYPE( pxEvt_OpenDialog_StuckThread );
bool UseAdminMode = false;
wxDirName SettingsFolder;
@ -276,14 +275,14 @@ void Pcsx2App::OnCoreThreadStatus( wxCommandEvent& evt )
switch( status )
{
case CoreStatus_Started:
case CoreStatus_Reset:
case CoreStatus_Stopped:
case CoreThread_Started:
case CoreThread_Reset:
case CoreThread_Stopped:
FpsManager.Reset();
break;
case CoreStatus_Resumed:
case CoreStatus_Suspended:
case CoreThread_Resumed:
case CoreThread_Suspended:
FpsManager.Resume();
break;
}
@ -538,64 +537,22 @@ void Pcsx2App::HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent&
}
}
class CancelCoreThreadWhenSaveStateDone : public IEventListener_CoreThread,
public IDeletableObject
{
public:
virtual ~CancelCoreThreadWhenSaveStateDone() throw() {}
void OnCoreStatus_Resumed()
{
Pcsx2App& myapp( wxGetApp() );
myapp.DeleteObject( this );
myapp.PostMenuAction( MenuId_Exit );
}
};
// Common exit handler which can be called from any event (though really it should
// be called only from CloseWindow handlers since that's the more appropriate way
// to handle cancelable window closures)
//
// returns true if the app can close, or false if the close event was canceled by
// the glorious user, whomever (s)he-it might be.
bool Pcsx2App::PrepForExit( bool canCancel )
void Pcsx2App::PrepForExit()
{
// If a savestate is saving, we should wait until it finishes. Otherwise the user
// might lose data.
if( StateCopy_IsBusy() )
{
new CancelCoreThreadWhenSaveStateDone();
throw Exception::CancelEvent( "Savestate in progress, close event delayed until action is complete." );
}
CancelLoadingPlugins();
/*
if( canCancel )
{
// TODO: Confirm with the user?
// Problem: Suspend is often slow because it needs to wait until the current EE frame
// has finished processing (if the GS or logging has incurred severe overhead this makes
// closing PCSX2 difficult). A non-blocking suspend with modal dialog might suffice
// however. --air
bool resume = CoreThread.Suspend();
if( false )
{
if(resume) CoreThread.Resume();
return false;
}
}*/
DispatchEvent( AppStatus_Exiting );
// This should be called by OnExit(), but sometimes wxWidgets fails to call OnExit(), so
// do it here just in case (no harm anyway -- OnExit is the next logical step after
// CloseWindow returns true from the TopLevel window).
CleanupRestartable();
return true;
}
// This method generates debug assertions if the MainFrame handle is NULL (typically
@ -968,8 +925,8 @@ void Pcsx2App::OnSysExecute( wxCommandEvent& evt )
void Pcsx2App::SysReset()
{
StateCopy_Clear();
CoreThread.Reset();
CoreThread.Cancel();
CoreThread.ReleaseResumeLock();
m_CorePlugins = NULL;
}

77
pcsx2/gui/AppSaveStates.h Normal file
View File

@ -0,0 +1,77 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2009 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "SaveState.h"
enum SaveStateActionType
{
SaveStateAction_CreateFinished,
SaveStateAction_RestoreFinished,
SaveStateAction_ZipToDiskFinished,
SaveStateAction_UnzipFromDiskFinished,
};
// --------------------------------------------------------------------------------------
// IEventListener_SaveStateThread
// --------------------------------------------------------------------------------------
class IEventListener_SaveStateThread : public IEventDispatcher<SaveStateActionType>
{
public:
typedef SaveStateActionType EvtParams;
public:
IEventListener_SaveStateThread() {}
virtual ~IEventListener_SaveStateThread() throw() {}
virtual void DispatchEvent( const SaveStateActionType& status )
{
switch( status )
{
case SaveStateAction_CreateFinished: SaveStateAction_OnCreateFinished(); break;
case SaveStateAction_RestoreFinished: SaveStateAction_OnRestoreFinished(); break;
case SaveStateAction_ZipToDiskFinished: SaveStateAction_OnZipToDiskFinished(); break;
case SaveStateAction_UnzipFromDiskFinished: SaveStateAction_OnUnzipFromDiskFinished(); break;
jNO_DEFAULT;
}
}
protected:
virtual void SaveStateAction_OnCreateFinished() {}
virtual void SaveStateAction_OnRestoreFinished() {}
virtual void SaveStateAction_OnZipToDiskFinished() {}
virtual void SaveStateAction_OnUnzipFromDiskFinished() {}
};
extern bool StateCopy_InvokeOnSaveComplete( IActionInvocation* sst );
extern bool StateCopy_InvokeOnCopyComplete( IActionInvocation* sst );
extern bool StateCopy_IsValid();
extern void StateCopy_FreezeToMem();
extern void StateCopy_FreezeToMem_Blocking();
extern void StateCopy_ThawFromMem_Blocking();
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();
extern bool StateCopy_IsBusy();
extern const SafeArray<u8>* StateCopy_GetBuffer();

View File

@ -90,8 +90,8 @@ public:
// pxLogTextCtrl
// --------------------------------------------------------------------------------------
class pxLogTextCtrl : public wxTextCtrl,
public IEventListener_CoreThread,
public IEventListener_Plugins
public EventListener_CoreThread,
public EventListener_Plugins
{
protected:
//EventListenerHelper_CoreThread<pxLogTextCtrl> m_listener_CoreThread;

View File

@ -16,7 +16,7 @@
#include "PrecompiledHeader.h"
#include "MainFrame.h"
#include "HostGui.h"
#include "SaveState.h"
#include "AppSaveStates.h"
#include "GS.h"
#include "Dump.h"

View File

@ -15,6 +15,7 @@
#include "PrecompiledHeader.h"
#include "MainFrame.h"
#include "AppSaveStates.h"
#include "ConsoleLogger.h"
#include "MSWstuff.h"
@ -70,6 +71,8 @@ void MainEmuFrame::UpdateIsoSrcSelection()
//
void MainEmuFrame::OnCloseWindow(wxCloseEvent& evt)
{
CoreThread.Suspend();
bool isClosing = false;
if( !evt.CanVeto() )
@ -79,10 +82,20 @@ void MainEmuFrame::OnCloseWindow(wxCloseEvent& evt)
}
else
{
isClosing = wxGetApp().PrepForExit( evt.CanVeto() );
if( !isClosing ) evt.Veto( true );
// TODO : Add confirmation prior to exit here!
// Problem: Suspend is often slow because it needs to wait until the current EE frame
// has finished processing (if the GS or logging has incurred severe overhead this makes
// closing PCSX2 difficult). A non-blocking suspend with modal dialog might suffice
// however. --air
if( StateCopy_InvokeOnSaveComplete( new InvokeAction_MenuCommand( MenuId_Exit ) ) ) return;
}
if( isClosing )
wxGetApp().PrepForExit();
else
evt.Veto( true );
sApp.OnMainFrameClosed();
evt.Skip();

View File

@ -20,6 +20,7 @@
#include <wx/docview.h>
#include "App.h"
#include "AppSaveStates.h"
enum LimiterModeType
{
@ -33,7 +34,7 @@ extern LimiterModeType g_LimiterMode;
// --------------------------------------------------------------------------------------
// GSPanel
// --------------------------------------------------------------------------------------
class GSPanel : public wxWindow, public IEventListener_AppStatus
class GSPanel : public wxWindow, public EventListener_AppStatus
{
typedef wxWindow _parent;
@ -71,7 +72,7 @@ protected:
// --------------------------------------------------------------------------------------
// GSFrame
// --------------------------------------------------------------------------------------
class GSFrame : public wxFrame, public IEventListener_AppStatus
class GSFrame : public wxFrame, public EventListener_AppStatus
{
typedef wxFrame _parent;
@ -153,13 +154,30 @@ public:
operator const wxMenu*() const { return &MyMenu; }
};
class InvokeAction_MenuCommand : public IActionInvocation
{
protected:
MenuIdentifiers m_menu_cmd;
public:
InvokeAction_MenuCommand( MenuIdentifiers menu_command )
{
m_menu_cmd = menu_command;
}
virtual void InvokeAction()
{
wxGetApp().PostMenuAction( m_menu_cmd );
}
};
// --------------------------------------------------------------------------------------
// MainEmuFrame
// --------------------------------------------------------------------------------------
class MainEmuFrame : public wxFrame,
public IEventListener_Plugins,
public IEventListener_CoreThread,
public IEventListener_AppStatus
public EventListener_Plugins,
public EventListener_CoreThread,
public EventListener_AppStatus
{
protected:
// EventListenerHelper_Plugins<MainEmuFrame> m_listener_plugins;

View File

@ -68,9 +68,11 @@ static void WipeSettings()
// manually from explorer does work. Can't think of a good work-around at the moment. --air
//wxRmdir( GetSettingsFolder().ToString() );
g_Conf = new AppConfig();
}
class RestartEverything_WhenCoreThreadStops : public IEventListener_CoreThread,
class RestartEverything_WhenCoreThreadStops : public EventListener_CoreThread,
public virtual IDeletableObject
{
public:
@ -78,20 +80,21 @@ public:
virtual ~RestartEverything_WhenCoreThreadStops() throw() {}
protected:
virtual void OnCoreStatus_Stopped()
virtual void CoreThread_OnStopped()
{
wxGetApp().DeleteObject( this );
WipeSettings();
}
};
class CancelCoreThread_WhenSaveStateDone : public IEventListener_CoreThread,
class CancelCoreThread_WhenSaveStateDone :
public EventListener_CoreThread,
public IDeletableObject
{
public:
virtual ~CancelCoreThread_WhenSaveStateDone() throw() {}
void OnCoreStatus_Resumed()
void CoreThread_OnResumed()
{
wxGetApp().DeleteObject( this );
CoreThread.Cancel();
@ -336,7 +339,8 @@ void MainEmuFrame::Menu_SuspendResume_Click(wxCommandEvent &event)
void MainEmuFrame::Menu_SysReset_Click(wxCommandEvent &event)
{
//if( !SysHasValidState() ) return;
if( StateCopy_InvokeOnCopyComplete( new InvokeAction_MenuCommand(MenuId_Sys_Reset) ) ) return;
sApp.SysReset();
sApp.SysExecute();
//GetMenuBar()->Enable( MenuId_Sys_Reset, true );
@ -344,7 +348,9 @@ void MainEmuFrame::Menu_SysReset_Click(wxCommandEvent &event)
void MainEmuFrame::Menu_SysShutdown_Click(wxCommandEvent &event)
{
if( !SysHasValidState() ) return;
if( !SysHasValidState() && g_plugins == NULL ) return;
if( StateCopy_InvokeOnCopyComplete( new InvokeAction_MenuCommand(MenuId_Sys_Shutdown) ) ) return;
sApp.SysReset();
GetMenuBar()->Enable( MenuId_Sys_Shutdown, false );
}

View File

@ -403,7 +403,7 @@ namespace Panels
// PluginSelectorPanel
// --------------------------------------------------------------------------------------
class PluginSelectorPanel: public BaseSelectorPanel,
public IEventListener_Plugins
public EventListener_Plugins
{
protected:
// ----------------------------------------------------------------------------

View File

@ -15,8 +15,8 @@
#include "PrecompiledHeader.h"
#include "App.h"
#include "AppSaveStates.h"
#include "Plugins.h"
#include "SaveState.h"
#include "Utilities/ScopedPtr.h"
#include "ConfigurationPanels.h"
#include "Dialogs/ModalPopups.h"
@ -232,7 +232,7 @@ void Panels::PluginSelectorPanel::ComboBoxPanel::Reset()
// =====================================================================================================
void Panels::PluginSelectorPanel::DispatchEvent( const PluginEventType& evt )
{
if( (evt != PluginsEvt_Loaded) && (evt != PluginsEvt_Unloaded) ) return; // everything else we don't care about
if( (evt != CorePlugins_Loaded) && (evt != CorePlugins_Unloaded) ) return; // everything else we don't care about
if( IsBeingDeleted() ) return;

View File

@ -54,29 +54,29 @@ public:
virtual ~AppPluginManager() throw()
{
sApp.PostPluginStatus( PluginsEvt_Unloaded );
sApp.PostPluginStatus( CorePlugins_Unloaded );
}
void Init()
{
SetSettingsFolder( GetSettingsFolder().ToString() );
_parent::Init();
sApp.PostPluginStatus( PluginsEvt_Init );
sApp.PostPluginStatus( CorePlugins_Init );
}
void Shutdown()
{
_parent::Shutdown();
sApp.PostPluginStatus( PluginsEvt_Shutdown );
sApp.PostPluginStatus( CorePlugins_Shutdown );
}
void Close()
{
if( !NeedsClose() ) return;
sApp.PostPluginStatus( PluginsEvt_Closing );
sApp.PostPluginStatus( CorePlugins_Closing );
_parent::Close();
sApp.PostPluginStatus( PluginsEvt_Closed );
sApp.PostPluginStatus( CorePlugins_Closed );
}
void Open()
@ -85,9 +85,9 @@ public:
if( !NeedsOpen() ) return;
sApp.PostPluginStatus( PluginsEvt_Opening );
sApp.PostPluginStatus( CorePlugins_Opening );
_parent::Open();
sApp.PostPluginStatus( PluginsEvt_Opened );
sApp.PostPluginStatus( CorePlugins_Opened );
}
// Yay, this plugin is guaranteed to always be opened first and closed last.
@ -319,7 +319,7 @@ void Pcsx2App::OnLoadPluginsComplete( wxCommandEvent& evt )
if( fn_tmp != NULL ) fn_tmp( evt );
PostPluginStatus( PluginsEvt_Loaded );
PostPluginStatus( CorePlugins_Loaded );
}
void Pcsx2App::CancelLoadingPlugins()

View File

@ -19,7 +19,7 @@
// RecentIsoManager
// --------------------------------------------------------------------------------------
class RecentIsoManager : public wxEvtHandler,
public IEventListener_AppStatus
public EventListener_AppStatus
{
protected:
struct RecentItem

View File

@ -15,6 +15,7 @@
#include "PrecompiledHeader.h"
#include "App.h"
#include "AppSaveStates.h"
#include "Common.h"
#include "HostGui.h"
@ -24,12 +25,6 @@
StartupParams g_Startup;
// Save state save-to-file (or slot) helpers.
void States_Save( const wxString& file )
{
StateCopy_SaveToFile( file );
}
// --------------------------------------------------------------------------------------
// Saveslot Section
// --------------------------------------------------------------------------------------

View File

@ -2559,6 +2559,10 @@
RelativePath="..\..\gui\ApplyState.h"
>
</File>
<File
RelativePath="..\..\gui\AppSaveStates.h"
>
</File>
<File
RelativePath="..\..\gui\ConsoleLogger.h"
>