From 338cc07ce94fdbc89efaa4c573cc2e5bf47cd032 Mon Sep 17 00:00:00 2001 From: "Jake.Stine" Date: Tue, 11 May 2010 11:44:33 +0000 Subject: [PATCH] Some missing svn:eol-style=native props. It's like they vanish into thin air these things. git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2981 96395faa-99c1-11dd-bbfe-3dabce05a288 --- pcsx2/gui/AppCorePlugins.h | 104 +- pcsx2/gui/AppCoreThread.h | 472 ++++---- pcsx2/gui/Dialogs/BaseConfigurationDialog.inl | 56 +- pcsx2/gui/ExecutorThread.cpp | 1028 ++++++++--------- pcsx2/gui/GSFrame.h | 240 ++-- pcsx2/gui/SysState.cpp | 706 +++++------ pcsx2/gui/UpdateUI.cpp | 218 ++-- pcsx2/gui/pxEventThread.h | 474 ++++---- 8 files changed, 1649 insertions(+), 1649 deletions(-) diff --git a/pcsx2/gui/AppCorePlugins.h b/pcsx2/gui/AppCorePlugins.h index c9ef5711df..56f9c80d9f 100644 --- a/pcsx2/gui/AppCorePlugins.h +++ b/pcsx2/gui/AppCorePlugins.h @@ -1,52 +1,52 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "AppCommon.h" - -// -------------------------------------------------------------------------------------- -// AppPluginManager -// -------------------------------------------------------------------------------------- -// This extension of PluginManager provides event listener sources for plugins -- loading, -// unloading, open, close, shutdown, etc. -// -// FIXME : Should this be made part of the PCSX2 core emulation? (integrated into PluginManager) -// I'm undecided on if it makes sense more in that context or in this one (interface). -// -class AppPluginManager : public PluginManager -{ - typedef PluginManager _parent; - -public: - AppPluginManager(); - virtual ~AppPluginManager() throw(); - - void Load( const wxString (&folders)[PluginId_Count] ); - void Load( PluginsEnum_t pid, const wxString& srcfile ); - void Unload( PluginsEnum_t pid ); - void Unload(); - - void Init(); - void Init( PluginsEnum_t pid ); - void Shutdown( PluginsEnum_t pid ); - void Shutdown(); - void Close(); - void Open(); - -protected: - bool OpenPlugin_GS(); - void ClosePlugin_GS(); -}; +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2010 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 . + */ + +#pragma once + +#include "AppCommon.h" + +// -------------------------------------------------------------------------------------- +// AppPluginManager +// -------------------------------------------------------------------------------------- +// This extension of PluginManager provides event listener sources for plugins -- loading, +// unloading, open, close, shutdown, etc. +// +// FIXME : Should this be made part of the PCSX2 core emulation? (integrated into PluginManager) +// I'm undecided on if it makes sense more in that context or in this one (interface). +// +class AppPluginManager : public PluginManager +{ + typedef PluginManager _parent; + +public: + AppPluginManager(); + virtual ~AppPluginManager() throw(); + + void Load( const wxString (&folders)[PluginId_Count] ); + void Load( PluginsEnum_t pid, const wxString& srcfile ); + void Unload( PluginsEnum_t pid ); + void Unload(); + + void Init(); + void Init( PluginsEnum_t pid ); + void Shutdown( PluginsEnum_t pid ); + void Shutdown(); + void Close(); + void Open(); + +protected: + bool OpenPlugin_GS(); + void ClosePlugin_GS(); +}; diff --git a/pcsx2/gui/AppCoreThread.h b/pcsx2/gui/AppCoreThread.h index 22fe1cdc0c..b9bfe7a231 100644 --- a/pcsx2/gui/AppCoreThread.h +++ b/pcsx2/gui/AppCoreThread.h @@ -1,236 +1,236 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#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." ) - -#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 -// -------------------------------------------------------------------------------------- -class AppCoreThread : public SysCoreThread -{ - typedef SysCoreThread _parent; - -public: - AppCoreThread(); - virtual ~AppCoreThread() throw(); - - virtual void Suspend( bool isBlocking=false ); - virtual void Resume(); - virtual void Shutdown(); - virtual void Cancel( bool isBlocking=true ); - virtual void StateCheckInThread(); - virtual void ChangeCdvdSource(); - - virtual void ApplySettings( const Pcsx2Config& src ); - virtual void UploadStateCopy( const VmStateBuffer& copy ); - -protected: - virtual void OnResumeReady(); - virtual void OnResumeInThread( bool IsSuspended ); - virtual void OnSuspendInThread(); - virtual void OnCleanupInThread(); - virtual void PostVsyncToUI(); - virtual void ExecuteTaskInThread(); - virtual void DoCpuReset(); -}; - -// -------------------------------------------------------------------------------------- -// IScopedCoreThread / BaseScopedCoreThread -// -------------------------------------------------------------------------------------- -class IScopedCoreThread -{ -protected: - IScopedCoreThread() {} - -public: - virtual ~IScopedCoreThread() throw() {}; - virtual void AllowResume()=0; - virtual void DisallowResume()=0; -}; - -class BaseScopedCoreThread : public IScopedCoreThread -{ - DeclareNoncopyableObject( BaseScopedCoreThread ); - -protected: - bool m_allowResume; - bool m_alreadyStopped; - bool m_alreadyScoped; - SynchronousActionState m_sync; - SynchronousActionState m_sync_resume; - Threading::Mutex m_mtx_resume; - - BaseScopedCoreThread(); - -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(); -}; - -// -------------------------------------------------------------------------------------- -// ScopedCoreThreadClose / ScopedCoreThreadPause / ScopedCoreThreadPopup -// -------------------------------------------------------------------------------------- -// This class behaves a bit differently from other scoped classes due to the "standard" -// assumption that we actually do *not* want to resume CoreThread operations when an -// exception occurs. Because of this, the destructor of this class does *not* unroll the -// suspend operation. Instead you must manually instruct the class to resume using a call -// to the provisioned AllowResume() method. -// -// If the class leaves scope without having been resumed, a log is written to the console. -// This can be useful for troubleshooting, and also allows the log a second line of info -// indicating the status of CoreThread execution at the time of the exception. -// -// ScopedCoreThreadPopup is intended for use where message boxes are popped up to the user. -// The old style GUI (without GSopen2) must use a full close of the CoreThread, in order to -// ensure that the GS window isn't blocking the popup, and to avoid crashes if the GS window -// is maximized or fullscreen. -// -class ScopedCoreThreadClose : public BaseScopedCoreThread -{ - typedef BaseScopedCoreThread _parent; - -public: - ScopedCoreThreadClose(); - virtual ~ScopedCoreThreadClose() throw(); - - void LoadPlugins(); -}; - -struct ScopedCoreThreadPause : public BaseScopedCoreThread -{ - typedef BaseScopedCoreThread _parent; - -public: - ScopedCoreThreadPause( BaseSysExecEvent_ScopedCore* abuse_me=NULL ); - virtual ~ScopedCoreThreadPause() throw(); -}; - -struct ScopedCoreThreadPopup : public IScopedCoreThread -{ -protected: - ScopedPtr m_scoped_core; - -public: - ScopedCoreThreadPopup(); - virtual ~ScopedCoreThreadPopup() throw() {} - - virtual void AllowResume(); - virtual void DisallowResume(); -}; +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2010 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 . + */ + +#pragma once + +#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." ) + +#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 +// -------------------------------------------------------------------------------------- +class AppCoreThread : public SysCoreThread +{ + typedef SysCoreThread _parent; + +public: + AppCoreThread(); + virtual ~AppCoreThread() throw(); + + virtual void Suspend( bool isBlocking=false ); + virtual void Resume(); + virtual void Shutdown(); + virtual void Cancel( bool isBlocking=true ); + virtual void StateCheckInThread(); + virtual void ChangeCdvdSource(); + + virtual void ApplySettings( const Pcsx2Config& src ); + virtual void UploadStateCopy( const VmStateBuffer& copy ); + +protected: + virtual void OnResumeReady(); + virtual void OnResumeInThread( bool IsSuspended ); + virtual void OnSuspendInThread(); + virtual void OnCleanupInThread(); + virtual void PostVsyncToUI(); + virtual void ExecuteTaskInThread(); + virtual void DoCpuReset(); +}; + +// -------------------------------------------------------------------------------------- +// IScopedCoreThread / BaseScopedCoreThread +// -------------------------------------------------------------------------------------- +class IScopedCoreThread +{ +protected: + IScopedCoreThread() {} + +public: + virtual ~IScopedCoreThread() throw() {}; + virtual void AllowResume()=0; + virtual void DisallowResume()=0; +}; + +class BaseScopedCoreThread : public IScopedCoreThread +{ + DeclareNoncopyableObject( BaseScopedCoreThread ); + +protected: + bool m_allowResume; + bool m_alreadyStopped; + bool m_alreadyScoped; + SynchronousActionState m_sync; + SynchronousActionState m_sync_resume; + Threading::Mutex m_mtx_resume; + + BaseScopedCoreThread(); + +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(); +}; + +// -------------------------------------------------------------------------------------- +// ScopedCoreThreadClose / ScopedCoreThreadPause / ScopedCoreThreadPopup +// -------------------------------------------------------------------------------------- +// This class behaves a bit differently from other scoped classes due to the "standard" +// assumption that we actually do *not* want to resume CoreThread operations when an +// exception occurs. Because of this, the destructor of this class does *not* unroll the +// suspend operation. Instead you must manually instruct the class to resume using a call +// to the provisioned AllowResume() method. +// +// If the class leaves scope without having been resumed, a log is written to the console. +// This can be useful for troubleshooting, and also allows the log a second line of info +// indicating the status of CoreThread execution at the time of the exception. +// +// ScopedCoreThreadPopup is intended for use where message boxes are popped up to the user. +// The old style GUI (without GSopen2) must use a full close of the CoreThread, in order to +// ensure that the GS window isn't blocking the popup, and to avoid crashes if the GS window +// is maximized or fullscreen. +// +class ScopedCoreThreadClose : public BaseScopedCoreThread +{ + typedef BaseScopedCoreThread _parent; + +public: + ScopedCoreThreadClose(); + virtual ~ScopedCoreThreadClose() throw(); + + void LoadPlugins(); +}; + +struct ScopedCoreThreadPause : public BaseScopedCoreThread +{ + typedef BaseScopedCoreThread _parent; + +public: + ScopedCoreThreadPause( BaseSysExecEvent_ScopedCore* abuse_me=NULL ); + virtual ~ScopedCoreThreadPause() throw(); +}; + +struct ScopedCoreThreadPopup : public IScopedCoreThread +{ +protected: + ScopedPtr m_scoped_core; + +public: + ScopedCoreThreadPopup(); + virtual ~ScopedCoreThreadPopup() throw() {} + + virtual void AllowResume(); + virtual void DisallowResume(); +}; diff --git a/pcsx2/gui/Dialogs/BaseConfigurationDialog.inl b/pcsx2/gui/Dialogs/BaseConfigurationDialog.inl index 7c695221a2..03a7bb0795 100644 --- a/pcsx2/gui/Dialogs/BaseConfigurationDialog.inl +++ b/pcsx2/gui/Dialogs/BaseConfigurationDialog.inl @@ -1,28 +1,28 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include - -template< typename T > -void Dialogs::BaseConfigurationDialog::AddPage( const char* label, int iconid ) -{ - const wxString labelstr( fromUTF8( label ) ); - const int curidx = m_labels.Add( labelstr ); - m_ApplyState.SetCurrentPage( curidx ); - m_listbook.AddPage( new T( &m_listbook ), wxGetTranslation( labelstr ), - ( labelstr == GetConfSettingsTabName() ), iconid ); -} +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2010 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 . + */ + +#pragma once + +#include + +template< typename T > +void Dialogs::BaseConfigurationDialog::AddPage( const char* label, int iconid ) +{ + const wxString labelstr( fromUTF8( label ) ); + const int curidx = m_labels.Add( labelstr ); + m_ApplyState.SetCurrentPage( curidx ); + m_listbook.AddPage( new T( &m_listbook ), wxGetTranslation( labelstr ), + ( labelstr == GetConfSettingsTabName() ), iconid ); +} diff --git a/pcsx2/gui/ExecutorThread.cpp b/pcsx2/gui/ExecutorThread.cpp index 7408b2d554..41f578ca62 100644 --- a/pcsx2/gui/ExecutorThread.cpp +++ b/pcsx2/gui/ExecutorThread.cpp @@ -1,514 +1,514 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#include "App.h" - - -wxString SysExecEvent::GetEventName() const -{ - pxFail( "Warning: Unnamed SysExecutor Event! Please overload GetEventName() in all SysExecEvent derived classes." ); - return wxEmptyString; -} - -wxString SysExecEvent::GetEventMessage() const -{ - return GetEventName(); -} - -// This is called from _DoInvokeEvent after various affinity and state checks have verified the -// message as executable. Override this when possible. Only override _DoInvokeEvent if you -// need some kind of additional low-level ability. -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++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. -void SysExecEvent::CleanupEvent() -{ - PostResult(); -} - -// Transports the specified exception to the thread/context that invoked this event. -// Events are run from a try/catch block in the event handler that automatically transports -// exceptions as neeeded, so there shouldn't be much need to use this method directly. -void SysExecEvent::SetException( BaseException* ex ) -{ - if( !ex ) return; - - ex->DiagMsg() += wxsFormat(L"(%s) ", GetEventName().c_str()); - //ex->UserMsg() = prefix + ex->UserMsg(); - - if( m_sync ) - { - // Throws the exception inline with the message handler (this makes the exception - // look like it was thrown quite naturally). - m_sync->SetException( ex ); - } - else - { - // 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. - - wxGetApp().PostEvent( pxExceptionEvent( ex ) ); - } -} - -void SysExecEvent::SetException( const BaseException& ex ) -{ - SetException( ex.Clone() ); -} - - -// This method calls _DoInvoke after performing some setup, exception handling, and -// affinity checks. For implementing behavior of your event, override _DoInvoke -// instead, which is the intended method of implementing derived class invocation. -void SysExecEvent::_DoInvokeEvent() -{ - AffinityAssert_AllowFrom_SysExecutor(); - - try { - InvokeEvent(); - } - catch( BaseException& ex ) - { - SetException( ex ); - } - catch( std::runtime_error& ex ) - { - SetException( new Exception::RuntimeError(ex) ); - } - - // 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. -// If the invoking thread posted the event in non-blocking fashion then no action is -// taken. -void SysExecEvent::PostResult() const -{ - if( m_sync ) m_sync->PostResult(); -} - -// -------------------------------------------------------------------------------------- -// pxEvtHandler Implementations -// -------------------------------------------------------------------------------------- -pxEvtHandler::pxEvtHandler() -{ - AtomicExchange( m_Quitting, false ); - m_qpc_Start = 0; -} - -// Puts the event queue into Shutdown mode, which does *not* immediately stop nor cancel -// the queue's processing. Instead it marks the queue inaccessible to all new events -// and continues procesing queued events for critical events that should not be ignored. -// (typically these are shutdown events critical to closing the app cleanly). Once -// all such events have been processed, the thread is stopped. -// -void pxEvtHandler::ShutdownQueue() -{ - if( m_Quitting ) return; - AtomicExchange( m_Quitting, true ); - m_wakeup.Post(); -} - -struct ScopedThreadCancelDisable -{ - ScopedThreadCancelDisable() - { - int oldstate; - pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate ); - } - - ~ScopedThreadCancelDisable() throw() - { - int oldstate; - pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate ); - } -}; - -void pxEvtHandler::ProcessEvents( pxEvtList& list ) -{ - ScopedLock synclock( m_mtx_pending ); - - pxEvtList::iterator node; - while( node = list.begin(), node != list.end() ) - { - ScopedPtr deleteMe(*node); - - list.erase( node ); - if( !m_Quitting || deleteMe->IsCriticalEvent() ) - { - // Some messages can be blocking, so we should release the mutex lock while - // processing, to avoid having cases where the main thread deadlocks simply - // trying to add a message to the queue due to the basic mutex acquire needed. - - m_qpc_Start = GetCPUTicks(); - - synclock.Release(); - - DevCon.WriteLn( L"(pxEvtHandler) Executing Event: %s [%s]", deleteMe->GetEventName().c_str(), deleteMe->AllowCancelOnExit() ? L"Cancelable" : L"Noncancelable" ); - - if( deleteMe->AllowCancelOnExit() ) - deleteMe->_DoInvokeEvent(); - else - { - ScopedThreadCancelDisable thr_cancel_scope; - deleteMe->_DoInvokeEvent(); - } - - u64 qpc_end = GetCPUTicks(); - DevCon.WriteLn( L"(pxEvtHandler) Event '%s' completed in %dms", deleteMe->GetEventName().c_str(), ((qpc_end-m_qpc_Start)*1000) / GetTickFrequency() ); - - synclock.Acquire(); - m_qpc_Start = 0; // lets the main thread know the message completed. - } - else - { - Console.WriteLn( L"(pxEvtHandler) Skipping Event: %s", deleteMe->GetEventName().c_str() ); - deleteMe->PostResult(); - } - } -} - -void pxEvtHandler::ProcessIdleEvents() -{ - ProcessEvents( m_idleEvents ); -} - -void pxEvtHandler::ProcessPendingEvents() -{ - ProcessEvents( m_pendingEvents ); -} - -// This method is provided for wxWidgets API conformance. I like to use PostEvent instead -// since it's remenicient of PostMessage in Windows (and behaves rather similarly). -void pxEvtHandler::AddPendingEvent( SysExecEvent& evt ) -{ - PostEvent( evt ); -} - -// Adds an event to the event queue in non-blocking fashion. The thread executing this -// event queue will be woken up if it's idle/sleeping. -// IMPORTANT: The pointer version of this function will *DELETE* the event object passed -// to it automatically when the event has been executed. If you are using a scoped event -// you should use the Reference/Handle overload instead! -// -void pxEvtHandler::PostEvent( SysExecEvent* evt ) -{ - if( !evt ) return; - - if( m_Quitting ) - { - evt->PostResult(); - return; - } - - ScopedLock synclock( m_mtx_pending ); - - //DbgCon.WriteLn( L"(%s) Posting event: %s (queue count=%d)", GetEventHandlerName().c_str(), evt->GetEventName().c_str(), m_pendingEvents.size() ); - - m_pendingEvents.push_back( evt ); - if( m_pendingEvents.size() == 1) - m_wakeup.Post(); -} - -void pxEvtHandler::PostEvent( const SysExecEvent& evt ) -{ - PostEvent( evt.Clone() ); -} - -void pxEvtHandler::PostIdleEvent( SysExecEvent* evt ) -{ - if( !evt ) return; - - if( m_Quitting ) - { - evt->PostResult(); - return; - } - - ScopedLock synclock( m_mtx_pending ); - - if( m_idleEvents.size() == 0) - { - m_pendingEvents.push_back( evt ); - m_wakeup.Post(); - } - else - m_idleEvents.push_back( evt ); -} - -void pxEvtHandler::PostIdleEvent( const SysExecEvent& evt ) -{ - PostIdleEvent( evt.Clone() ); -} - -void pxEvtHandler::ProcessEvent( SysExecEvent& evt ) -{ - if( wxThread::GetCurrentId() != m_OwnerThreadId ) - { - SynchronousActionState sync; - evt.SetSyncState( sync ); - PostEvent( evt ); - sync.WaitForResult(); - } - else - evt._DoInvokeEvent(); -} - -void pxEvtHandler::ProcessEvent( SysExecEvent* evt ) -{ - if( !evt ) return; - - if( wxThread::GetCurrentId() != m_OwnerThreadId ) - { - SynchronousActionState sync; - evt->SetSyncState( sync ); - PostEvent( evt ); - sync.WaitForResult(); - } - else - { - ScopedPtr deleteMe( evt ); - deleteMe->_DoInvokeEvent(); - } -} - -bool pxEvtHandler::ProcessMethodSelf( FnType_Void* method ) -{ - if( wxThread::GetCurrentId() != m_OwnerThreadId ) - { - SynchronousActionState sync; - SysExecEvent_MethodVoid evt(method); - evt.SetSyncState( sync ); - PostEvent( evt ); - sync.WaitForResult(); - - return true; - } - - return false; -} - -// This method invokes the derived class Idle implementations (if any) and then enters -// the sleep state until such time that new messages are received. -// -// FUTURE: Processes idle messages from the idle message queue (not implemented yet). -// -// Extending: Derived classes should override _DoIdle instead, unless it is necessary -// to implement post-wakeup behavior. -// -void pxEvtHandler::Idle() -{ - ProcessIdleEvents(); - _DoIdle(); - m_wakeup.WaitWithoutYield(); -} - -void pxEvtHandler::SetActiveThread() -{ - m_OwnerThreadId = wxThread::GetCurrentId(); -} - -// -------------------------------------------------------------------------------------- -// WaitingForThreadedTaskDialog -// -------------------------------------------------------------------------------------- -// Note: currently unused (legacy code). May be utilized at a later date, so I'm leaving -// it in (for now!) -// -class WaitingForThreadedTaskDialog - : public wxDialogWithHelpers -{ -private: - typedef wxDialogWithHelpers _parent; - -protected: - PersistentThread* m_thread; - -public: - WaitingForThreadedTaskDialog( PersistentThread* thr, wxWindow* parent, const wxString& title, const wxString& content ); - virtual ~WaitingForThreadedTaskDialog() throw() {} - -protected: - void OnCancel_Clicked( wxCommandEvent& evt ); - void OnTerminateApp_Clicked( wxCommandEvent& evt ); -}; - -// -------------------------------------------------------------------------------------- -// WaitingForThreadedTaskDialog Implementations -// -------------------------------------------------------------------------------------- -WaitingForThreadedTaskDialog::WaitingForThreadedTaskDialog( PersistentThread* thr, wxWindow* parent, const wxString& title, const wxString& content ) - : wxDialogWithHelpers( parent, title, wxVERTICAL ) -{ - m_thread = thr; - m_idealWidth = 500; - - *this += Heading( content ); - *this += 15; - *this += Heading( _("Press Cancel to attempt to cancel the action.") ); - *this += Heading( _("Press Terminate to kill PCSX2 immediately.") ); - - *this += new wxButton( this, wxID_CANCEL ); - *this += new wxButton( this, wxID_ANY, _("Terminate App") ); -} - -void WaitingForThreadedTaskDialog::OnCancel_Clicked( wxCommandEvent& evt ) -{ - evt.Skip(); - if( !m_thread ) return; - m_thread->Cancel( false ); - - if( wxWindow* cancel = FindWindowById( wxID_CANCEL ) ) cancel->Disable(); -} - -void WaitingForThreadedTaskDialog::OnTerminateApp_Clicked( wxCommandEvent& evt ) -{ - // (note: SIGTERM is a "handled" kill that performs shutdown stuff, which typically just crashes anyway) - wxKill( wxGetProcessId(), wxSIGKILL ); -} - -// -------------------------------------------------------------------------------------- -// ExecutorThread Implementations -// -------------------------------------------------------------------------------------- -ExecutorThread::ExecutorThread( pxEvtHandler* evthandler ) -{ - m_EvtHandler = evthandler; -} - -// Exposes the internal pxEvtHandler::ShutdownQueue API. See pxEvtHandler for details. -void ExecutorThread::ShutdownQueue() -{ - if( !m_EvtHandler ) return; - - if( !m_EvtHandler->IsShuttingDown() ) - m_EvtHandler->ShutdownQueue(); - - Block(); -} - -// Exposes the internal pxEvtHandler::PostEvent API. See pxEvtHandler for details. -void ExecutorThread::PostEvent( SysExecEvent* evt ) -{ - if( !pxAssert( m_EvtHandler ) ) return; - m_EvtHandler->PostEvent( evt ); -} - -void ExecutorThread::PostEvent( const SysExecEvent& evt ) -{ - if( !pxAssert( m_EvtHandler ) ) return; - m_EvtHandler->PostEvent( evt ); -} - -// Exposes the internal pxEvtHandler::PostIdleEvent API. See pxEvtHandler for details. -void ExecutorThread::PostIdleEvent( SysExecEvent* evt ) -{ - if( !pxAssert( m_EvtHandler ) ) return; - m_EvtHandler->PostIdleEvent( evt ); -} - -void ExecutorThread::PostIdleEvent( const SysExecEvent& evt ) -{ - if( !pxAssert( m_EvtHandler ) ) return; - m_EvtHandler->PostIdleEvent( evt ); -} - -// Exposes the internal pxEvtHandler::ProcessEvent API. See pxEvtHandler for details. -void ExecutorThread::ProcessEvent( SysExecEvent* evt ) -{ - if( m_EvtHandler ) - m_EvtHandler->ProcessEvent( evt ); - else - { - ScopedPtr deleteMe( evt ); - deleteMe->_DoInvokeEvent(); - } -} - -void ExecutorThread::ProcessEvent( SysExecEvent& evt ) -{ - if( m_EvtHandler ) - m_EvtHandler->ProcessEvent( evt ); - else - evt._DoInvokeEvent(); -} - -void ExecutorThread::OnStart() -{ - //if( !m_ExecutorTimer ) - // m_ExecutorTimer = new wxTimer( wxTheApp, pxEvt_ThreadTaskTimeout_SysExec ); - - m_name = L"SysExecutor"; - _parent::OnStart(); -} - -void ExecutorThread::ExecuteTaskInThread() -{ - if( !pxAssertDev( m_EvtHandler, "Gimme a damn Event Handler first, object whore." ) ) return; - - m_EvtHandler->SetActiveThread(); - - while( true ) - { - if( !pxAssertDev( m_EvtHandler, "Event handler has been deallocated during SysExecutor thread execution." ) ) return; - - m_EvtHandler->Idle(); - m_EvtHandler->ProcessPendingEvents(); - if( m_EvtHandler->IsShuttingDown() ) break; - } -} - -void ExecutorThread::OnCleanupInThread() -{ - //wxGetApp().PostCommand( m_task, pxEvt_ThreadTaskComplete ); - _parent::OnCleanupInThread(); -} - -// -------------------------------------------------------------------------------------- -// Threaded Event Handlers (Pcsx2App) -// -------------------------------------------------------------------------------------- - -// This event is called when the SysExecutorThread's timer triggers, which means the -// VM/system task has taken an oddly long period of time to complete. The task is able -// to invoke a modal dialog from here that will grant the user some options for handling -// the unresponsive task. -void Pcsx2App::OnSysExecutorTaskTimeout( wxTimerEvent& evt ) -{ - if( !SysExecutorThread.IsRunning() ) return; - - //BaseThreadedInvocation* task = SysExecutorThread.GetTask(); - //if( !task ) return; - - //task->ShowModalStatus(); -} +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2010 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 . + */ + +#include "PrecompiledHeader.h" +#include "App.h" + + +wxString SysExecEvent::GetEventName() const +{ + pxFail( "Warning: Unnamed SysExecutor Event! Please overload GetEventName() in all SysExecEvent derived classes." ); + return wxEmptyString; +} + +wxString SysExecEvent::GetEventMessage() const +{ + return GetEventName(); +} + +// This is called from _DoInvokeEvent after various affinity and state checks have verified the +// message as executable. Override this when possible. Only override _DoInvokeEvent if you +// need some kind of additional low-level ability. +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++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. +void SysExecEvent::CleanupEvent() +{ + PostResult(); +} + +// Transports the specified exception to the thread/context that invoked this event. +// Events are run from a try/catch block in the event handler that automatically transports +// exceptions as neeeded, so there shouldn't be much need to use this method directly. +void SysExecEvent::SetException( BaseException* ex ) +{ + if( !ex ) return; + + ex->DiagMsg() += wxsFormat(L"(%s) ", GetEventName().c_str()); + //ex->UserMsg() = prefix + ex->UserMsg(); + + if( m_sync ) + { + // Throws the exception inline with the message handler (this makes the exception + // look like it was thrown quite naturally). + m_sync->SetException( ex ); + } + else + { + // 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. + + wxGetApp().PostEvent( pxExceptionEvent( ex ) ); + } +} + +void SysExecEvent::SetException( const BaseException& ex ) +{ + SetException( ex.Clone() ); +} + + +// This method calls _DoInvoke after performing some setup, exception handling, and +// affinity checks. For implementing behavior of your event, override _DoInvoke +// instead, which is the intended method of implementing derived class invocation. +void SysExecEvent::_DoInvokeEvent() +{ + AffinityAssert_AllowFrom_SysExecutor(); + + try { + InvokeEvent(); + } + catch( BaseException& ex ) + { + SetException( ex ); + } + catch( std::runtime_error& ex ) + { + SetException( new Exception::RuntimeError(ex) ); + } + + // 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. +// If the invoking thread posted the event in non-blocking fashion then no action is +// taken. +void SysExecEvent::PostResult() const +{ + if( m_sync ) m_sync->PostResult(); +} + +// -------------------------------------------------------------------------------------- +// pxEvtHandler Implementations +// -------------------------------------------------------------------------------------- +pxEvtHandler::pxEvtHandler() +{ + AtomicExchange( m_Quitting, false ); + m_qpc_Start = 0; +} + +// Puts the event queue into Shutdown mode, which does *not* immediately stop nor cancel +// the queue's processing. Instead it marks the queue inaccessible to all new events +// and continues procesing queued events for critical events that should not be ignored. +// (typically these are shutdown events critical to closing the app cleanly). Once +// all such events have been processed, the thread is stopped. +// +void pxEvtHandler::ShutdownQueue() +{ + if( m_Quitting ) return; + AtomicExchange( m_Quitting, true ); + m_wakeup.Post(); +} + +struct ScopedThreadCancelDisable +{ + ScopedThreadCancelDisable() + { + int oldstate; + pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate ); + } + + ~ScopedThreadCancelDisable() throw() + { + int oldstate; + pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate ); + } +}; + +void pxEvtHandler::ProcessEvents( pxEvtList& list ) +{ + ScopedLock synclock( m_mtx_pending ); + + pxEvtList::iterator node; + while( node = list.begin(), node != list.end() ) + { + ScopedPtr deleteMe(*node); + + list.erase( node ); + if( !m_Quitting || deleteMe->IsCriticalEvent() ) + { + // Some messages can be blocking, so we should release the mutex lock while + // processing, to avoid having cases where the main thread deadlocks simply + // trying to add a message to the queue due to the basic mutex acquire needed. + + m_qpc_Start = GetCPUTicks(); + + synclock.Release(); + + DevCon.WriteLn( L"(pxEvtHandler) Executing Event: %s [%s]", deleteMe->GetEventName().c_str(), deleteMe->AllowCancelOnExit() ? L"Cancelable" : L"Noncancelable" ); + + if( deleteMe->AllowCancelOnExit() ) + deleteMe->_DoInvokeEvent(); + else + { + ScopedThreadCancelDisable thr_cancel_scope; + deleteMe->_DoInvokeEvent(); + } + + u64 qpc_end = GetCPUTicks(); + DevCon.WriteLn( L"(pxEvtHandler) Event '%s' completed in %dms", deleteMe->GetEventName().c_str(), ((qpc_end-m_qpc_Start)*1000) / GetTickFrequency() ); + + synclock.Acquire(); + m_qpc_Start = 0; // lets the main thread know the message completed. + } + else + { + Console.WriteLn( L"(pxEvtHandler) Skipping Event: %s", deleteMe->GetEventName().c_str() ); + deleteMe->PostResult(); + } + } +} + +void pxEvtHandler::ProcessIdleEvents() +{ + ProcessEvents( m_idleEvents ); +} + +void pxEvtHandler::ProcessPendingEvents() +{ + ProcessEvents( m_pendingEvents ); +} + +// This method is provided for wxWidgets API conformance. I like to use PostEvent instead +// since it's remenicient of PostMessage in Windows (and behaves rather similarly). +void pxEvtHandler::AddPendingEvent( SysExecEvent& evt ) +{ + PostEvent( evt ); +} + +// Adds an event to the event queue in non-blocking fashion. The thread executing this +// event queue will be woken up if it's idle/sleeping. +// IMPORTANT: The pointer version of this function will *DELETE* the event object passed +// to it automatically when the event has been executed. If you are using a scoped event +// you should use the Reference/Handle overload instead! +// +void pxEvtHandler::PostEvent( SysExecEvent* evt ) +{ + if( !evt ) return; + + if( m_Quitting ) + { + evt->PostResult(); + return; + } + + ScopedLock synclock( m_mtx_pending ); + + //DbgCon.WriteLn( L"(%s) Posting event: %s (queue count=%d)", GetEventHandlerName().c_str(), evt->GetEventName().c_str(), m_pendingEvents.size() ); + + m_pendingEvents.push_back( evt ); + if( m_pendingEvents.size() == 1) + m_wakeup.Post(); +} + +void pxEvtHandler::PostEvent( const SysExecEvent& evt ) +{ + PostEvent( evt.Clone() ); +} + +void pxEvtHandler::PostIdleEvent( SysExecEvent* evt ) +{ + if( !evt ) return; + + if( m_Quitting ) + { + evt->PostResult(); + return; + } + + ScopedLock synclock( m_mtx_pending ); + + if( m_idleEvents.size() == 0) + { + m_pendingEvents.push_back( evt ); + m_wakeup.Post(); + } + else + m_idleEvents.push_back( evt ); +} + +void pxEvtHandler::PostIdleEvent( const SysExecEvent& evt ) +{ + PostIdleEvent( evt.Clone() ); +} + +void pxEvtHandler::ProcessEvent( SysExecEvent& evt ) +{ + if( wxThread::GetCurrentId() != m_OwnerThreadId ) + { + SynchronousActionState sync; + evt.SetSyncState( sync ); + PostEvent( evt ); + sync.WaitForResult(); + } + else + evt._DoInvokeEvent(); +} + +void pxEvtHandler::ProcessEvent( SysExecEvent* evt ) +{ + if( !evt ) return; + + if( wxThread::GetCurrentId() != m_OwnerThreadId ) + { + SynchronousActionState sync; + evt->SetSyncState( sync ); + PostEvent( evt ); + sync.WaitForResult(); + } + else + { + ScopedPtr deleteMe( evt ); + deleteMe->_DoInvokeEvent(); + } +} + +bool pxEvtHandler::ProcessMethodSelf( FnType_Void* method ) +{ + if( wxThread::GetCurrentId() != m_OwnerThreadId ) + { + SynchronousActionState sync; + SysExecEvent_MethodVoid evt(method); + evt.SetSyncState( sync ); + PostEvent( evt ); + sync.WaitForResult(); + + return true; + } + + return false; +} + +// This method invokes the derived class Idle implementations (if any) and then enters +// the sleep state until such time that new messages are received. +// +// FUTURE: Processes idle messages from the idle message queue (not implemented yet). +// +// Extending: Derived classes should override _DoIdle instead, unless it is necessary +// to implement post-wakeup behavior. +// +void pxEvtHandler::Idle() +{ + ProcessIdleEvents(); + _DoIdle(); + m_wakeup.WaitWithoutYield(); +} + +void pxEvtHandler::SetActiveThread() +{ + m_OwnerThreadId = wxThread::GetCurrentId(); +} + +// -------------------------------------------------------------------------------------- +// WaitingForThreadedTaskDialog +// -------------------------------------------------------------------------------------- +// Note: currently unused (legacy code). May be utilized at a later date, so I'm leaving +// it in (for now!) +// +class WaitingForThreadedTaskDialog + : public wxDialogWithHelpers +{ +private: + typedef wxDialogWithHelpers _parent; + +protected: + PersistentThread* m_thread; + +public: + WaitingForThreadedTaskDialog( PersistentThread* thr, wxWindow* parent, const wxString& title, const wxString& content ); + virtual ~WaitingForThreadedTaskDialog() throw() {} + +protected: + void OnCancel_Clicked( wxCommandEvent& evt ); + void OnTerminateApp_Clicked( wxCommandEvent& evt ); +}; + +// -------------------------------------------------------------------------------------- +// WaitingForThreadedTaskDialog Implementations +// -------------------------------------------------------------------------------------- +WaitingForThreadedTaskDialog::WaitingForThreadedTaskDialog( PersistentThread* thr, wxWindow* parent, const wxString& title, const wxString& content ) + : wxDialogWithHelpers( parent, title, wxVERTICAL ) +{ + m_thread = thr; + m_idealWidth = 500; + + *this += Heading( content ); + *this += 15; + *this += Heading( _("Press Cancel to attempt to cancel the action.") ); + *this += Heading( _("Press Terminate to kill PCSX2 immediately.") ); + + *this += new wxButton( this, wxID_CANCEL ); + *this += new wxButton( this, wxID_ANY, _("Terminate App") ); +} + +void WaitingForThreadedTaskDialog::OnCancel_Clicked( wxCommandEvent& evt ) +{ + evt.Skip(); + if( !m_thread ) return; + m_thread->Cancel( false ); + + if( wxWindow* cancel = FindWindowById( wxID_CANCEL ) ) cancel->Disable(); +} + +void WaitingForThreadedTaskDialog::OnTerminateApp_Clicked( wxCommandEvent& evt ) +{ + // (note: SIGTERM is a "handled" kill that performs shutdown stuff, which typically just crashes anyway) + wxKill( wxGetProcessId(), wxSIGKILL ); +} + +// -------------------------------------------------------------------------------------- +// ExecutorThread Implementations +// -------------------------------------------------------------------------------------- +ExecutorThread::ExecutorThread( pxEvtHandler* evthandler ) +{ + m_EvtHandler = evthandler; +} + +// Exposes the internal pxEvtHandler::ShutdownQueue API. See pxEvtHandler for details. +void ExecutorThread::ShutdownQueue() +{ + if( !m_EvtHandler ) return; + + if( !m_EvtHandler->IsShuttingDown() ) + m_EvtHandler->ShutdownQueue(); + + Block(); +} + +// Exposes the internal pxEvtHandler::PostEvent API. See pxEvtHandler for details. +void ExecutorThread::PostEvent( SysExecEvent* evt ) +{ + if( !pxAssert( m_EvtHandler ) ) return; + m_EvtHandler->PostEvent( evt ); +} + +void ExecutorThread::PostEvent( const SysExecEvent& evt ) +{ + if( !pxAssert( m_EvtHandler ) ) return; + m_EvtHandler->PostEvent( evt ); +} + +// Exposes the internal pxEvtHandler::PostIdleEvent API. See pxEvtHandler for details. +void ExecutorThread::PostIdleEvent( SysExecEvent* evt ) +{ + if( !pxAssert( m_EvtHandler ) ) return; + m_EvtHandler->PostIdleEvent( evt ); +} + +void ExecutorThread::PostIdleEvent( const SysExecEvent& evt ) +{ + if( !pxAssert( m_EvtHandler ) ) return; + m_EvtHandler->PostIdleEvent( evt ); +} + +// Exposes the internal pxEvtHandler::ProcessEvent API. See pxEvtHandler for details. +void ExecutorThread::ProcessEvent( SysExecEvent* evt ) +{ + if( m_EvtHandler ) + m_EvtHandler->ProcessEvent( evt ); + else + { + ScopedPtr deleteMe( evt ); + deleteMe->_DoInvokeEvent(); + } +} + +void ExecutorThread::ProcessEvent( SysExecEvent& evt ) +{ + if( m_EvtHandler ) + m_EvtHandler->ProcessEvent( evt ); + else + evt._DoInvokeEvent(); +} + +void ExecutorThread::OnStart() +{ + //if( !m_ExecutorTimer ) + // m_ExecutorTimer = new wxTimer( wxTheApp, pxEvt_ThreadTaskTimeout_SysExec ); + + m_name = L"SysExecutor"; + _parent::OnStart(); +} + +void ExecutorThread::ExecuteTaskInThread() +{ + if( !pxAssertDev( m_EvtHandler, "Gimme a damn Event Handler first, object whore." ) ) return; + + m_EvtHandler->SetActiveThread(); + + while( true ) + { + if( !pxAssertDev( m_EvtHandler, "Event handler has been deallocated during SysExecutor thread execution." ) ) return; + + m_EvtHandler->Idle(); + m_EvtHandler->ProcessPendingEvents(); + if( m_EvtHandler->IsShuttingDown() ) break; + } +} + +void ExecutorThread::OnCleanupInThread() +{ + //wxGetApp().PostCommand( m_task, pxEvt_ThreadTaskComplete ); + _parent::OnCleanupInThread(); +} + +// -------------------------------------------------------------------------------------- +// Threaded Event Handlers (Pcsx2App) +// -------------------------------------------------------------------------------------- + +// This event is called when the SysExecutorThread's timer triggers, which means the +// VM/system task has taken an oddly long period of time to complete. The task is able +// to invoke a modal dialog from here that will grant the user some options for handling +// the unresponsive task. +void Pcsx2App::OnSysExecutorTaskTimeout( wxTimerEvent& evt ) +{ + if( !SysExecutorThread.IsRunning() ) return; + + //BaseThreadedInvocation* task = SysExecutorThread.GetTask(); + //if( !task ) return; + + //task->ShowModalStatus(); +} diff --git a/pcsx2/gui/GSFrame.h b/pcsx2/gui/GSFrame.h index 2212493c03..3b1456046b 100644 --- a/pcsx2/gui/GSFrame.h +++ b/pcsx2/gui/GSFrame.h @@ -1,120 +1,120 @@ - -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "App.h" -#include "CpuUsageProvider.h" - - -enum LimiterModeType -{ - Limit_Nominal, - Limit_Turbo, - Limit_Slomo, -}; - -extern LimiterModeType g_LimiterMode; - -// -------------------------------------------------------------------------------------- -// GSPanel -// -------------------------------------------------------------------------------------- -class GSPanel : public wxWindow, public EventListener_AppStatus -{ - typedef wxWindow _parent; - -protected: - AcceleratorDictionary m_Accels; - wxTimer m_HideMouseTimer; - bool m_CursorShown; - bool m_HasFocus; - -public: - GSPanel( wxWindow* parent ); - virtual ~GSPanel() throw(); - - void DoResize(); - void DoShowMouse(); - -protected: - void AppStatusEvent_OnSettingsApplied(); - -#ifdef __WXMSW__ - virtual WXLRESULT MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam); -#endif - - void InitDefaultAccelerators(); - - void OnCloseWindow( wxCloseEvent& evt ); - void OnResize(wxSizeEvent& event); - void OnShowMouse( wxMouseEvent& evt ); - void OnHideMouseTimeout( wxTimerEvent& evt ); - void OnKeyDown( wxKeyEvent& evt ); - void OnFocus( wxFocusEvent& evt ); - void OnFocusLost( wxFocusEvent& evt ); -}; - - -// -------------------------------------------------------------------------------------- -// GSFrame -// -------------------------------------------------------------------------------------- -class GSFrame : public wxFrame, - public EventListener_AppStatus, - public EventListener_CoreThread -{ - typedef wxFrame _parent; - -protected: - wxTimer m_timer_UpdateTitle; - wxWindowID m_id_gspanel; - wxWindowID m_id_OutputDisabled; - wxStaticText* m_label_Disabled; - wxStatusBar* m_statusbar; - - CpuUsageProvider m_CpuUsage; - -public: - GSFrame(wxWindow* parent, const wxString& title); - virtual ~GSFrame() throw(); - - GSPanel* GetViewport(); - void SetFocus(); - bool Show( bool shown=true ); - wxStaticText* GetLabel_OutputDisabled() const; - -protected: - void OnCloseWindow( wxCloseEvent& evt ); - void OnMove( wxMoveEvent& evt ); - void OnResize( wxSizeEvent& evt ); - void OnActivate( wxActivateEvent& evt ); - void OnUpdateTitle( wxTimerEvent& evt ); - - void AppStatusEvent_OnSettingsApplied(); - void CoreThread_OnResumed(); - void CoreThread_OnSuspended(); -}; - -// -------------------------------------------------------------------------------------- -// s* macros! ['s' stands for 'shortcut'] -// -------------------------------------------------------------------------------------- -// Use these for "silent fail" invocation of PCSX2 Application-related constructs. If the -// construct (albeit wxApp, MainFrame, CoreThread, etc) is null, the requested method will -// not be invoked, and an optional "else" clause can be affixed for handling the end case. -// -// See App.h (sApp) for more details. -// -#define sGSFrame \ - if( GSFrame* __gsframe_ = wxGetApp().GetGsFramePtr() ) (*__gsframe_) + +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2010 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 . + */ + +#pragma once + +#include "App.h" +#include "CpuUsageProvider.h" + + +enum LimiterModeType +{ + Limit_Nominal, + Limit_Turbo, + Limit_Slomo, +}; + +extern LimiterModeType g_LimiterMode; + +// -------------------------------------------------------------------------------------- +// GSPanel +// -------------------------------------------------------------------------------------- +class GSPanel : public wxWindow, public EventListener_AppStatus +{ + typedef wxWindow _parent; + +protected: + AcceleratorDictionary m_Accels; + wxTimer m_HideMouseTimer; + bool m_CursorShown; + bool m_HasFocus; + +public: + GSPanel( wxWindow* parent ); + virtual ~GSPanel() throw(); + + void DoResize(); + void DoShowMouse(); + +protected: + void AppStatusEvent_OnSettingsApplied(); + +#ifdef __WXMSW__ + virtual WXLRESULT MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam); +#endif + + void InitDefaultAccelerators(); + + void OnCloseWindow( wxCloseEvent& evt ); + void OnResize(wxSizeEvent& event); + void OnShowMouse( wxMouseEvent& evt ); + void OnHideMouseTimeout( wxTimerEvent& evt ); + void OnKeyDown( wxKeyEvent& evt ); + void OnFocus( wxFocusEvent& evt ); + void OnFocusLost( wxFocusEvent& evt ); +}; + + +// -------------------------------------------------------------------------------------- +// GSFrame +// -------------------------------------------------------------------------------------- +class GSFrame : public wxFrame, + public EventListener_AppStatus, + public EventListener_CoreThread +{ + typedef wxFrame _parent; + +protected: + wxTimer m_timer_UpdateTitle; + wxWindowID m_id_gspanel; + wxWindowID m_id_OutputDisabled; + wxStaticText* m_label_Disabled; + wxStatusBar* m_statusbar; + + CpuUsageProvider m_CpuUsage; + +public: + GSFrame(wxWindow* parent, const wxString& title); + virtual ~GSFrame() throw(); + + GSPanel* GetViewport(); + void SetFocus(); + bool Show( bool shown=true ); + wxStaticText* GetLabel_OutputDisabled() const; + +protected: + void OnCloseWindow( wxCloseEvent& evt ); + void OnMove( wxMoveEvent& evt ); + void OnResize( wxSizeEvent& evt ); + void OnActivate( wxActivateEvent& evt ); + void OnUpdateTitle( wxTimerEvent& evt ); + + void AppStatusEvent_OnSettingsApplied(); + void CoreThread_OnResumed(); + void CoreThread_OnSuspended(); +}; + +// -------------------------------------------------------------------------------------- +// s* macros! ['s' stands for 'shortcut'] +// -------------------------------------------------------------------------------------- +// Use these for "silent fail" invocation of PCSX2 Application-related constructs. If the +// construct (albeit wxApp, MainFrame, CoreThread, etc) is null, the requested method will +// not be invoked, and an optional "else" clause can be affixed for handling the end case. +// +// See App.h (sApp) for more details. +// +#define sGSFrame \ + if( GSFrame* __gsframe_ = wxGetApp().GetGsFramePtr() ) (*__gsframe_) diff --git a/pcsx2/gui/SysState.cpp b/pcsx2/gui/SysState.cpp index 24f1c88676..0aac4d4218 100644 --- a/pcsx2/gui/SysState.cpp +++ b/pcsx2/gui/SysState.cpp @@ -1,353 +1,353 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 te 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 . - */ - -#include "PrecompiledHeader.h" -#include "App.h" - -#include "System/SysThreads.h" -#include "SaveState.h" - -#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 const char SavestateIdentString[] = "PCSX2 Savestate"; -static const uint SavestateIdentLen = sizeof(SavestateIdentString); - -static void SaveStateFile_WriteHeader( IStreamWriter& thr ) -{ - thr.Write( SavestateIdentString ); - thr.Write( g_SaveVersion ); -} - -static void SaveStateFile_ReadHeader( IStreamReader& thr ) -{ - char ident[SavestateIdentLen] = {0}; - - thr.Read( ident ); - - if( strcmp(SavestateIdentString, ident) ) - throw Exception::SaveStateLoadError( thr.GetStreamName(), - wxsFormat( L"Unrecognized file signature while loading savestate: %s", ident ), - _("File is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2.") - ); - - u32 savever; - thr.Read( savever ); - - if( (savever >> 16) != (g_SaveVersion >> 16) ) - throw Exception::SaveStateLoadError( thr.GetStreamName(), - wxsFormat( L"Unrecognized file signature while loading savestate: %s", ident ), - _("File is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2.") - ); - - if( savever > g_SaveVersion ) - throw Exception::SaveStateLoadError( thr.GetStreamName(), - wxsFormat( L"Unrecognized file signature while loading savestate: %s", ident ), - _("File is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2.") - ); -}; - -// -------------------------------------------------------------------------------------- -// gzipReader -// -------------------------------------------------------------------------------------- -// Interface for reading data from a gzip stream. -// -class gzipReader : public IStreamReader -{ - DeclareNoncopyableObject(gzipReader); - -protected: - wxString m_filename; - gzFile m_gzfp; - -public: - gzipReader( const wxString& filename ) - : m_filename( filename ) - { - if( NULL == (m_gzfp = gzopen( m_filename.ToUTF8(), "rb" )) ) - throw Exception::CannotCreateStream( m_filename, "Cannot open file for reading." ); - - gzbuffer(m_gzfp, 0x100000); // 1mb buffer for zlib internal operations - } - - virtual ~gzipReader() throw () - { - if( m_gzfp ) gzclose( m_gzfp ); - } - - wxString GetStreamName() const { return m_filename; } - - void Read( void* dest, size_t size ) - { - int result = gzread( m_gzfp, dest, size ); - if( result == -1) - throw Exception::BadStream( m_filename, "Data read failed: Invalid or corrupted gzip archive." ); - - if( (size_t)result < size ) - throw Exception::EndOfStream( m_filename ); - } -}; - -static bool IsSavingOrLoading = false; - - -// -------------------------------------------------------------------------------------- -// SysExecEvent_DownloadState -// -------------------------------------------------------------------------------------- -// Pauses core emulation and downloads the savestate into the state_buffer. -// -class SysExecEvent_DownloadState : public SysExecEvent -{ -protected: - VmStateBuffer* m_dest_buffer; - -public: - wxString GetEventName() const { return L"VM_Download"; } - - virtual ~SysExecEvent_DownloadState() throw() {} - SysExecEvent_DownloadState* Clone() const { return new SysExecEvent_DownloadState( *this ); } - SysExecEvent_DownloadState( VmStateBuffer* dest=NULL ) - { - m_dest_buffer = dest; - } - - bool IsCriticalEvent() const { return true; } - bool AllowCancelOnExit() const { return false; } - -protected: - void InvokeEvent() - { - ScopedCoreThreadPause paused_core; - - if( !SysHasValidState() ) - 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.") ); - - memSavingState(m_dest_buffer).FreezeAll(); - - UI_EnableStateActions(); - paused_core.AllowResume(); - } -}; - -// It's bad mojo to have savestates trying to read and write from the same file at the -// same time. To prevent that we use this mutex lock, which is used by both the -// CompressThread and the UnzipFromDisk events. (note that CompressThread locks the -// mutex during OnStartInThread, which ensures that the ZipToDisk event blocks; preventing -// the SysExecutor's Idle Event from re-enabing savestates and slots.) -// -static Mutex mtx_CompressToDisk; - -// -------------------------------------------------------------------------------------- -// CompressThread_VmState -// -------------------------------------------------------------------------------------- -class VmStateZipThread : public CompressThread_gzip -{ - typedef CompressThread_gzip _parent; - -protected: - ScopedLock m_lock_Compress; - -public: - VmStateZipThread( const wxString& file, VmStateBuffer* srcdata ) - : _parent( file, srcdata, SaveStateFile_WriteHeader ) - { - m_lock_Compress.Assign(mtx_CompressToDisk); - } - - VmStateZipThread( const wxString& file, ScopedPtr& srcdata ) - : _parent( file, srcdata, SaveStateFile_WriteHeader ) - { - m_lock_Compress.Assign(mtx_CompressToDisk); - } - - virtual ~VmStateZipThread() throw() - { - - } - -protected: - void OnStartInThread() - { - _parent::OnStartInThread(); - m_lock_Compress.Acquire(); - } - - void OnCleanupInThread() - { - m_lock_Compress.Release(); - _parent::OnCleanupInThread(); - } -}; - -// -------------------------------------------------------------------------------------- -// SysExecEvent_ZipToDisk -// -------------------------------------------------------------------------------------- -class SysExecEvent_ZipToDisk : public SysExecEvent -{ -protected: - VmStateBuffer* m_src_buffer; - wxString m_filename; - -public: - wxString GetEventName() const { return L"VM_ZipToDisk"; } - - virtual ~SysExecEvent_ZipToDisk() throw() - { - delete m_src_buffer; - } - - SysExecEvent_ZipToDisk* Clone() const { return new SysExecEvent_ZipToDisk( *this ); } - - SysExecEvent_ZipToDisk( ScopedPtr& src, const wxString& filename ) - : m_filename( filename ) - { - m_src_buffer = src.DetachPtr(); - } - - SysExecEvent_ZipToDisk( VmStateBuffer* src, const wxString& filename ) - : m_filename( filename ) - { - m_src_buffer = src; - } - - bool IsCriticalEvent() const { return true; } - bool AllowCancelOnExit() const { return false; } - -protected: - void InvokeEvent() - { - (new VmStateZipThread( m_filename, m_src_buffer ))->Start(); - m_src_buffer = NULL; - } -}; - -// -------------------------------------------------------------------------------------- -// SysExecEvent_UnzipFromDisk -// -------------------------------------------------------------------------------------- -// 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 -{ -protected: - wxString m_filename; - -public: - wxString GetEventName() const { return L"VM_UnzipFromDisk"; } - - virtual ~SysExecEvent_UnzipFromDisk() throw() {} - SysExecEvent_UnzipFromDisk* Clone() const { return new SysExecEvent_UnzipFromDisk( *this ); } - SysExecEvent_UnzipFromDisk( const wxString& filename ) - : m_filename( filename ) - { - } - - wxString GetStreamName() const { return m_filename; } - -protected: - void InvokeEvent() - { - ScopedLock lock( mtx_CompressToDisk ); - gzipReader m_gzreader(m_filename ); - SaveStateFile_ReadHeader( m_gzreader ); - - // We use direct Suspend/Resume control here, since it's desirable that emulation - // *ALWAYS* start execution after the new savestate is loaded. - - GetCoreThread().Pause(); - - // fixme: should start initially with the file size, and then grow from there. - - static const int BlockSize = 0x100000; - - VmStateBuffer buffer( 0x800000, L"StateBuffer_UnzipFromDisk" ); // start with an 8 meg buffer to avoid frequent reallocation. - int curidx = 0; - - try { - while(true) { - buffer.MakeRoomFor( curidx+BlockSize ); - m_gzreader.Read( buffer.GetPtr(curidx), BlockSize ); - curidx += BlockSize; - Threading::pxTestCancel(); - } - } - catch( Exception::EndOfStream& ) - { - // This exception actually means success! Any others we let get sent - // to the main event handler/thread for handling. - } - - // 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().UploadStateCopy( buffer ); - GetCoreThread().Resume(); // force resume regardless of emulation state earlier. - } -}; - -// ===================================================================================================== -// StateCopy Public Interface -// ===================================================================================================== - -void StateCopy_SaveToFile( const wxString& file ) -{ - UI_DisableStateActions(); - - ScopedPtr zipbuf(new VmStateBuffer( L"Zippable Savestate" )); - GetSysExecutorThread().PostEvent(new SysExecEvent_DownloadState( zipbuf )); - GetSysExecutorThread().PostEvent(new SysExecEvent_ZipToDisk( zipbuf, file )); -} - -void StateCopy_LoadFromFile( const wxString& file ) -{ - UI_DisableSysActions(); - GetSysExecutorThread().PostEvent(new SysExecEvent_UnzipFromDisk( file )); -} - -// Saves recovery state info to the given saveslot, or saves the active emulation state -// (if one exists) and no recovery data was found. This is needed because when a recovery -// state is made, the emulation state is usually reset so the only persisting state is -// the one in the memory save. :) -void StateCopy_SaveToSlot( uint num ) -{ - const wxString file( SaveStateBase::GetFilename( num ) ); - - Console.WriteLn( Color_StrongGreen, "Saving savestate to slot %d...", num ); - Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", file.c_str() ); - - StateCopy_SaveToFile( file ); -} - -void StateCopy_LoadFromSlot( uint slot ) -{ - wxString file( SaveStateBase::GetFilename( slot ) ); - - if( !wxFileExists( file ) ) - { - Console.Warning( "Savestate slot %d is empty.", slot ); - return; - } - - Console.WriteLn( Color_StrongGreen, "Loading savestate from slot %d...", slot ); - Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", file.c_str() ); - - StateCopy_LoadFromFile( file ); -} +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2010 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 te 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 . + */ + +#include "PrecompiledHeader.h" +#include "App.h" + +#include "System/SysThreads.h" +#include "SaveState.h" + +#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 const char SavestateIdentString[] = "PCSX2 Savestate"; +static const uint SavestateIdentLen = sizeof(SavestateIdentString); + +static void SaveStateFile_WriteHeader( IStreamWriter& thr ) +{ + thr.Write( SavestateIdentString ); + thr.Write( g_SaveVersion ); +} + +static void SaveStateFile_ReadHeader( IStreamReader& thr ) +{ + char ident[SavestateIdentLen] = {0}; + + thr.Read( ident ); + + if( strcmp(SavestateIdentString, ident) ) + throw Exception::SaveStateLoadError( thr.GetStreamName(), + wxsFormat( L"Unrecognized file signature while loading savestate: %s", ident ), + _("File is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2.") + ); + + u32 savever; + thr.Read( savever ); + + if( (savever >> 16) != (g_SaveVersion >> 16) ) + throw Exception::SaveStateLoadError( thr.GetStreamName(), + wxsFormat( L"Unrecognized file signature while loading savestate: %s", ident ), + _("File is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2.") + ); + + if( savever > g_SaveVersion ) + throw Exception::SaveStateLoadError( thr.GetStreamName(), + wxsFormat( L"Unrecognized file signature while loading savestate: %s", ident ), + _("File is not a valid PCSX2 savestate, or is from an older unsupported version of PCSX2.") + ); +}; + +// -------------------------------------------------------------------------------------- +// gzipReader +// -------------------------------------------------------------------------------------- +// Interface for reading data from a gzip stream. +// +class gzipReader : public IStreamReader +{ + DeclareNoncopyableObject(gzipReader); + +protected: + wxString m_filename; + gzFile m_gzfp; + +public: + gzipReader( const wxString& filename ) + : m_filename( filename ) + { + if( NULL == (m_gzfp = gzopen( m_filename.ToUTF8(), "rb" )) ) + throw Exception::CannotCreateStream( m_filename, "Cannot open file for reading." ); + + gzbuffer(m_gzfp, 0x100000); // 1mb buffer for zlib internal operations + } + + virtual ~gzipReader() throw () + { + if( m_gzfp ) gzclose( m_gzfp ); + } + + wxString GetStreamName() const { return m_filename; } + + void Read( void* dest, size_t size ) + { + int result = gzread( m_gzfp, dest, size ); + if( result == -1) + throw Exception::BadStream( m_filename, "Data read failed: Invalid or corrupted gzip archive." ); + + if( (size_t)result < size ) + throw Exception::EndOfStream( m_filename ); + } +}; + +static bool IsSavingOrLoading = false; + + +// -------------------------------------------------------------------------------------- +// SysExecEvent_DownloadState +// -------------------------------------------------------------------------------------- +// Pauses core emulation and downloads the savestate into the state_buffer. +// +class SysExecEvent_DownloadState : public SysExecEvent +{ +protected: + VmStateBuffer* m_dest_buffer; + +public: + wxString GetEventName() const { return L"VM_Download"; } + + virtual ~SysExecEvent_DownloadState() throw() {} + SysExecEvent_DownloadState* Clone() const { return new SysExecEvent_DownloadState( *this ); } + SysExecEvent_DownloadState( VmStateBuffer* dest=NULL ) + { + m_dest_buffer = dest; + } + + bool IsCriticalEvent() const { return true; } + bool AllowCancelOnExit() const { return false; } + +protected: + void InvokeEvent() + { + ScopedCoreThreadPause paused_core; + + if( !SysHasValidState() ) + 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.") ); + + memSavingState(m_dest_buffer).FreezeAll(); + + UI_EnableStateActions(); + paused_core.AllowResume(); + } +}; + +// It's bad mojo to have savestates trying to read and write from the same file at the +// same time. To prevent that we use this mutex lock, which is used by both the +// CompressThread and the UnzipFromDisk events. (note that CompressThread locks the +// mutex during OnStartInThread, which ensures that the ZipToDisk event blocks; preventing +// the SysExecutor's Idle Event from re-enabing savestates and slots.) +// +static Mutex mtx_CompressToDisk; + +// -------------------------------------------------------------------------------------- +// CompressThread_VmState +// -------------------------------------------------------------------------------------- +class VmStateZipThread : public CompressThread_gzip +{ + typedef CompressThread_gzip _parent; + +protected: + ScopedLock m_lock_Compress; + +public: + VmStateZipThread( const wxString& file, VmStateBuffer* srcdata ) + : _parent( file, srcdata, SaveStateFile_WriteHeader ) + { + m_lock_Compress.Assign(mtx_CompressToDisk); + } + + VmStateZipThread( const wxString& file, ScopedPtr& srcdata ) + : _parent( file, srcdata, SaveStateFile_WriteHeader ) + { + m_lock_Compress.Assign(mtx_CompressToDisk); + } + + virtual ~VmStateZipThread() throw() + { + + } + +protected: + void OnStartInThread() + { + _parent::OnStartInThread(); + m_lock_Compress.Acquire(); + } + + void OnCleanupInThread() + { + m_lock_Compress.Release(); + _parent::OnCleanupInThread(); + } +}; + +// -------------------------------------------------------------------------------------- +// SysExecEvent_ZipToDisk +// -------------------------------------------------------------------------------------- +class SysExecEvent_ZipToDisk : public SysExecEvent +{ +protected: + VmStateBuffer* m_src_buffer; + wxString m_filename; + +public: + wxString GetEventName() const { return L"VM_ZipToDisk"; } + + virtual ~SysExecEvent_ZipToDisk() throw() + { + delete m_src_buffer; + } + + SysExecEvent_ZipToDisk* Clone() const { return new SysExecEvent_ZipToDisk( *this ); } + + SysExecEvent_ZipToDisk( ScopedPtr& src, const wxString& filename ) + : m_filename( filename ) + { + m_src_buffer = src.DetachPtr(); + } + + SysExecEvent_ZipToDisk( VmStateBuffer* src, const wxString& filename ) + : m_filename( filename ) + { + m_src_buffer = src; + } + + bool IsCriticalEvent() const { return true; } + bool AllowCancelOnExit() const { return false; } + +protected: + void InvokeEvent() + { + (new VmStateZipThread( m_filename, m_src_buffer ))->Start(); + m_src_buffer = NULL; + } +}; + +// -------------------------------------------------------------------------------------- +// SysExecEvent_UnzipFromDisk +// -------------------------------------------------------------------------------------- +// 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 +{ +protected: + wxString m_filename; + +public: + wxString GetEventName() const { return L"VM_UnzipFromDisk"; } + + virtual ~SysExecEvent_UnzipFromDisk() throw() {} + SysExecEvent_UnzipFromDisk* Clone() const { return new SysExecEvent_UnzipFromDisk( *this ); } + SysExecEvent_UnzipFromDisk( const wxString& filename ) + : m_filename( filename ) + { + } + + wxString GetStreamName() const { return m_filename; } + +protected: + void InvokeEvent() + { + ScopedLock lock( mtx_CompressToDisk ); + gzipReader m_gzreader(m_filename ); + SaveStateFile_ReadHeader( m_gzreader ); + + // We use direct Suspend/Resume control here, since it's desirable that emulation + // *ALWAYS* start execution after the new savestate is loaded. + + GetCoreThread().Pause(); + + // fixme: should start initially with the file size, and then grow from there. + + static const int BlockSize = 0x100000; + + VmStateBuffer buffer( 0x800000, L"StateBuffer_UnzipFromDisk" ); // start with an 8 meg buffer to avoid frequent reallocation. + int curidx = 0; + + try { + while(true) { + buffer.MakeRoomFor( curidx+BlockSize ); + m_gzreader.Read( buffer.GetPtr(curidx), BlockSize ); + curidx += BlockSize; + Threading::pxTestCancel(); + } + } + catch( Exception::EndOfStream& ) + { + // This exception actually means success! Any others we let get sent + // to the main event handler/thread for handling. + } + + // 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().UploadStateCopy( buffer ); + GetCoreThread().Resume(); // force resume regardless of emulation state earlier. + } +}; + +// ===================================================================================================== +// StateCopy Public Interface +// ===================================================================================================== + +void StateCopy_SaveToFile( const wxString& file ) +{ + UI_DisableStateActions(); + + ScopedPtr zipbuf(new VmStateBuffer( L"Zippable Savestate" )); + GetSysExecutorThread().PostEvent(new SysExecEvent_DownloadState( zipbuf )); + GetSysExecutorThread().PostEvent(new SysExecEvent_ZipToDisk( zipbuf, file )); +} + +void StateCopy_LoadFromFile( const wxString& file ) +{ + UI_DisableSysActions(); + GetSysExecutorThread().PostEvent(new SysExecEvent_UnzipFromDisk( file )); +} + +// Saves recovery state info to the given saveslot, or saves the active emulation state +// (if one exists) and no recovery data was found. This is needed because when a recovery +// state is made, the emulation state is usually reset so the only persisting state is +// the one in the memory save. :) +void StateCopy_SaveToSlot( uint num ) +{ + const wxString file( SaveStateBase::GetFilename( num ) ); + + Console.WriteLn( Color_StrongGreen, "Saving savestate to slot %d...", num ); + Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", file.c_str() ); + + StateCopy_SaveToFile( file ); +} + +void StateCopy_LoadFromSlot( uint slot ) +{ + wxString file( SaveStateBase::GetFilename( slot ) ); + + if( !wxFileExists( file ) ) + { + Console.Warning( "Savestate slot %d is empty.", slot ); + return; + } + + Console.WriteLn( Color_StrongGreen, "Loading savestate from slot %d...", slot ); + Console.Indent().WriteLn( Color_StrongGreen, L"filename: %s", file.c_str() ); + + StateCopy_LoadFromFile( file ); +} diff --git a/pcsx2/gui/UpdateUI.cpp b/pcsx2/gui/UpdateUI.cpp index e6788042ef..1a15f7373a 100644 --- a/pcsx2/gui/UpdateUI.cpp +++ b/pcsx2/gui/UpdateUI.cpp @@ -1,109 +1,109 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#include "PrecompiledHeader.h" -#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. >_< -void MainEmuFrame::EnableMenuItem( int id, bool enable ) -{ - if( wxMenuItem* item = m_menubar.FindItem(id) ) - item->Enable( enable ); -} - -static void _SaveLoadStuff( bool enabled ) -{ - sMainFrame.EnableMenuItem( MenuId_Sys_LoadStates, enabled ); - sMainFrame.EnableMenuItem( MenuId_Sys_SaveStates, enabled ); -} - -// Updates the enable/disable status of all System related controls: menus, toolbars, -// etc. Typically called by SysEvtHandler whenever the message pump becomes idle. -void UI_UpdateSysControls() -{ - if( wxGetApp().PostMethodMyself( &UI_UpdateSysControls ) ) return; - - sApp.PostAction( CoreThreadStatusEvent( CoreThread_Indeterminate ) ); - - _SaveLoadStuff( true ); -} - -void UI_DisableSysReset() -{ - if( wxGetApp().PostMethodMyself( UI_DisableSysReset ) ) return; - sMainFrame.EnableMenuItem( MenuId_Sys_Restart, false ); - - _SaveLoadStuff( false ); -} - -void UI_DisableSysShutdown() -{ - if( wxGetApp().PostMethodMyself( &UI_DisableSysShutdown ) ) return; - - sMainFrame.EnableMenuItem( MenuId_Sys_Restart, false ); - sMainFrame.EnableMenuItem( MenuId_Sys_Shutdown, false ); -} - -void UI_EnableSysShutdown() -{ - if( wxGetApp().PostMethodMyself( &UI_EnableSysShutdown ) ) return; - - sMainFrame.EnableMenuItem( MenuId_Sys_Restart, true ); - sMainFrame.EnableMenuItem( MenuId_Sys_Shutdown, true ); -} - - -void UI_DisableSysActions() -{ - if( wxGetApp().PostMethodMyself( &UI_DisableSysActions ) ) return; - - sMainFrame.EnableMenuItem( MenuId_Sys_Restart, false ); - sMainFrame.EnableMenuItem( MenuId_Sys_Shutdown, false ); - - _SaveLoadStuff( false ); -} - -void UI_EnableSysActions() -{ - if( wxGetApp().PostMethodMyself( &UI_EnableSysActions ) ) return; - - sMainFrame.EnableMenuItem( MenuId_Sys_Restart, true ); - sMainFrame.EnableMenuItem( MenuId_Sys_Shutdown, true ); - - _SaveLoadStuff( true ); -} - -void UI_DisableStateActions() -{ - if( wxGetApp().PostMethodMyself( &UI_DisableStateActions ) ) return; - _SaveLoadStuff( false ); -} - -void UI_EnableStateActions() -{ - if( wxGetApp().PostMethodMyself( &UI_EnableStateActions ) ) return; - _SaveLoadStuff( true ); -} - - +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2010 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 . + */ + +#include "PrecompiledHeader.h" +#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. >_< +void MainEmuFrame::EnableMenuItem( int id, bool enable ) +{ + if( wxMenuItem* item = m_menubar.FindItem(id) ) + item->Enable( enable ); +} + +static void _SaveLoadStuff( bool enabled ) +{ + sMainFrame.EnableMenuItem( MenuId_Sys_LoadStates, enabled ); + sMainFrame.EnableMenuItem( MenuId_Sys_SaveStates, enabled ); +} + +// Updates the enable/disable status of all System related controls: menus, toolbars, +// etc. Typically called by SysEvtHandler whenever the message pump becomes idle. +void UI_UpdateSysControls() +{ + if( wxGetApp().PostMethodMyself( &UI_UpdateSysControls ) ) return; + + sApp.PostAction( CoreThreadStatusEvent( CoreThread_Indeterminate ) ); + + _SaveLoadStuff( true ); +} + +void UI_DisableSysReset() +{ + if( wxGetApp().PostMethodMyself( UI_DisableSysReset ) ) return; + sMainFrame.EnableMenuItem( MenuId_Sys_Restart, false ); + + _SaveLoadStuff( false ); +} + +void UI_DisableSysShutdown() +{ + if( wxGetApp().PostMethodMyself( &UI_DisableSysShutdown ) ) return; + + sMainFrame.EnableMenuItem( MenuId_Sys_Restart, false ); + sMainFrame.EnableMenuItem( MenuId_Sys_Shutdown, false ); +} + +void UI_EnableSysShutdown() +{ + if( wxGetApp().PostMethodMyself( &UI_EnableSysShutdown ) ) return; + + sMainFrame.EnableMenuItem( MenuId_Sys_Restart, true ); + sMainFrame.EnableMenuItem( MenuId_Sys_Shutdown, true ); +} + + +void UI_DisableSysActions() +{ + if( wxGetApp().PostMethodMyself( &UI_DisableSysActions ) ) return; + + sMainFrame.EnableMenuItem( MenuId_Sys_Restart, false ); + sMainFrame.EnableMenuItem( MenuId_Sys_Shutdown, false ); + + _SaveLoadStuff( false ); +} + +void UI_EnableSysActions() +{ + if( wxGetApp().PostMethodMyself( &UI_EnableSysActions ) ) return; + + sMainFrame.EnableMenuItem( MenuId_Sys_Restart, true ); + sMainFrame.EnableMenuItem( MenuId_Sys_Shutdown, true ); + + _SaveLoadStuff( true ); +} + +void UI_DisableStateActions() +{ + if( wxGetApp().PostMethodMyself( &UI_DisableStateActions ) ) return; + _SaveLoadStuff( false ); +} + +void UI_EnableStateActions() +{ + if( wxGetApp().PostMethodMyself( &UI_EnableStateActions ) ) return; + _SaveLoadStuff( true ); +} + + diff --git a/pcsx2/gui/pxEventThread.h b/pcsx2/gui/pxEventThread.h index fb3d394a00..40ad185d40 100644 --- a/pcsx2/gui/pxEventThread.h +++ b/pcsx2/gui/pxEventThread.h @@ -1,237 +1,237 @@ -/* PCSX2 - PS2 Emulator for PCs - * Copyright (C) 2002-2010 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 . - */ - -#pragma once - -#include "Utilities/PersistentThread.h" -#include "Utilities/pxEvents.h" - -// TODO!! Make this system a bit more generic, and then move it to the Utilities library. - -// -------------------------------------------------------------------------------------- -// SysExecEvent -// -------------------------------------------------------------------------------------- -// Base class for all pxEvtHandler processable events. -// -// Rules for deriving: -// * Override InvokeEvent(), *NOT* _DoInvokeEvent(). _DoInvokeEvent() performs setup and -// wraps exceptions for transport to the invoking context/thread, and then itself calls -// InvokeEvent() to perform the derived class implementation. -// -// * Derived classes must implement their own versions of an empty constructor and -// Clone(), or else the class will fail to be copied to the event handler's thread -// context correctly. -// -// * This class is not abstract, and gives no error if the invocation method is not -// overridden: It can be used as a simple ping device against the event queue, Re- -// awaking the invoking thread as soon as the queue has caught up to and processed -// the event. -// -// * Avoid using virtual class inheritence. It's unreliable at best. -// -class SysExecEvent : public ICloneable -{ -protected: - SynchronousActionState* m_sync; - -public: - virtual ~SysExecEvent() throw() {} - SysExecEvent* Clone() const { return new SysExecEvent( *this ); } - - SysExecEvent( SynchronousActionState* sync=NULL ) - { - m_sync = sync; - } - - SysExecEvent( SynchronousActionState& sync ) - { - m_sync = &sync; - } - - const SynchronousActionState* GetSyncState() const { return m_sync; } - SynchronousActionState* GetSyncState() { return m_sync; } - - 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 - // system, since program crashes can occur if the program tries to exit while threads - // are running. - virtual bool IsCriticalEvent() const { return false; } - - // Tells the Event Handler whether or not this event can be canceled. Typically events - // should not prohibit cancellation, since it expedites program termination and helps - // avoid permanent deadlock. Some actions like saving states and shutdown procedures - // should not allow cancellation since they could result in program crashes or corrupted - // data. - virtual bool AllowCancelOnExit() const { return true; } - - virtual void _DoInvokeEvent(); - virtual void PostResult() const; - - virtual wxString GetEventName() const; - virtual wxString GetEventMessage() const; - - virtual int GetResult() - { - if( !pxAssertDev( m_sync != NULL, "SysEvent: Expecting return value, but no sync object provided." ) ) return 0; - return m_sync->return_value; - } - - virtual void SetException( BaseException* ex ); - - void SetException( const BaseException& ex ); - -protected: - virtual void InvokeEvent(); - virtual void CleanupEvent(); -}; - -// -------------------------------------------------------------------------------------- -// SysExecEvent_MethodVoid -// -------------------------------------------------------------------------------------- -class SysExecEvent_MethodVoid : public SysExecEvent -{ -protected: - FnType_Void* m_method; - -public: - wxString GetEventName() const { return L"MethodVoid"; } - - virtual ~SysExecEvent_MethodVoid() throw() {} - SysExecEvent_MethodVoid* Clone() const { return new SysExecEvent_MethodVoid( *this ); } - - explicit SysExecEvent_MethodVoid( FnType_Void* method = NULL ) - { - m_method = method; - } - -protected: - void InvokeEvent() - { - if( m_method ) m_method(); - } -}; - - -typedef std::list pxEvtList; - -// -------------------------------------------------------------------------------------- -// pxEvtHandler -// -------------------------------------------------------------------------------------- -// Purpose: To provide a safe environment for queuing tasks that must be executed in -// sequential order (in blocking fashion). Unlike the wxWidgets event handlers, instances -// of this handler can be stalled for extended periods of time without affecting the -// responsiveness of the GUI or frame updates of the DirectX output windows. This class -// is mostly intended to be used from the context of an ExecutorThread. -// -// Rationales: -// * Using the main event handler of wxWidgets is dangerous because it must call itself -// recursively when waiting on threaded events such as semaphore and mutexes. Thus, -// tasks such as suspending the VM would invoke the event queue while waiting, -// running events that expect the suspend to be complete while the suspend was still -// pending. -// -// * wxWidgets Event Queue (wxEvtHandler) isn't thread-safe and isn't even -// intended for use for anything other than wxWindow events (it uses static vars -// and checks/modifies wxApp globals while processing), so it's useless to us. -// Have to roll our own. -_- -// -class pxEvtHandler -{ -protected: - pxEvtList m_pendingEvents; - pxEvtList m_idleEvents; - - Threading::MutexRecursive m_mtx_pending; - Threading::Semaphore m_wakeup; - wxThreadIdType m_OwnerThreadId; - volatile u32 m_Quitting; - - // Used for performance measuring the execution of individual events, - // and also for detecting deadlocks during message processing. - volatile u64 m_qpc_Start; - -public: - pxEvtHandler(); - virtual ~pxEvtHandler() throw() {} - - virtual wxString GetEventHandlerName() const { return L"pxEvtHandler"; } - - virtual void ShutdownQueue(); - bool IsShuttingDown() const { return !!m_Quitting; } - - void ProcessEvents( pxEvtList& list ); - void ProcessPendingEvents(); - void ProcessIdleEvents(); - void Idle(); - - void AddPendingEvent( SysExecEvent& evt ); - void PostEvent( SysExecEvent* evt ); - void PostEvent( const SysExecEvent& evt ); - void PostIdleEvent( SysExecEvent* evt ); - void PostIdleEvent( const SysExecEvent& evt ); - - void ProcessEvent( SysExecEvent* evt ); - void ProcessEvent( SysExecEvent& evt ); - - bool ProcessMethodSelf( FnType_Void* method ); - void SetActiveThread(); - -protected: - virtual void _DoIdle() {} -}; - -// -------------------------------------------------------------------------------------- -// ExecutorThread -// -------------------------------------------------------------------------------------- -// Threaded wrapper class for implementing pxEvtHandler. Simply create the desired -// EvtHandler, start the thread, and enjoy queued event execution in fully blocking fashion. -// -class ExecutorThread : public Threading::PersistentThread -{ - typedef Threading::PersistentThread _parent; - -protected: - ScopedPtr m_ExecutorTimer; - ScopedPtr m_EvtHandler; - -public: - ExecutorThread( pxEvtHandler* evtandler = NULL ); - virtual ~ExecutorThread() throw() { } - - virtual void ShutdownQueue(); - - void PostEvent( SysExecEvent* evt ); - void PostEvent( const SysExecEvent& evt ); - - void PostIdleEvent( SysExecEvent* evt ); - void PostIdleEvent( const SysExecEvent& evt ); - - void ProcessEvent( SysExecEvent* evt ); - void ProcessEvent( SysExecEvent& evt ); - - bool ProcessMethodSelf( void (*evt)() ) - { - return m_EvtHandler ? m_EvtHandler->ProcessMethodSelf( evt ) : false; - } - -protected: - void OnStart(); - void ExecuteTaskInThread(); - void OnCleanupInThread(); -}; - +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2010 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 . + */ + +#pragma once + +#include "Utilities/PersistentThread.h" +#include "Utilities/pxEvents.h" + +// TODO!! Make this system a bit more generic, and then move it to the Utilities library. + +// -------------------------------------------------------------------------------------- +// SysExecEvent +// -------------------------------------------------------------------------------------- +// Base class for all pxEvtHandler processable events. +// +// Rules for deriving: +// * Override InvokeEvent(), *NOT* _DoInvokeEvent(). _DoInvokeEvent() performs setup and +// wraps exceptions for transport to the invoking context/thread, and then itself calls +// InvokeEvent() to perform the derived class implementation. +// +// * Derived classes must implement their own versions of an empty constructor and +// Clone(), or else the class will fail to be copied to the event handler's thread +// context correctly. +// +// * This class is not abstract, and gives no error if the invocation method is not +// overridden: It can be used as a simple ping device against the event queue, Re- +// awaking the invoking thread as soon as the queue has caught up to and processed +// the event. +// +// * Avoid using virtual class inheritence. It's unreliable at best. +// +class SysExecEvent : public ICloneable +{ +protected: + SynchronousActionState* m_sync; + +public: + virtual ~SysExecEvent() throw() {} + SysExecEvent* Clone() const { return new SysExecEvent( *this ); } + + SysExecEvent( SynchronousActionState* sync=NULL ) + { + m_sync = sync; + } + + SysExecEvent( SynchronousActionState& sync ) + { + m_sync = &sync; + } + + const SynchronousActionState* GetSyncState() const { return m_sync; } + SynchronousActionState* GetSyncState() { return m_sync; } + + 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 + // system, since program crashes can occur if the program tries to exit while threads + // are running. + virtual bool IsCriticalEvent() const { return false; } + + // Tells the Event Handler whether or not this event can be canceled. Typically events + // should not prohibit cancellation, since it expedites program termination and helps + // avoid permanent deadlock. Some actions like saving states and shutdown procedures + // should not allow cancellation since they could result in program crashes or corrupted + // data. + virtual bool AllowCancelOnExit() const { return true; } + + virtual void _DoInvokeEvent(); + virtual void PostResult() const; + + virtual wxString GetEventName() const; + virtual wxString GetEventMessage() const; + + virtual int GetResult() + { + if( !pxAssertDev( m_sync != NULL, "SysEvent: Expecting return value, but no sync object provided." ) ) return 0; + return m_sync->return_value; + } + + virtual void SetException( BaseException* ex ); + + void SetException( const BaseException& ex ); + +protected: + virtual void InvokeEvent(); + virtual void CleanupEvent(); +}; + +// -------------------------------------------------------------------------------------- +// SysExecEvent_MethodVoid +// -------------------------------------------------------------------------------------- +class SysExecEvent_MethodVoid : public SysExecEvent +{ +protected: + FnType_Void* m_method; + +public: + wxString GetEventName() const { return L"MethodVoid"; } + + virtual ~SysExecEvent_MethodVoid() throw() {} + SysExecEvent_MethodVoid* Clone() const { return new SysExecEvent_MethodVoid( *this ); } + + explicit SysExecEvent_MethodVoid( FnType_Void* method = NULL ) + { + m_method = method; + } + +protected: + void InvokeEvent() + { + if( m_method ) m_method(); + } +}; + + +typedef std::list pxEvtList; + +// -------------------------------------------------------------------------------------- +// pxEvtHandler +// -------------------------------------------------------------------------------------- +// Purpose: To provide a safe environment for queuing tasks that must be executed in +// sequential order (in blocking fashion). Unlike the wxWidgets event handlers, instances +// of this handler can be stalled for extended periods of time without affecting the +// responsiveness of the GUI or frame updates of the DirectX output windows. This class +// is mostly intended to be used from the context of an ExecutorThread. +// +// Rationales: +// * Using the main event handler of wxWidgets is dangerous because it must call itself +// recursively when waiting on threaded events such as semaphore and mutexes. Thus, +// tasks such as suspending the VM would invoke the event queue while waiting, +// running events that expect the suspend to be complete while the suspend was still +// pending. +// +// * wxWidgets Event Queue (wxEvtHandler) isn't thread-safe and isn't even +// intended for use for anything other than wxWindow events (it uses static vars +// and checks/modifies wxApp globals while processing), so it's useless to us. +// Have to roll our own. -_- +// +class pxEvtHandler +{ +protected: + pxEvtList m_pendingEvents; + pxEvtList m_idleEvents; + + Threading::MutexRecursive m_mtx_pending; + Threading::Semaphore m_wakeup; + wxThreadIdType m_OwnerThreadId; + volatile u32 m_Quitting; + + // Used for performance measuring the execution of individual events, + // and also for detecting deadlocks during message processing. + volatile u64 m_qpc_Start; + +public: + pxEvtHandler(); + virtual ~pxEvtHandler() throw() {} + + virtual wxString GetEventHandlerName() const { return L"pxEvtHandler"; } + + virtual void ShutdownQueue(); + bool IsShuttingDown() const { return !!m_Quitting; } + + void ProcessEvents( pxEvtList& list ); + void ProcessPendingEvents(); + void ProcessIdleEvents(); + void Idle(); + + void AddPendingEvent( SysExecEvent& evt ); + void PostEvent( SysExecEvent* evt ); + void PostEvent( const SysExecEvent& evt ); + void PostIdleEvent( SysExecEvent* evt ); + void PostIdleEvent( const SysExecEvent& evt ); + + void ProcessEvent( SysExecEvent* evt ); + void ProcessEvent( SysExecEvent& evt ); + + bool ProcessMethodSelf( FnType_Void* method ); + void SetActiveThread(); + +protected: + virtual void _DoIdle() {} +}; + +// -------------------------------------------------------------------------------------- +// ExecutorThread +// -------------------------------------------------------------------------------------- +// Threaded wrapper class for implementing pxEvtHandler. Simply create the desired +// EvtHandler, start the thread, and enjoy queued event execution in fully blocking fashion. +// +class ExecutorThread : public Threading::PersistentThread +{ + typedef Threading::PersistentThread _parent; + +protected: + ScopedPtr m_ExecutorTimer; + ScopedPtr m_EvtHandler; + +public: + ExecutorThread( pxEvtHandler* evtandler = NULL ); + virtual ~ExecutorThread() throw() { } + + virtual void ShutdownQueue(); + + void PostEvent( SysExecEvent* evt ); + void PostEvent( const SysExecEvent& evt ); + + void PostIdleEvent( SysExecEvent* evt ); + void PostIdleEvent( const SysExecEvent& evt ); + + void ProcessEvent( SysExecEvent* evt ); + void ProcessEvent( SysExecEvent& evt ); + + bool ProcessMethodSelf( void (*evt)() ) + { + return m_EvtHandler ? m_EvtHandler->ProcessMethodSelf( evt ) : false; + } + +protected: + void OnStart(); + void ExecuteTaskInThread(); + void OnCleanupInThread(); +}; +