From 15d2824d1be8404858c396590792ffabaa6143dc Mon Sep 17 00:00:00 2001 From: "Jake.Stine" Date: Tue, 29 Sep 2009 19:16:00 +0000 Subject: [PATCH] (work in progress -- some of this stuff still doesn't quite work as it should) * Rewrote Savestate code, fixed lots of stuff in PersistentThread, and renamed most "Emu"s to "Sys". * Removed wxScopedPtr and whipped up our own ScopedPtr that doesn't use boost's fail-style function names (and made it more thread-safe). * Handling exceptions across threads: Added Clone() and Rethrow() methods to BaseException, and added a Rethrow() to PersistentThread. * Go rid of AppInvoke macros (those were nasty!) git-svn-id: http://pcsx2.googlecode.com/svn/trunk@1929 96395faa-99c1-11dd-bbfe-3dabce05a288 --- common/include/Pcsx2Types.h | 49 +-- common/include/Utilities/Exceptions.h | 11 +- common/include/Utilities/SafeArray.h | 16 +- common/include/Utilities/ScopedPtr.h | 208 ++++++++++++- common/include/Utilities/Threading.h | 93 ++++-- common/include/Utilities/wxBaseTools.h | 42 ++- common/src/Utilities/ThreadTools.cpp | 129 ++++++-- common/src/x86emitter/x86emitter.cpp | 12 +- pcsx2/GSState.cpp | 4 +- pcsx2/MTGS.cpp | 4 +- pcsx2/PluginManager.cpp | 24 +- pcsx2/Plugins.h | 7 +- pcsx2/RecoverySystem.cpp | 339 +++++++++++++++------ pcsx2/SaveState.h | 18 +- pcsx2/Sio.cpp | 12 +- pcsx2/System.cpp | 35 +-- pcsx2/System/SysThreads.cpp | 6 +- pcsx2/System/SysThreads.h | 14 +- pcsx2/gui/App.h | 126 +++----- pcsx2/gui/AppConfig.cpp | 3 +- pcsx2/gui/AppConfig.h | 2 +- pcsx2/gui/AppMain.cpp | 197 +++++++++--- pcsx2/gui/AppRes.cpp | 8 +- pcsx2/gui/Dialogs/AboutBoxDialog.cpp | 5 +- pcsx2/gui/FrameForGS.cpp | 15 +- pcsx2/gui/GlobalCommands.cpp | 63 ++-- pcsx2/gui/MainFrame.cpp | 27 +- pcsx2/gui/MainFrame.h | 4 +- pcsx2/gui/MainMenuClicks.cpp | 20 +- pcsx2/gui/Panels/BiosSelectorPanel.cpp | 6 +- pcsx2/gui/Panels/ConfigurationPanels.h | 8 +- pcsx2/gui/Panels/PluginSelectorPanel.cpp | 16 +- pcsx2/gui/Plugins.cpp | 24 +- pcsx2/gui/Saveslots.cpp | 24 +- pcsx2/windows/VCprojects/pcsx2_2008.vcproj | 8 +- pcsx2/windows/WinConsolePipe.cpp | 3 +- 36 files changed, 1073 insertions(+), 509 deletions(-) diff --git a/common/include/Pcsx2Types.h b/common/include/Pcsx2Types.h index 0bfc171976..5ca16dc129 100644 --- a/common/include/Pcsx2Types.h +++ b/common/include/Pcsx2Types.h @@ -105,23 +105,30 @@ typedef s32 sptr; ////////////////////////////////////////////////////////////////////////////////////////// // A rough-and-ready cross platform 128-bit datatype, Non-SSE style. // +// Note: These structs do not provide any additional constructors because C++ can't allow +// the use of dataypes with constructors in unions (and since unions are ont of the primary +// uses of these types, that measn we can't have constructors). Embedded functions for +// performing explicity conversion from 64 and 32 bit values is provided instead. +// #ifdef __cplusplus struct u128 { u64 lo; u64 hi; - // Implicit conversion from u64 - u128( u64 src ) : - lo( src ) - , hi( 0 ) {} + // Explicit conversion from u64 + static u128 From64( u64 src ) + { + u128 retval = { src, 0 }; + return retval; + } - // Implicit conversion from u32 - u128( u32 src ) : - lo( src ) - , hi( 0 ) {} - - u128() {} + // Explicit conversion from u32 + static u128 From32( u32 src ) + { + u128 retval = { src, 0 }; + return retval; + } }; struct s128 @@ -129,17 +136,19 @@ struct s128 s64 lo; s64 hi; - // Implicit conversion from u64 - s128( s64 src ) : - lo( src ) - , hi( 0 ) {} + // explicit conversion from s64, with sign extension. + static s128 From64( s64 src ) + { + s128 retval = { src, (src < 0) ? -1 : 0 }; + return retval; + } - // Implicit conversion from u32 - s128( s32 src ) : - lo( src ) - , hi( 0 ) {} - - s128() {} + // explicit conversion from s32, with sign extension. + static s128 From64( s32 src ) + { + s128 retval = { src, (src < 0) ? -1 : 0 }; + return retval; + } }; #else diff --git a/common/include/Utilities/Exceptions.h b/common/include/Utilities/Exceptions.h index 11bcddd587..c6eb8796bb 100644 --- a/common/include/Utilities/Exceptions.h +++ b/common/include/Utilities/Exceptions.h @@ -47,6 +47,8 @@ extern bool DevAssert( bool condition, const char* msg ); namespace Exception { + int MakeNewType(); + // -------------------------------------------------------------------------------------- // BaseException // -------------------------------------------------------------------------------------- @@ -87,6 +89,9 @@ namespace Exception // Returns a message suitable for end-user display. // This message is usually meant for display in a user popup or such. virtual wxString FormatDisplayMessage() const { return m_message_user; } + + virtual void Rethrow() const=0; + virtual BaseException* Clone() const=0; protected: // Construction using two pre-formatted pre-translated messages @@ -129,7 +134,9 @@ namespace Exception // it will be optionally translated. // #define DEFINE_EXCEPTION_COPYTORS( classname ) \ - virtual ~classname() throw() {} + virtual ~classname() throw() {} \ + virtual void Rethrow() const { throw classname( *this ); } \ + virtual BaseException* Clone() const { return new classname( *this ); } #define DEFINE_RUNTIME_EXCEPTION( classname, defmsg ) \ DEFINE_EXCEPTION_COPYTORS( classname ) \ @@ -161,7 +168,7 @@ namespace Exception DEFINE_LOGIC_EXCEPTION( LogicError, wxLt("An unhandled logic error has occurred.") ) }; - class ObjectIsNull : public RuntimeError + class ObjectIsNull : public virtual RuntimeError { public: wxString ObjectName; diff --git a/common/include/Utilities/SafeArray.h b/common/include/Utilities/SafeArray.h index 658dfaabf7..6e830ab7b2 100644 --- a/common/include/Utilities/SafeArray.h +++ b/common/include/Utilities/SafeArray.h @@ -33,27 +33,33 @@ extern void pcsx2_aligned_free(void* pmem); // pointer to null after deallocation. #define safe_delete( ptr ) \ - ((void) (delete ptr), ptr = NULL) + ((void) (delete (ptr)), (ptr) = NULL) #define safe_delete_array( ptr ) \ - ((void) (delete[] ptr), ptr = NULL) + ((void) (delete[] (ptr)), (ptr) = NULL) // fixme: I'm pretty sure modern libc implementations under gcc and msvc check null status // inside free(), meaning we shouldn't have to do it ourselves. But legacy implementations // didn't always check, so best to be cautious unless absolutely certain it's being covered on // all ported platforms. #define safe_free( ptr ) \ - ((void) (( ( ptr != NULL ) && (free( ptr ), !!0) ), ptr = NULL)) + ((void) (( ( (ptr) != NULL ) && (free( ptr ), !!0) ), (ptr) = NULL)) + +#define safe_fclose( ptr ) \ + ((void) (( ( (ptr) != NULL ) && (fclose( ptr ), !!0) ), (ptr) = NULL)) // Implementation note: all known implementations of _aligned_free check the pointer for // NULL status (our implementation under GCC, and microsoft's under MSVC), so no need to // do it here. #define safe_aligned_free( ptr ) \ - ((void) ( _aligned_free( ptr ), ptr = NULL )) + ((void) ( _aligned_free( ptr ), (ptr) = NULL )) #define SafeSysMunmap( ptr, size ) \ - ((void) ( HostSys::Munmap( (uptr)ptr, size ), ptr = NULL )) + ((void) ( HostSys::Munmap( (uptr)(ptr), size ), (ptr) = NULL )) +// Microsoft Windows only macro, useful for freeing out COM objects: +#define safe_release( ptr ) \ + ((void) (( ( (ptr) != NULL ) && ((ptr)->Release(), !!0) ), (ptr) = NULL)) ////////////////////////////////////////////////////////////////////////////////////////// // Handy little class for allocating a resizable memory block, complete with diff --git a/common/include/Utilities/ScopedPtr.h b/common/include/Utilities/ScopedPtr.h index 8b0722c478..44a34153ee 100644 --- a/common/include/Utilities/ScopedPtr.h +++ b/common/include/Utilities/ScopedPtr.h @@ -1,4 +1,210 @@ #pragma once -#include #include + +// -------------------------------------------------------------------------------------- +// ScopedPtr +// -------------------------------------------------------------------------------------- + +template< typename T > +class ScopedPtr +{ + DeclareNoncopyableObject(ScopedPtr); + +protected: + T* m_ptr; + +public: + typedef T element_type; + + wxEXPLICIT ScopedPtr(T * ptr = NULL) : m_ptr(ptr) { } + + ~ScopedPtr() + { Delete(); } + + ScopedPtr& Reassign(T * ptr = NULL) + { + if ( ptr != m_ptr ) + { + Delete(); + m_ptr = ptr; + } + return *this; + } + + ScopedPtr& Delete() + { + // Thread-safe deletion: Set the pointer to NULL first, and then issue + // the deletion. This allows pending Application messages that might be + // dependent on the current object to nullify their actions. + + T* deleteme = m_ptr; + m_ptr = NULL; + delete deleteme; + + return *this; + } + + // Removes the pointer from scoped management, but does not delete! + T *DetachPtr() + { + T *ptr = m_ptr; + m_ptr = NULL; + return ptr; + } + + // Returns the managed pointer. Can return NULL as a valid result if the ScopedPtr + // has no object in management. + T* GetPtr() const + { + return m_ptr; + } + + void SwapPtr(ScopedPtr& other) + { + T * const tmp = other.m_ptr; + other.m_ptr = m_ptr; + m_ptr = tmp; + } + + // ---------------------------------------------------------------------------- + // ScopedPtr Operators + // ---------------------------------------------------------------------------- + // I've decided to use the ATL's approach to pointer validity tests, opposed to + // the wx/boost approach (which uses some bizarre member method pointer crap, and can't + // allow the T* implicit casting. + + bool operator!() const throw() + { + return m_ptr == NULL; + } + + operator T*() const + { + return m_ptr; + } + + // Equality + bool operator==(T* pT) const throw() + { + return m_ptr == pT; + } + + // Inequality + bool operator!=(T* pT) const throw() + { + return !operator==(pT); + } + + // Convenient assignment operator. ScopedPtr = NULL will issue an automatic deletion + // of the managed pointer. + ScopedPtr& operator=( T* src ) + { + return Reassign( src ); + } + + // Dereference operator, returns a handle to the managed pointer. + // Generates a debug assertion if the object is NULL! + T& operator*() const + { + wxASSERT(m_ptr != NULL); + return *m_ptr; + } + + T* operator->() const + { + wxASSERT(m_ptr != NULL); + return m_ptr; + } +}; + + +// -------------------------------------------------------------------------------------- +// pxObjPtr -- fancified version of wxScopedPtr +// -------------------------------------------------------------------------------------- +// This class is a non-null scoped pointer container. What that means is that the object +// always resets itself to a valid "placebo" function rather than NULL, such that methods +// can be invoked safely without fear of NULL pointer exceptions. This system is useful +// for objects where most or all public methods can fail silently, and still allow program +// execution flow to continue. +// +// It also implements basic scoped pointer behavior: when the pxObjPtr class is deleted, +// it will automatically delete the pointer in its posession, if the pointer is valid. +// +// Notes: +// * This class intentionally does not implement the "release" API, because it doesn't +// really make sense within the context of a non-nullable pointer specification. +// +template< typename T, T& DefaultStaticInst > +class pxObjPtr +{ + DeclareNoncopyableObject(pxObjPtr); + +protected: + T * m_ptr; + +public: + typedef T element_type; + + explicit pxObjPtr(T * ptr = &DefaultStaticInst) : m_ptr(ptr) { } + + bool IsEmpty() const + { + return m_ptr != &DefaultStaticInst; + } + + ~pxObjPtr() + { + if( !IsEmpty() ) delete m_ptr; + m_ptr = NULL; + } + + // test for pointer validity: defining conversion to unspecified_bool_type + // and not more obvious bool to avoid implicit conversions to integer types + typedef T *(pxObjPtr::*unspecified_bool_type)() const; + + operator unspecified_bool_type() const + { + return ( !IsEmpty() ) ? &pxScopedPtr::get : NULL; + } + + void reset(T * ptr = &DefaultStaticInst) + { + if ( ptr != m_ptr ) + { + if( !IsEmpty() ) + delete m_ptr; + m_ptr = ptr; + } + } + + T& operator*() const + { + wxASSERT(m_ptr != NULL); + return *m_ptr; + } + + T* operator->() const + { + wxASSERT(m_ptr != NULL); + return m_ptr; + } + + T* get() const + { + wxASSERT(m_ptr != NULL); + return m_ptr; + } + + void swap(pxObjPtr& other) + { + // Neither pointer in either container should ever be NULL... + wxASSERT(m_ptr != NULL); + wxASSERT(other.m_ptr != NULL); + + T * const tmp = other.m_ptr; + other.m_ptr = m_ptr; + m_ptr = tmp; + } +}; + diff --git a/common/include/Utilities/Threading.h b/common/include/Utilities/Threading.h index ddb89ef1e7..befa17d964 100644 --- a/common/include/Utilities/Threading.h +++ b/common/include/Utilities/Threading.h @@ -20,6 +20,7 @@ #include #include "Pcsx2Defs.h" +#include "ScopedPtr.h" namespace Exception { @@ -87,6 +88,7 @@ namespace Threading void Lock(); void Unlock(); + bool TryLock(); }; // Returns the number of available logical CPUs (cores plus hyperthreaded cpus) @@ -101,55 +103,94 @@ namespace Threading // sleeps the current thread for the given number of milliseconds. extern void Sleep( int ms ); - ////////////////////////////////////////////////////////////////////////////////////////// - // PersistentThread - Helper class for the basics of starting/managing persistent threads. - // - // Use this as a base class for your threaded procedure, and implement the 'int ExecuteTask()' - // method. Use Start() and Cancel() to start and shutdown the thread, and use m_sem_event - // internally to post/receive events for the thread (make a public accessor for it in your - // derived class if your thread utilizes the post). - // - // Notes: - // * Constructing threads as static global vars isn't recommended since it can potentially - // confuse w32pthreads, if the static initializers are executed out-of-order (C++ offers - // no dependency options for ensuring correct static var initializations). Use heap - // allocation to create thread objects instead. - // - class PersistentThread +// -------------------------------------------------------------------------------------- +// IThread - Interface for the public access to PersistentThread. +// -------------------------------------------------------------------------------------- +// Class usage: Can be used for allowing safe nullification of a thread handle. Rather +// than being NULL'd, the handle can be mapped to an IThread implementation which acts +// as a do-nothing placebo or an assertion generator. +// + class IThread + { + DeclareNoncopyableObject(IThread); + + public: + IThread() {} + virtual ~IThread() throw() {} + + virtual bool IsSelf() const { return false; } + virtual bool IsRunning() { return false; } + virtual int GetReturnCode() const + { + DevAssert( false, "Cannot obtain a return code from a placebo thread." ); + return 0; + } + + virtual void Start() {} + virtual void Cancel( bool isBlocking = true ) {} + virtual sptr Block() { return NULL; } + virtual bool Detach() { return false; } + }; + +// -------------------------------------------------------------------------------------- +// PersistentThread - Helper class for the basics of starting/managing persistent threads. +// -------------------------------------------------------------------------------------- +// Use this as a base class for your threaded procedure, and implement the 'int ExecuteTask()' +// method. Use Start() and Cancel() to start and shutdown the thread, and use m_sem_event +// internally to post/receive events for the thread (make a public accessor for it in your +// derived class if your thread utilizes the post). +// +// Notes: +// * Constructing threads as static global vars isn't recommended since it can potentially +// confuse w32pthreads, if the static initializers are executed out-of-order (C++ offers +// no dependency options for ensuring correct static var initializations). Use heap +// allocation to create thread objects instead. +// + class PersistentThread : public virtual IThread { DeclareNoncopyableObject(PersistentThread); protected: typedef int (*PlainJoeFP)(); + + wxString m_name; // diagnostic name for our thread. + pthread_t m_thread; Semaphore m_sem_event; // general wait event that's needed by most threads. Semaphore m_sem_finished; // used for canceling and closing threads in a deadlock-safe manner + MutexLock m_lock_start; // used to lock the Start() code from starting simutaneous threads accidentally. sptr m_returncode; // value returned from the thread on close. - + volatile long m_detached; // a boolean value which indicates if the m_thread handle is valid volatile long m_running; // set true by Start(), and set false by Cancel(), Block(), etc. + + // exception handle, set non-NULL if the thread terminated with an exception + // Use RethrowException() to re-throw the exception using its original exception type. + ScopedPtr m_except; public: virtual ~PersistentThread() throw(); PersistentThread(); + PersistentThread( const char* name ); virtual void Start(); virtual void Cancel( bool isBlocking = true ); - virtual void Detach(); - - // Gets the return code of the thread. - // Throws std::logic_error if the thread has not terminated. - virtual int GetReturnCode() const; - - virtual bool IsRunning() const; + virtual bool Detach(); virtual sptr Block(); - + + virtual int GetReturnCode() const; + virtual void RethrowException() const; + + bool IsRunning() const; bool IsSelf() const; + wxString GetName() const; virtual void DoThreadCleanup(); protected: - void SetName( __unused const char* name ); + void DoSetThreadName( const wxString& name ); + void DoSetThreadName( __unused const char* name ); + void _internal_execute(); // Used to dispatch the thread callback function. // (handles some thread cleanup on Win32, and is basically a typecast @@ -159,7 +200,7 @@ namespace Threading // Implemented by derived class to handle threading actions! virtual sptr ExecuteTask()=0; }; - + ////////////////////////////////////////////////////////////////////////////////////////// // ScopedLock: Helper class for using Mutexes. // Using this class provides an exception-safe (and generally clean) method of locking diff --git a/common/include/Utilities/wxBaseTools.h b/common/include/Utilities/wxBaseTools.h index 471db349eb..3cd62cbed4 100644 --- a/common/include/Utilities/wxBaseTools.h +++ b/common/include/Utilities/wxBaseTools.h @@ -17,17 +17,19 @@ #include "Dependencies.h" -// ---------------------------------------------------------------------------- +// -------------------------------------------------------------------------------------- // wxBaseTools.h // // This file is meant to contain utility classes for users of the wxWidgets library. // All classes in this file are strictly dependent on wxBase libraries only, meaning // you don't need to include or link against wxCore (GUI) to build them. For tools // which require wxCore, see wxGuiTools.h -// ---------------------------------------------------------------------------- +// -------------------------------------------------------------------------------------- -////////////////////////////////////////////////////////////////////////////////////////// -// wxDoNotLogInThisScope + +// -------------------------------------------------------------------------------------- +// wxDoNotLogInThisScope +// -------------------------------------------------------------------------------------- // This class is used to disable wx's sometimes inappropriate amount of forced error logging // during specific activities. For example, when using wxDynamicLibrary to detect the // validity of DLLs, wx will log errors for missing symbols. (sigh) @@ -54,3 +56,35 @@ public: } }; +// -------------------------------------------------------------------------------------- +// wxToUTF8 - shortcut for str.ToUTF8().data() +// -------------------------------------------------------------------------------------- +class wxToUTF8 +{ + DeclareNoncopyableObject( wxToUTF8 ); + +protected: + wxCharBuffer m_charbuffer; + +public: + wxToUTF8( const wxString& str ) : m_charbuffer( str.ToUTF8().data() ) { } + virtual ~wxToUTF8() throw() {} + + operator const char*() { return m_charbuffer.data(); } +}; + +// This class provided for completeness sake. You probably should use ToUTF8 instead. +class wxToAscii +{ + DeclareNoncopyableObject( wxToAscii ); + +protected: + wxCharBuffer m_charbuffer; + +public: + wxToAscii( const wxString& str ) : m_charbuffer( str.ToAscii().data() ) { } + virtual ~wxToAscii() throw() {} + + operator const char*() { return m_charbuffer.data(); } +}; + diff --git a/common/src/Utilities/ThreadTools.cpp b/common/src/Utilities/ThreadTools.cpp index 25882a52df..bc8ffefc50 100644 --- a/common/src/Utilities/ThreadTools.cpp +++ b/common/src/Utilities/ThreadTools.cpp @@ -16,6 +16,7 @@ #include "PrecompiledHeader.h" #include "Threading.h" +#include "wxBaseTools.h" #include #include @@ -37,11 +38,13 @@ namespace Threading } PersistentThread::PersistentThread() : - m_thread() + m_name( L"PersistentThread" ) + , m_thread() , m_sem_event() , m_sem_finished() + , m_lock_start( true ) // recursive mutexing! , m_returncode( 0 ) - , m_detached( false ) + , m_detached( true ) // start out with m_thread in detached/invalid state , m_running( false ) { } @@ -53,40 +56,42 @@ namespace Threading // your sister, and then cheating on her with your daughter. PersistentThread::~PersistentThread() throw() { - if( !m_running ) return; - - wxASSERT( !IsSelf() ); // not allowed from our own thread. - - if( !_InterlockedExchange( &m_detached, true ) ) + if( m_running ) { #if wxUSE_GUI m_sem_finished.WaitGui(); #else m_sem_finished.Wait(); #endif - m_running = false; } + + Detach(); } // This function should not be called from the owner thread. void PersistentThread::Start() { + ScopedLock startlock( m_lock_start ); // Prevents sudden parallel startup if( m_running ) return; + + Detach(); // clean up previous thread, if one exists. m_sem_finished.Reset(); if( pthread_create( &m_thread, NULL, _internal_callback, this ) != 0 ) throw Exception::ThreadCreationError(); - m_running = true; + m_detached = false; } + // Returns: TRUE if the detachment was performed, or FALSE if the thread was + // already detached or isn't running at all. // This function should not be called from the owner thread. - void PersistentThread::Detach() + bool PersistentThread::Detach() { - if( !m_running ) return; - if( _InterlockedExchange( &m_detached, true ) ) return; - wxASSERT( !IsSelf() ); // not allowed from our own thread. + + if( _InterlockedExchange( &m_detached, true ) ) return false; pthread_detach( m_thread ); + return true; } // Remarks: @@ -102,15 +107,15 @@ namespace Threading // void PersistentThread::Cancel( bool isBlocking ) { + wxASSERT( !IsSelf() ); if( !m_running ) return; - if( _InterlockedExchange( &m_detached, true ) ) + if( m_detached ) { Console::Notice( "Threading Warning: Attempted to cancel detached thread; Ignoring..." ); return; } - wxASSERT( !IsSelf() ); pthread_cancel( m_thread ); if( isBlocking ) @@ -121,8 +126,6 @@ namespace Threading m_sem_finished.Wait(); #endif } - else - pthread_detach( m_thread ); } // Blocks execution of the calling thread until this thread completes its task. The @@ -153,14 +156,10 @@ namespace Threading bool PersistentThread::IsRunning() const { - if (!m_running) return false; - - if( !!m_detached ) - return !!m_running; - else - return ( ESRCH != pthread_kill( m_thread, 0 ) ); + return !!m_running; } + // Gets the return code of the thread. // Exceptions: // InvalidOperation - thrown if the thread is still running or has never been started. // @@ -171,32 +170,95 @@ namespace Threading return m_returncode; } + + // Throws an exception if the thread encountered one. Uses the BaseException's Rethrow() method, + // which ensures the exception type remains consistent. Debuggable stacktraces will be lost, since + // the thread will have allowed itself to terminate properly. + void PersistentThread::RethrowException() const + { + if( !m_except ) return; + m_except->Rethrow(); + } // invoked when canceling or exiting the thread. void PersistentThread::DoThreadCleanup() { wxASSERT( IsSelf() ); // only allowed from our own thread, thanks. - _InterlockedExchange( &m_running, false ); + m_running = false; m_sem_finished.Post(); } + wxString PersistentThread::GetName() const + { + return m_name; + } + + void PersistentThread::_internal_execute() + { + m_running = true; + DoSetThreadName( m_name ); + + try { + m_returncode = ExecuteTask(); + } + catch( std::logic_error& ex ) + { + throw Exception::LogicError( wxsFormat( L"(thread: %s) STL Logic Error: %s\n\t%s", + GetName().c_str(), wxString::FromUTF8( ex.what() ) ) + ); + } + catch( Exception::LogicError& ex ) + { + m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName() ) + m_except->DiagMsg(); + ex.Rethrow(); + } + catch( std::runtime_error& ex ) + { + m_except = new Exception::RuntimeError( + // Diagnostic message: + wxsFormat( L"(thread: %s) STL Runtime Error: %s\n\t%s", + GetName().c_str(), wxString::FromUTF8( ex.what() ) + ), + + // User Message (not translated, std::exception doesn't have that kind of fancy! + wxsFormat( L"A runtime error occurred in %s:\n\n%s (STL)", + GetName().c_str(), wxString::FromUTF8( ex.what() ) + ) + ); + } + catch( Exception::RuntimeError& ex ) + { + m_except = ex.Clone(); + m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName() ) + m_except->DiagMsg(); + } + } + void* PersistentThread::_internal_callback( void* itsme ) { jASSUME( itsme != NULL ); PersistentThread& owner = *((PersistentThread*)itsme); pthread_cleanup_push( _pt_callback_cleanup, itsme ); - owner.m_returncode = owner.ExecuteTask(); + owner._internal_execute(); pthread_cleanup_pop( true ); - return (void*)owner.m_returncode; } + + void PersistentThread::DoSetThreadName( const wxString& name ) + { + DoSetThreadName( wxToUTF8(name) ); + } - void PersistentThread::SetName( __unused const char* name ) + void PersistentThread::DoSetThreadName( __unused const char* name ) { wxASSERT( IsSelf() ); // only allowed from our own thread, thanks. #ifdef _WINDOWS_ + + // This code sample was borrowed form some obscure MSDN article. + // In a rare bout of sanity, it's an actual Micrsoft-published hack + // that actually works! + static const int MS_VC_EXCEPTION = 0x406D1388; #pragma pack(push,8) @@ -210,10 +272,10 @@ namespace Threading #pragma pack(pop) THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = name; - info.dwThreadID = GetCurrentThreadId(); - info.dwFlags = 0; + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = GetCurrentThreadId(); + info.dwFlags = 0; __try { @@ -480,6 +542,11 @@ namespace Threading pthread_mutex_unlock( &mutex ); } + bool MutexLock::TryLock() + { + return EBUSY != pthread_mutex_trylock( &mutex ); + } + // -------------------------------------------------------------------------------------- // InterlockedExchanges / AtomicExchanges (PCSX2's Helper versions) // -------------------------------------------------------------------------------------- diff --git a/common/src/x86emitter/x86emitter.cpp b/common/src/x86emitter/x86emitter.cpp index 60df554030..38883c4fdb 100644 --- a/common/src/x86emitter/x86emitter.cpp +++ b/common/src/x86emitter/x86emitter.cpp @@ -85,12 +85,12 @@ __forceinline void xWrite64( u64 val ) xWrite( val ); } -const xAddressIndexerBase ptr; -const xAddressIndexer ptr128; -const xAddressIndexer ptr64; -const xAddressIndexer ptr32; -const xAddressIndexer ptr16; -const xAddressIndexer ptr8; +const xAddressIndexerBase ptr; +const xAddressIndexer ptr128; +const xAddressIndexer ptr64; +const xAddressIndexer ptr32; +const xAddressIndexer ptr16; +const xAddressIndexer ptr8; // ------------------------------------------------------------------------ diff --git a/pcsx2/GSState.cpp b/pcsx2/GSState.cpp index e4be0e704a..c7f9451ec2 100644 --- a/pcsx2/GSState.cpp +++ b/pcsx2/GSState.cpp @@ -23,7 +23,7 @@ // GS Playback int g_SaveGSStream = 0; // save GS stream; 1 - prepare, 2 - save int g_nLeftGSFrames = 0; // when saving, number of frames left -static wxScopedPtr g_fGSSave; +static ScopedPtr g_fGSSave; // fixme - need to take this concept and make it MTGS friendly. #ifdef _STGS_GSSTATE_CODE @@ -76,7 +76,7 @@ void SaveGSState(const wxString& file) Console::WriteLn( wxsFormat( L"\t%s", file.c_str() ) ); SafeArray buf; - g_fGSSave.reset( new memSavingState( buf ) ); + g_fGSSave = new memSavingState( buf ); g_SaveGSStream = 1; g_nLeftGSFrames = 2; diff --git a/pcsx2/MTGS.cpp b/pcsx2/MTGS.cpp index c5dee24273..c75553d3b4 100644 --- a/pcsx2/MTGS.cpp +++ b/pcsx2/MTGS.cpp @@ -107,6 +107,7 @@ mtgsThreadObject::mtgsThreadObject() : , m_lock_Stack() #endif { + m_name = L"MTGS"; } void mtgsThreadObject::Start() @@ -139,6 +140,7 @@ void mtgsThreadObject::PollStatus() mtgsThreadObject::~mtgsThreadObject() throw() { + _parent::Cancel(); } void mtgsThreadObject::OnResumeReady() @@ -244,8 +246,6 @@ void mtgsThreadObject::OpenPlugin() sptr mtgsThreadObject::ExecuteTask() { - SetName( "MTGS" ); - #ifdef RINGBUF_DEBUG_STACK PacketTagType prevCmd; #endif diff --git a/pcsx2/PluginManager.cpp b/pcsx2/PluginManager.cpp index 08aa58ea3b..5d42a3caaa 100644 --- a/pcsx2/PluginManager.cpp +++ b/pcsx2/PluginManager.cpp @@ -28,29 +28,29 @@ # include "svnrev.h" #endif -EmuPluginBindings EmuPlugins; +SysPluginBindings SysPlugins; -bool EmuPluginBindings::McdIsPresent( uint port, uint slot ) +bool SysPluginBindings::McdIsPresent( uint port, uint slot ) { return !!Mcd->McdIsPresent( (PS2E_THISPTR) Mcd, port, slot ); } -void EmuPluginBindings::McdRead( uint port, uint slot, u8 *dest, u32 adr, int size ) +void SysPluginBindings::McdRead( uint port, uint slot, u8 *dest, u32 adr, int size ) { Mcd->McdRead( (PS2E_THISPTR) Mcd, port, slot, dest, adr, size ); } -void EmuPluginBindings::McdSave( uint port, uint slot, const u8 *src, u32 adr, int size ) +void SysPluginBindings::McdSave( uint port, uint slot, const u8 *src, u32 adr, int size ) { Mcd->McdSave( (PS2E_THISPTR) Mcd, port, slot, src, adr, size ); } -void EmuPluginBindings::McdEraseBlock( uint port, uint slot, u32 adr ) +void SysPluginBindings::McdEraseBlock( uint port, uint slot, u32 adr ) { Mcd->McdEraseBlock( (PS2E_THISPTR) Mcd, port, slot, adr ); } -u64 EmuPluginBindings::McdGetCRC( uint port, uint slot ) +u64 SysPluginBindings::McdGetCRC( uint port, uint slot ) { return Mcd->McdGetCRC( (PS2E_THISPTR) Mcd, port, slot ); } @@ -988,10 +988,10 @@ void PluginManager::Init() throw Exception::PluginInitError( pid ); } while( ++pi, pi->shortname != NULL ); - if( EmuPlugins.Mcd == NULL ) + if( SysPlugins.Mcd == NULL ) { - EmuPlugins.Mcd = (PS2E_ComponentAPI_Mcd*)m_mcdPlugin->NewComponentInstance( PS2E_TYPE_Mcd ); - if( EmuPlugins.Mcd == NULL ) + SysPlugins.Mcd = (PS2E_ComponentAPI_Mcd*)m_mcdPlugin->NewComponentInstance( PS2E_TYPE_Mcd ); + if( SysPlugins.Mcd == NULL ) { // fixme: use plugin's GetLastError (not implemented yet!) throw Exception::PluginInitError( PluginId_Mcd, "Internal Memorycard Plugin failed to initialize." ); @@ -1029,10 +1029,10 @@ void PluginManager::Shutdown() // More memorycard hacks!! - if( (EmuPlugins.Mcd != NULL) && (m_mcdPlugin != NULL) ) + if( (SysPlugins.Mcd != NULL) && (m_mcdPlugin != NULL) ) { - m_mcdPlugin->DeleteComponentInstance( (PS2E_THISPTR)EmuPlugins.Mcd ); - EmuPlugins.Mcd = NULL; + m_mcdPlugin->DeleteComponentInstance( (PS2E_THISPTR)SysPlugins.Mcd ); + SysPlugins.Mcd = NULL; } DbgCon::Status( "Plugins shutdown successfully." ); diff --git a/pcsx2/Plugins.h b/pcsx2/Plugins.h index 06d8f50226..0e3e06edcf 100644 --- a/pcsx2/Plugins.h +++ b/pcsx2/Plugins.h @@ -48,6 +48,7 @@ namespace Exception public: DEFINE_EXCEPTION_COPYTORS( PluginError ) + PluginError() {} PluginError( PluginsEnum_t pid, const char* msg="Generic plugin error" ) { @@ -183,13 +184,13 @@ class mtgsThreadObject; // make the current PluginManager largely obsolete (with the exception of the general Load/Unload // management facilities) // -class EmuPluginBindings +class SysPluginBindings { protected: PS2E_ComponentAPI_Mcd* Mcd; public: - EmuPluginBindings() : + SysPluginBindings() : Mcd( NULL ) { @@ -204,7 +205,7 @@ public: friend class PluginManager; }; -extern EmuPluginBindings EmuPlugins; +extern SysPluginBindings SysPlugins; // -------------------------------------------------------------------------------------- diff --git a/pcsx2/RecoverySystem.cpp b/pcsx2/RecoverySystem.cpp index f5428d3409..2aeb46da7d 100644 --- a/pcsx2/RecoverySystem.cpp +++ b/pcsx2/RecoverySystem.cpp @@ -20,129 +20,268 @@ #include "HostGui.h" #include "zlib/zlib.h" -using namespace Threading; +static SafeArray state_buffer; -static wxScopedPtr< SafeArray > g_RecoveryState; +// Simple lock boolean for the state buffer in use by a thread. This simple solution works because +// we are assured that state save/load actions will only be initiated from the main thread. +static bool state_buffer_lock = false; -namespace StateRecovery { +// This boolean is to keep the system from resuming emulation until the current state has completely +// uploaded or downloaded itself. It is only modified from the main thread, and should only be read +// form the main thread. +bool sys_resume_lock = false; - bool HasState() +class StateThread_Freeze : public PersistentThread +{ + typedef PersistentThread _parent; + +public: + StateThread_Freeze( const wxString& file ) { - return g_RecoveryState; - } + m_name = L"SaveState::CopyAndZip"; - void Recover() - { - if( !g_RecoveryState ) return; + DevAssert( wxThread::IsMain(), "StateThread creation is allowed from the Main thread only." ); + if( state_buffer_lock ) + throw Exception::RuntimeError( "Cannot save state; a previous save or load action is already in progress." ); - Console::Status( "Resuming execution from full memory state..." ); - memLoadingState( *g_RecoveryState ).FreezeAll(); - - StateRecovery::Clear(); - SysClearExecutionCache(); + Start(); + sys_resume_lock = true; } - SafeArray gzSavingBuffer; - - class gzThreadClass : public PersistentThread +protected: + sptr ExecuteTask() { - typedef PersistentThread _parent; - - protected: - gzFile m_file; - - public: - gzThreadClass( const wxString& file ) : - m_file( gzopen( file.ToUTF8().data(), "wb" ) ) - { - if( m_file == NULL ) - throw Exception::CreateStream( file, "Cannot create savestate file for writing." ); - - Start(); - } - - virtual void DoThreadCleanup() - { - gzSavingBuffer.Dispose(); - if( m_file != NULL ) - { - gzclose( m_file ); - m_file = NULL; - } - - _parent::DoThreadCleanup(); - } - - virtual ~gzThreadClass() throw() - { - // fixme: something a little more graceful than Block, perhaps? - Block(); - } - - protected: - int ExecuteTask() - { - if( (m_file == NULL) || (gzSavingBuffer.GetSizeInBytes() == 0) ) return 0 ; - SetName( "Savestate::gzipper" ); - gzwrite( m_file, gzSavingBuffer.GetPtr(), gzSavingBuffer.GetSizeInBytes() ); - - return 0; - } - }; - - wxScopedPtr gzThread; - - void SaveToFile( const wxString& file ) + memSavingState( state_buffer ).FreezeAll(); + return 0; + } + + void DoThreadCleanup() { - SysSuspend( false ); - gzThread.reset( NULL ); // blocks on any existing gzipping business. + wxCommandEvent evt( pxEVT_FreezeFinished ); + evt.SetClientData( this ); + wxGetApp().AddPendingEvent( evt ); - memSavingState( gzSavingBuffer ).FreezeAll(); + _parent::DoThreadCleanup(); + } +}; - // start that encoding thread: - gzThread.reset( new gzThreadClass( file ) ); - - SysResume(); +class StateThread_ZipToDisk : public PersistentThread +{ + typedef PersistentThread _parent; + +protected: + gzFile m_gzfp; + +public: + StateThread_ZipToDisk( const wxString& file ) : m_gzfp( NULL ) + { + m_name = L"SaveState::ZipToDisk"; + + DevAssert( wxThread::IsMain(), "StateThread creation is allowed from the Main thread only." ); + if( state_buffer_lock ) + throw Exception::RuntimeError( "Cannot save state; a previous save or load action is already in progress." ); + + m_gzfp = gzopen( file.ToUTF8().data(), "wb" ); + if( m_gzfp == NULL ) + throw Exception::CreateStream( file, "Cannot create savestate file for writing." ); + + try{ Start(); } + catch(...) + { + gzclose( m_gzfp ); m_gzfp = NULL; + throw; + } + sys_resume_lock = true; } - // 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 SaveToSlot( uint num ) + ~StateThread_ZipToDisk() throw() { - SaveToFile( SaveStateBase::GetFilename( num ) ); + sys_resume_lock = false; // just in case; + if( m_gzfp != NULL ) gzclose( m_gzfp ); } - // Creates a full recovery of the entire emulation state (CPU and all plugins). - // If a current recovery state is already present, then nothing is done (the - // existing recovery state takes precedence since if it were out-of-date it'd be - // deleted!). - void MakeFull() +protected: + sptr ExecuteTask() { - if( g_RecoveryState ) return; - if( !EmulationInProgress() ) return; + Sleep( 2 ); + if( gzwrite( (gzFile)m_gzfp, state_buffer.GetPtr(), state_buffer.GetSizeInBytes() ) < state_buffer.GetSizeInBytes() ) + throw Exception::BadStream(); - try + return 0; + } + + void DoThreadCleanup() + { + wxCommandEvent evt( pxEVT_FreezeFinished ); + evt.SetClientData( this ); // tells message to clean us up. + wxGetApp().AddPendingEvent( evt ); + + _parent::DoThreadCleanup(); + } +}; + + +class StateThread_UnzipFromDisk : public PersistentThread +{ + typedef PersistentThread _parent; + +protected: + gzFile m_gzfp; + +public: + StateThread_UnzipFromDisk( const wxString& file ) : m_gzfp( NULL ) + { + m_name = L"SaveState::UnzipFromDisk"; + + DevAssert( wxThread::IsMain(), "StateThread creation is allowed from the Main thread only." ); + if( state_buffer_lock ) + throw Exception::RuntimeError( "Cannot save state; a previous save or load action is already in progress." ); + + m_gzfp = gzopen( file.ToUTF8().data(), "wb" ); + if( m_gzfp == NULL ) + throw Exception::CreateStream( file, "Cannot create savestate file for writing." ); + + try{ Start(); } + catch(...) { - g_RecoveryState.reset( new SafeArray( L"Memory Savestate Recovery" ) ); - memSavingState( *g_RecoveryState ).FreezeAll(); - } - catch( Exception::RuntimeError& ex ) - { - Msgbox::Alert( wxsFormat( // fixme: needs proper translation - L"PCSX2 encountered an error while trying to backup/suspend the PS2 VirtualMachine state. " - L"You may resume emulation without losing any data, however the machine state will not be " - L"able to recover if you make changes to your PCSX2 configuration.\n\n" - L"Details: %s", ex.FormatDisplayMessage().c_str() ) - ); - g_RecoveryState.reset(); + gzclose( m_gzfp ); m_gzfp = NULL; + throw; } + sys_resume_lock = true; } - // Clears and deallocates any recovery states. - void Clear() + ~StateThread_UnzipFromDisk() throw() { - g_RecoveryState.reset(); + sys_resume_lock = false; // just in case; + if( m_gzfp != NULL ) gzclose( m_gzfp ); + } + +protected: + sptr ExecuteTask() + { + // fixme: should start initially with the file size, and then grow from there. + + static const int BlockSize = 327680; + int curidx = 0; + do + { + state_buffer.ExactAlloc( curidx+BlockSize ); + gzread( m_gzfp, state_buffer.GetPtr(curidx), BlockSize ); + curidx += BlockSize; + } while( !gzeof(m_gzfp) ); + } + + void DoThreadCleanup() + { + wxCommandEvent evt( pxEVT_ThawFinished ); + evt.SetClientData( this ); // tells message to clean us up. + wxGetApp().AddPendingEvent( evt ); + + _parent::DoThreadCleanup(); + } +}; + +void Pcsx2App::OnFreezeFinished( wxCommandEvent& evt ) +{ + state_buffer.Dispose(); + state_buffer_lock = false; + + SysClearExecutionCache(); + SysResume(); + + if( PersistentThread* thread = (PersistentThread*)evt.GetClientData() ) + { + delete thread; } } + +void Pcsx2App::OnThawFinished( wxCommandEvent& evt ) +{ + state_buffer.Dispose(); + state_buffer_lock = false; + + SysClearExecutionCache(); + SysResume(); + + if( PersistentThread* thread = (PersistentThread*)evt.GetClientData() ) + { + delete thread; + } +} + +void StateCopy_SaveToFile( const wxString& file ) +{ + if( state_buffer_lock ) return; + // [TODO] Implement optional 7zip compression here? +} + +// 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 ) +{ + if( state_buffer_lock ) return; + StateCopy_SaveToFile( SaveStateBase::GetFilename( num ) ); +} + +bool StateCopy_IsValid() +{ + return !state_buffer.IsDisposed(); +} + +bool StateCopy_HasFullState() +{ + return false; +} + +bool StateCopy_HasPartialState() +{ + return false; +} + +void StateCopy_FreezeToMem() +{ + if( state_buffer_lock ) return; +} + +void StateCopy_ThawFromMem() +{ + if( state_buffer_lock ) return; +} + +void StateCopy_Clear() +{ + if( state_buffer_lock ) return; + state_buffer.Dispose(); +} + + + +// Creates a full recovery of the entire emulation state (CPU and all plugins). +// If a current recovery state is already present, then nothing is done (the +// existing recovery state takes precedence since if it were out-of-date it'd be +// deleted!). +void MakeFull() +{ + //if( g_RecoveryState ) return; + //if( !SysHasValidState() ) return; + + /* + try + { + g_RecoveryState = new SafeArray( L"Memory Savestate Recovery" ); + memSavingState( *g_RecoveryState ).FreezeAll(); + } + catch( Exception::RuntimeError& ex ) + { + Msgbox::Alert( wxsFormat( // fixme: needs proper translation + L"PCSX2 encountered an error while trying to backup/suspend the PS2 VirtualMachine state. " + L"You may resume emulation without losing any data, however the machine state will not be " + L"able to recover if you make changes to your PCSX2 configuration.\n\n" + L"Details: %s", ex.FormatDisplayMessage().c_str() ) + ); + g_RecoveryState = NULL; + }*/ +} + diff --git a/pcsx2/SaveState.h b/pcsx2/SaveState.h index ba7c74855d..a202999a4d 100644 --- a/pcsx2/SaveState.h +++ b/pcsx2/SaveState.h @@ -193,13 +193,13 @@ public: bool IsFinished() const { return m_idx >= m_memory.GetSizeInBytes(); } }; -namespace StateRecovery -{ - extern bool HasState(); - extern void Recover(); - extern void SaveToFile( const wxString& file ); - extern void SaveToSlot( uint num ); - extern void MakeFull(); - extern void Clear(); -} +extern bool StateCopy_IsValid(); +extern bool StateCopy_HasFullState(); +extern bool StateCopy_HasPartialState(); + +extern void StateCopy_FreezeToMem(); +extern void StateCopy_ThawFromMem(); +extern void StateCopy_SaveToFile( const wxString& file ); +extern void StateCopy_SaveToSlot( uint num ); +extern void StateCopy_Clear(); diff --git a/pcsx2/Sio.cpp b/pcsx2/Sio.cpp index ecc1e5037b..848e35e92f 100644 --- a/pcsx2/Sio.cpp +++ b/pcsx2/Sio.cpp @@ -53,7 +53,7 @@ static bool IsMtapPresent( uint port ) static void _ReadMcd(u8 *data, u32 adr, int size) { - EmuPlugins.McdRead( + SysPlugins.McdRead( sio.GetMemcardIndex(), sio.activeMemcardSlot[sio.GetMemcardIndex()], data, adr, size ); @@ -61,7 +61,7 @@ static void _ReadMcd(u8 *data, u32 adr, int size) static void _SaveMcd(const u8 *data, u32 adr, int size) { - EmuPlugins.McdSave( + SysPlugins.McdSave( sio.GetMemcardIndex(), sio.activeMemcardSlot[sio.GetMemcardIndex()], data, adr, size ); @@ -69,7 +69,7 @@ static void _SaveMcd(const u8 *data, u32 adr, int size) static void _EraseMCDBlock(u32 adr) { - EmuPlugins.McdEraseBlock( sio.GetMemcardIndex(), sio.activeMemcardSlot[sio.GetMemcardIndex()], adr ); + SysPlugins.McdEraseBlock( sio.GetMemcardIndex(), sio.activeMemcardSlot[sio.GetMemcardIndex()], adr ); } static u8 sio_xor( const u8 *buf, uint length ) @@ -601,7 +601,7 @@ void InitializeSIO(u8 value) const uint port = sio.GetMemcardIndex(); const uint slot = sio.activeMemcardSlot[port]; - if( EmuPlugins.McdIsPresent( port, slot ) ) + if( SysPlugins.McdIsPresent( port, slot ) ) { sio2.packet.recvVal1 = 0x1100; PAD_LOG("START MEMCARD [port:%d, slot:%d] - Present", port, slot ); @@ -665,7 +665,7 @@ void SaveStateBase::sioFreeze() for( int port=0; port<2; ++port ) { for( int slot=0; slot<8; ++slot ) - m_mcdCRCs[port][slot] = EmuPlugins.McdGetCRC( port, slot ); + m_mcdCRCs[port][slot] = SysPlugins.McdGetCRC( port, slot ); } } Freeze( m_mcdCRCs ); @@ -689,7 +689,7 @@ void SaveStateBase::sioFreeze() { for( int slot=0; slot<8; ++slot ) { - u64 newCRC = EmuPlugins.McdGetCRC( port, slot ); + u64 newCRC = SysPlugins.McdGetCRC( port, slot ); if( newCRC != m_mcdCRCs[port][slot] ) { m_mcdCRCs[port][slot] = newCRC; diff --git a/pcsx2/System.cpp b/pcsx2/System.cpp index 7bd4c093dd..e274bd2eba 100644 --- a/pcsx2/System.cpp +++ b/pcsx2/System.cpp @@ -252,40 +252,11 @@ void SysClearExecutionCache() vuMicroCpuReset(); } -// The calling function should trap and handle exceptions as needed. -// Exceptions: -// Exception::StateLoadError - thrown when a fully recoverable exception ocurred. The -// virtual machine memory state is fully intact. -// -// Any other exception means the Virtual Memory state is indeterminate and probably -// invalid. void SysLoadState( const wxString& srcfile ) { - SafeArray buf; - gzFile gzfp = gzopen( srcfile.ToUTF8().data(), "rb" ); - if( gzfp == NULL ) - throw Exception::BadSavedState( srcfile, "File not found, or permission denied!" ); - - int curidx = 0; - do - { - buf.MakeRoomFor( curidx+327680 ); - gzread( gzfp, buf.GetPtr(curidx), 327680 ); - curidx += 327680; - } while( !gzeof(gzfp) ); - - gzclose( gzfp ); - - memLoadingState joe( buf ); // this could throw n StateLoadError. - - // we perform a full backup to memory first so that we can restore later if the - // load fails. fixme: should this be made optional? It could have significant - // speed impact on state loads on slower machines with low ram. >_< - StateRecovery::MakeFull(); - - SysClearExecutionCache(); - cpuReset(); - joe.FreezeAll(); + //SysClearExecutionCache(); + //cpuReset(); + //joe.FreezeAll(); } // Maps a block of memory for use as a recompiled code buffer, and ensures that the diff --git a/pcsx2/System/SysThreads.cpp b/pcsx2/System/SysThreads.cpp index 437d7299ad..99690938e1 100644 --- a/pcsx2/System/SysThreads.cpp +++ b/pcsx2/System/SysThreads.cpp @@ -204,6 +204,7 @@ SysCoreThread::SysCoreThread( PluginManager& plugins ) : , m_shortSuspend( false ) , m_plugins( plugins ) { + m_name = L"EE Core"; } SysCoreThread::~SysCoreThread() throw() @@ -280,13 +281,13 @@ void SysCoreThread::CpuInitializeMess() cpuReset(); SysClearExecutionCache(); - if( StateRecovery::HasState() ) + if( StateCopy_IsValid() ) { // no need to boot bios or detect CDs when loading savestates. // [TODO] : It might be useful to detect game SLUS/CRC and compare it against // the savestate info, and issue a warning to the user since, chances are, they // don't really want to run a game with the wrong ISO loaded into the emu. - StateRecovery::Recover(); + StateCopy_ThawFromMem(); } else { @@ -354,7 +355,6 @@ static void _cet_callback_cleanup( void* handle ) sptr SysCoreThread::ExecuteTask() { - SetName( "EE Core" ); tls_coreThread = this; StateCheck(); diff --git a/pcsx2/System/SysThreads.h b/pcsx2/System/SysThreads.h index 6eb82fff18..fbd1e600aa 100644 --- a/pcsx2/System/SysThreads.h +++ b/pcsx2/System/SysThreads.h @@ -19,7 +19,19 @@ using namespace Threading; -class SysSuspendableThread : public PersistentThread +class ISysThread : public virtual IThread +{ +public: + ISysThread() {} + virtual ~ISysThread() throw() {}; + + virtual bool IsSuspended() const { return false; } + virtual void Suspend( bool isBlocking = true ) { } + virtual void Resume() {} +}; + + +class SysSuspendableThread : public PersistentThread, public virtual ISysThread { typedef PersistentThread _parent; diff --git a/pcsx2/gui/App.h b/pcsx2/gui/App.h index 1ea20d680a..533efe1a98 100644 --- a/pcsx2/gui/App.h +++ b/pcsx2/gui/App.h @@ -26,6 +26,7 @@ class MainEmuFrame; class GSFrame; class ConsoleLogFrame; class PipeRedirectionBase; +class AppCoreThread; #include "Utilities/HashMap.h" #include "Utilities/wxGuiTools.h" @@ -39,6 +40,9 @@ BEGIN_DECLARE_EVENT_TYPES() DECLARE_EVENT_TYPE( pxEVT_OpenModalDialog, -1 ) DECLARE_EVENT_TYPE( pxEVT_ReloadPlugins, -1 ) DECLARE_EVENT_TYPE( pxEVT_LoadPluginsComplete, -1 ) + DECLARE_EVENT_TYPE( pxEVT_AppCoreThread_Terminated, -1 ) + DECLARE_EVENT_TYPE( pxEVT_FreezeFinished, -1 ) + DECLARE_EVENT_TYPE( pxEVT_ThawFinished, -1 ) END_DECLARE_EVENT_TYPES() // ------------------------------------------------------------------------ @@ -73,11 +77,11 @@ enum MenuIdentifiers MenuId_SkipBiosToggle, // enables the Bios Skip speedhack - MenuId_Emu_SuspendResume, // suspends/resumes active emulation, retains plugin states - MenuId_Emu_Close, // Closes the emulator (states are preserved) - MenuId_Emu_Reset, // Issues a complete reset (wipes preserved states) - MenuId_Emu_LoadStates, // Opens load states submenu - MenuId_Emu_SaveStates, // Opens save states submenu + MenuId_Sys_SuspendResume, // suspends/resumes active emulation, retains plugin states + MenuId_Sys_Close, // Closes the emulator (states are preserved) + MenuId_Sys_Reset, // Issues a complete reset (wipes preserved states) + MenuId_Sys_LoadStates, // Opens load states submenu + MenuId_Sys_SaveStates, // Opens save states submenu MenuId_EnablePatches, MenuId_State_Load, @@ -298,15 +302,15 @@ public: protected: wxImageList m_ConfigImages; - wxScopedPtr m_ToolbarImages; - wxScopedPtr m_Bitmap_Logo; - wxScopedPtrm_StdoutRedirHandle; - wxScopedPtrm_StderrRedirHandle; + ScopedPtr m_ToolbarImages; + ScopedPtr m_Bitmap_Logo; + ScopedPtr m_StdoutRedirHandle; + ScopedPtr m_StderrRedirHandle; public: - wxScopedPtr m_CoreAllocs; - wxScopedPtr m_CorePlugins; - wxScopedPtr m_CoreThread; + ScopedPtr m_CoreAllocs; + ScopedPtr m_CorePlugins; + ScopedPtr m_CoreThread; protected: // Note: Pointers to frames should not be scoped because wxWidgets handles deletion @@ -333,16 +337,8 @@ public: void SysExecute(); void SysExecute( CDVD_SourceType cdvdsrc ); - void SysReset() - { - m_CoreThread.reset(); - m_CorePlugins.reset(); - } - - bool EmuInProgress() const - { - return m_CoreThread && m_CoreThread->IsRunning(); - } + void SysReset(); + bool SysIsActive() const; const wxBitmap& GetLogoBitmap(); wxImageList& GetImgList_Config(); @@ -350,28 +346,13 @@ public: const AppImageIds& GetImgId() const { return m_ImageId; } - MainEmuFrame& GetMainFrame() const - { - wxASSERT( ((uptr)GetTopWindow()) == ((uptr)m_MainFrame) ); - wxASSERT( m_MainFrame != NULL ); - return *m_MainFrame; - } + bool HasMainFrame() const { return m_MainFrame != NULL; } + bool HasCoreThread() const { return m_CoreThread != NULL; } - MainEmuFrame& GetMainFrameOrExcept() const - { - if( m_MainFrame == NULL ) - throw Exception::ObjectIsNull( "main application frame" ); - - return *m_MainFrame; - } - - SysCoreThread& GetCoreThreadOrExcept() const - { - if( !m_CoreThread ) - throw Exception::ObjectIsNull( "core emulation thread" ); - - return *m_CoreThread; - } + MainEmuFrame& GetMainFrame() const; + SysCoreThread& GetCoreThread() const; + MainEmuFrame& GetMainFrameOrExcept() const; + SysCoreThread& GetCoreThreadOrExcept() const; void OpenGsFrame(); void OnGsFrameClosed(); @@ -412,9 +393,14 @@ protected: void OnLoadPluginsComplete( wxCommandEvent& evt ); void OnSemaphorePing( wxCommandEvent& evt ); void OnOpenModalDialog( wxCommandEvent& evt ); + void OnCoreThreadTerminated( wxCommandEvent& evt ); + + void OnFreezeFinished( wxCommandEvent& evt ); + void OnThawFinished( wxCommandEvent& evt ); + void OnMessageBox( pxMessageBoxEvent& evt ); void OnEmuKeyDown( wxKeyEvent& evt ); - + // ---------------------------------------------------------------------------- // Override wx default exception handling behavior // ---------------------------------------------------------------------------- @@ -432,10 +418,10 @@ protected: // -------------------------------------------------------------------------------------- -// AppEmuThread class +// AppCoreThread class // -------------------------------------------------------------------------------------- -class AppEmuThread : public SysCoreThread +class AppCoreThread : public SysCoreThread { typedef SysCoreThread _parent; @@ -443,15 +429,16 @@ protected: wxKeyEvent m_kevt; public: - AppEmuThread( PluginManager& plugins ); - virtual ~AppEmuThread() throw(); + AppCoreThread( PluginManager& plugins ); + virtual ~AppCoreThread() throw(); virtual void Suspend( bool isBlocking=true ); virtual void StateCheck( bool isCancelable=true ); virtual void ApplySettings( const Pcsx2Config& src ); - virtual void OnResumeReady(); protected: + virtual void OnResumeReady(); + virtual void DoThreadCleanup(); sptr ExecuteTask(); }; @@ -470,6 +457,7 @@ public: bool IsReentrant() const { return Counter > 1; } }; +extern bool sys_resume_lock; extern int EnumeratePluginsInFolder( const wxDirName& searchPath, wxArrayString* dest ); extern void LoadPluginsPassive(); @@ -477,13 +465,13 @@ extern void LoadPluginsImmediate(); extern void UnloadPlugins(); extern bool HandlePluginError( Exception::PluginError& ex ); -extern bool EmulationInProgress(); -extern bool SysHasValidState(); extern void AppLoadSettings(); extern void AppSaveSettings(); extern void AppApplySettings( const AppConfig* oldconf=NULL ); +extern bool SysHasValidState(); + extern void SysStatus( const wxString& text ); extern void SysSuspend( bool closePlugins = true ); extern void SysResume(); @@ -491,36 +479,8 @@ extern void SysReset(); extern void SysExecute(); extern void SysExecute( CDVD_SourceType cdvdsrc ); +extern bool HasMainFrame(); +extern bool HasCoreThread(); -// -------------------------------------------------------------------------------------- -// AppInvoke macro -// -------------------------------------------------------------------------------------- -// This handy macro provides a safe way to invoke functions on objects that may or may not -// exist. If the object is null, the function is not called. Useful for calling things that -// are cosmetic optional, such as logging or status bars. -// -// Performance Note: This macro uses exception handling, and should not be used in the -// context of tight loops or performant code. -// -// Parameters: -// obj - name of the object. The name must have a matching accessor in Pcsx2App in the -// format of GetSomethingOrExcept(), where 'Something' would be the object name. -// runme - The function to call, complete with parameters. Note that parameters that -// perform actions (such as creating new objects or something) won't be run unless -// the 'obj' itself exists. -// -#define AppInvoke( obj, runme ) \ -do { \ - try { \ - wxGetApp().Get##obj##OrExcept().runme; \ - } \ - catch( Exception::ObjectIsNull& ) { } \ -} while( false ) - -#define AppInvokeBool( obj, runme, dest ) \ -{ \ - try { \ - (dest) = wxGetApp().Get##obj##OrExcept().runme; \ - } \ - catch( Exception::ObjectIsNull& ) { } \ -} while( false ) +extern MainEmuFrame& GetMainFrame(); +extern SysCoreThread& GetCoreThread(); diff --git a/pcsx2/gui/AppConfig.cpp b/pcsx2/gui/AppConfig.cpp index 1efd0e27d3..0b5fbf8a69 100644 --- a/pcsx2/gui/AppConfig.cpp +++ b/pcsx2/gui/AppConfig.cpp @@ -525,7 +525,8 @@ void AppConfig_OnChangedSettingsFolder( bool overwrite ) AppLoadSettings(); AppApplySettings(); - AppInvoke( MainFrame, ReloadRecentLists() ); + if( HasMainFrame() ) + GetMainFrame().ReloadRecentLists(); g_Conf->Folders.Logs.Mkdir(); diff --git a/pcsx2/gui/AppConfig.h b/pcsx2/gui/AppConfig.h index 9f22dbaec2..543a3eb76a 100644 --- a/pcsx2/gui/AppConfig.h +++ b/pcsx2/gui/AppConfig.h @@ -178,4 +178,4 @@ extern ConfigOverrides OverrideOptions; extern wxFileConfig* OpenFileConfig( const wxString& filename ); extern void AppConfig_OnChangedSettingsFolder( bool overwrite = false ); -extern wxScopedPtr g_Conf; +extern ScopedPtr g_Conf; diff --git a/pcsx2/gui/AppMain.cpp b/pcsx2/gui/AppMain.cpp index 4273f094d1..e2b2d2e9ca 100644 --- a/pcsx2/gui/AppMain.cpp +++ b/pcsx2/gui/AppMain.cpp @@ -37,12 +37,15 @@ DEFINE_EVENT_TYPE( pxEVT_SemaphorePing ); DEFINE_EVENT_TYPE( pxEVT_OpenModalDialog ); DEFINE_EVENT_TYPE( pxEVT_ReloadPlugins ); DEFINE_EVENT_TYPE( pxEVT_LoadPluginsComplete ); +DEFINE_EVENT_TYPE( pxEVT_AppCoreThread_Terminated ); +DEFINE_EVENT_TYPE( pxEVT_FreezeFinished ); +DEFINE_EVENT_TYPE( pxEVT_ThawFinished ); bool UseAdminMode = false; wxDirName SettingsFolder; bool UseDefaultSettingsFolder = true; -wxScopedPtr g_Conf; +ScopedPtr g_Conf; ConfigOverrides OverrideOptions; namespace Exception @@ -54,7 +57,7 @@ namespace Exception class StartupAborted : public BaseException { public: - virtual ~StartupAborted() throw() {} + DEFINE_EXCEPTION_COPYTORS( StartupAborted ) StartupAborted( const wxString& msg_eng=L"Startup initialization was aborted by the user." ) { @@ -64,21 +67,21 @@ namespace Exception }; } -AppEmuThread::AppEmuThread( PluginManager& plugins ) : +AppCoreThread::AppCoreThread( PluginManager& plugins ) : SysCoreThread( plugins ) , m_kevt() { } -AppEmuThread::~AppEmuThread() throw() +AppCoreThread::~AppCoreThread() throw() { - AppInvoke( MainFrame, ApplySettings() ); } -void AppEmuThread::Suspend( bool isBlocking ) +void AppCoreThread::Suspend( bool isBlocking ) { _parent::Suspend( isBlocking ); - AppInvoke( MainFrame, ApplySettings() ); + if( HasMainFrame() ) + GetMainFrame().ApplySettings(); // Clear the sticky key statuses, because hell knows what'll change while the PAD // plugin is suspended. @@ -88,7 +91,7 @@ void AppEmuThread::Suspend( bool isBlocking ) m_kevt.m_altDown = false; } -void AppEmuThread::OnResumeReady() +void AppCoreThread::OnResumeReady() { if( !DevAssert( wxThread::IsMain(), "SysCoreThread can only be resumed from the main/gui thread." ) ) return; @@ -99,7 +102,19 @@ void AppEmuThread::OnResumeReady() if( GSopen2 != NULL ) wxGetApp().OpenGsFrame(); - AppInvoke( MainFrame, ApplySettings() ); + if( HasMainFrame() ) + GetMainFrame().ApplySettings(); +} + +// Called whenever the thread has terminated, for either regular or irregular reasons. +// Typically the thread handles all its own errors, so there's no need to have error +// handling here. However it's a good idea to update the status of the GUI to reflect +// the new (lack of) thread status, so this posts a message to the App to do so. +void AppCoreThread::DoThreadCleanup() +{ + wxCommandEvent evt( pxEVT_AppCoreThread_Terminated ); + wxGetApp().AddPendingEvent( evt ); + _parent::DoThreadCleanup(); } // This is used when the GS plugin is handling its own window. Messages from the PAD @@ -111,14 +126,14 @@ static const int pxID_PadHandler_Keydown = 8030; extern int TranslateGDKtoWXK( u32 keysym ); #endif -void AppEmuThread::StateCheck( bool isCancelable ) +void AppCoreThread::StateCheck( bool isCancelable ) { _parent::StateCheck( isCancelable ); const keyEvent* ev = PADkeyEvent(); if( ev == NULL || (ev->key == 0) ) return; - GetPluginManager().KeyEvent( *ev ); + m_plugins.KeyEvent( *ev ); m_kevt.SetEventType( ( ev->evt == KEYPRESS ) ? wxEVT_KEY_DOWN : wxEVT_KEY_UP ); const bool isDown = (ev->evt == KEYPRESS); @@ -141,8 +156,13 @@ void AppEmuThread::StateCheck( bool isCancelable ) wxGetApp().PostPadKey( m_kevt ); } -void AppEmuThread::ApplySettings( const Pcsx2Config& src ) +// To simplify settings application rules and re-entry conditions, the main App's implementation +// of ApplySettings requires that the caller manually ensure that the thread has been properly +// suspended. If the thread has mot been suspended, this call will fail *silently*. +void AppCoreThread::ApplySettings( const Pcsx2Config& src ) { + if( !IsSuspended() ) return; + // Re-entry guard protects against cases where code wants to manually set core settings // which are not part of g_Conf. The subsequent call to apply g_Conf settings (which is // usually the desired behavior) will be ignored. @@ -153,17 +173,14 @@ void AppEmuThread::ApplySettings( const Pcsx2Config& src ) SysCoreThread::ApplySettings( src ); } -__forceinline bool EmulationInProgress() -{ - return wxGetApp().EmuInProgress(); -} - +// Returns true if there is a "valid" virtual machine state from the user's perspective. This +// means the user has started the emulator and not issued a full reset. __forceinline bool SysHasValidState() { - return wxGetApp().EmuInProgress() || StateRecovery::HasState(); + bool isRunning = HasCoreThread() ? GetCoreThread().IsRunning() : false; + return isRunning || StateCopy_HasFullState(); } - bool HandlePluginError( Exception::PluginError& ex ) { if( pxDialogExists( DialogId_CoreSettings ) ) return true; @@ -183,7 +200,7 @@ bool HandlePluginError( Exception::PluginError& ex ) return result; } -sptr AppEmuThread::ExecuteTask() +sptr AppCoreThread::ExecuteTask() { try { @@ -195,7 +212,7 @@ sptr AppEmuThread::ExecuteTask() m_plugins.Close(); if( ex.StreamName == g_Conf->FullpathToBios() ) { - GetPluginManager().Close(); + m_plugins.Close(); bool result = Msgbox::OkCancel( ex.FormatDisplayMessage() + _("\n\nPress Ok to go to the BIOS Configuration Panel.") ); @@ -270,7 +287,7 @@ void Pcsx2App::ReadUserModeSettings() wxFileName usermodefile( FilenameDefs::GetUsermodeConfig() ); usermodefile.SetPath( usrlocaldir.ToString() ); - wxScopedPtr conf_usermode( OpenFileConfig( usermodefile.GetFullPath() ) ); + ScopedPtr conf_usermode( OpenFileConfig( usermodefile.GetFullPath() ) ); wxString groupname( wxsFormat( L"CWD.%08x", hashres ) ); @@ -401,10 +418,10 @@ bool Pcsx2App::OnInit() wxInitAllImageHandlers(); if( !wxApp::OnInit() ) return false; - g_Conf.reset( new AppConfig() ); + g_Conf = new AppConfig(); - m_StdoutRedirHandle.reset( NewPipeRedir(stdout) ); - m_StderrRedirHandle.reset( NewPipeRedir(stderr) ); + m_StdoutRedirHandle = NewPipeRedir(stdout); + m_StderrRedirHandle = NewPipeRedir(stderr); wxLocale::AddCatalogLookupPathPrefix( wxGetCwd() ); #define pxMessageBoxEventThing(func) \ @@ -416,11 +433,14 @@ bool Pcsx2App::OnInit() Connect( pxEVT_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) ); Connect( pxEVT_ReloadPlugins, wxCommandEventHandler( Pcsx2App::OnReloadPlugins ) ); Connect( pxEVT_LoadPluginsComplete, wxCommandEventHandler( Pcsx2App::OnLoadPluginsComplete ) ); + + Connect( pxEVT_FreezeFinished, wxCommandEventHandler( Pcsx2App::OnFreezeFinished ) ); + Connect( pxEVT_ThawFinished, wxCommandEventHandler( Pcsx2App::OnThawFinished ) ); Connect( pxID_PadHandler_Keydown, wxEVT_KEY_DOWN, wxKeyEventHandler( Pcsx2App::OnEmuKeyDown ) ); // User/Admin Mode Dual Setup: - // Pcsx2 now supports two fundamental modes of operation. The default is Classic mode, + // PCSX2 now supports two fundamental modes of operation. The default is Classic mode, // which uses the Current Working Directory (CWD) for all user data files, and requires // Admin access on Vista (and some Linux as well). The second mode is the Vista- // compatible \documents folder usage. The mode is determined by the presence and @@ -456,7 +476,7 @@ bool Pcsx2App::OnInit() SysDetect(); AppApplySettings(); - m_CoreAllocs.reset( new SysCoreAllocations() ); + m_CoreAllocs = new SysCoreAllocations(); if( m_CoreAllocs->HadSomeFailures( g_Conf->EmuOptions.Cpu.Recompiler ) ) { @@ -578,6 +598,14 @@ void Pcsx2App::Ping() const // Pcsx2App Event Handlers // ---------------------------------------------------------------------------- +// Invoked by the AppCoreThread when the thread has terminated itself. +void Pcsx2App::OnCoreThreadTerminated( wxCommandEvent& evt ) +{ + if( HasMainFrame() ) + GetMainFrame().ApplySettings(); + m_CoreThread = NULL; +} + void Pcsx2App::OnSemaphorePing( wxCommandEvent& evt ) { ((Semaphore*)evt.GetClientData())->Post(); @@ -709,7 +737,7 @@ void Pcsx2App::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent& // the glorious user, whomever (s)he-it might be. bool Pcsx2App::PrepForExit() { - m_CoreThread.reset(); + m_CoreThread = NULL; CleanupMess(); return true; @@ -750,6 +778,36 @@ Pcsx2App::~Pcsx2App() CleanupMess(); } +MainEmuFrame& Pcsx2App::GetMainFrame() const +{ + wxASSERT( ((uptr)GetTopWindow()) == ((uptr)m_MainFrame) ); + wxASSERT( m_MainFrame != NULL ); + return *m_MainFrame; +} + +SysCoreThread& Pcsx2App::GetCoreThread() const +{ + wxASSERT( m_CoreThread != NULL ); + return *m_CoreThread; +} + + +MainEmuFrame& Pcsx2App::GetMainFrameOrExcept() const +{ + if( m_MainFrame == NULL ) + throw Exception::ObjectIsNull( "main application frame" ); + + return *m_MainFrame; +} + +SysCoreThread& Pcsx2App::GetCoreThreadOrExcept() const +{ + if( !m_CoreThread ) + throw Exception::ObjectIsNull( "core emulation thread" ); + + return *m_CoreThread; +} + void AppApplySettings( const AppConfig* oldconf ) { DevAssert( wxThread::IsMain(), "ApplySettings valid from the GUI thread only." ); @@ -780,9 +838,10 @@ void AppApplySettings( const AppConfig* oldconf ) } } - // Both AppInvokes cause unhandled runtime errors in Linux. - AppInvoke( MainFrame, ApplySettings() ); - AppInvoke( CoreThread, ApplySettings( g_Conf->EmuOptions ) ); + if( HasMainFrame() ) + GetMainFrame().ApplySettings(); + if( HasCoreThread() ) + GetCoreThread().ApplySettings( g_Conf->EmuOptions ); } void AppLoadSettings() @@ -793,7 +852,8 @@ void AppLoadSettings() IniLoader loader( *conf ); g_Conf->LoadSave( loader ); - AppInvoke( MainFrame, LoadRecentIsoList( *conf ) ); + if( HasMainFrame() ) + GetMainFrame().LoadRecentIsoList( *conf ); } void AppSaveSettings() @@ -804,7 +864,8 @@ void AppSaveSettings() IniSaver saver( *conf ); g_Conf->LoadSave( saver ); - AppInvoke( MainFrame, SaveRecentIsoList( *conf ) ); + if( HasMainFrame() ) + GetMainFrame().SaveRecentIsoList( *conf ); } // -------------------------------------------------------------------------------------- @@ -815,9 +876,15 @@ void AppSaveSettings() // configured CDVD source device. void Pcsx2App::SysExecute() { + if( sys_resume_lock ) + { + Console::WriteLn( "SysExecute: State is locked, ignoring Execute request!" ); + return; + } + SysReset(); LoadPluginsImmediate(); - m_CoreThread.reset( new AppEmuThread( *m_CorePlugins ) ); + m_CoreThread = new AppCoreThread( *m_CorePlugins ); m_CoreThread->Resume(); } @@ -826,15 +893,35 @@ void Pcsx2App::SysExecute() // sources. void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc ) { + if( sys_resume_lock ) + { + Console::WriteLn( "SysExecute: State is locked, ignoring Execute request!" ); + return; + } + SysReset(); LoadPluginsImmediate(); CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso ); CDVDsys_ChangeSource( cdvdsrc ); - m_CoreThread.reset( new AppEmuThread( *m_CorePlugins ) ); + m_CoreThread = new AppCoreThread( *m_CorePlugins ); m_CoreThread->Resume(); } +void Pcsx2App::SysReset() +{ + m_CoreThread = NULL; +} + +// Returns the state of the emulated system (Virtual Machine). Returns true if the +// system is active, or is in the process of reporting or handling an error. Returns +// false if there is no active system (thread is shutdown or hasn't been started). +bool Pcsx2App::SysIsActive() const +{ + return m_CoreThread && m_CoreThread->IsRunning(); +} + + void Pcsx2App::OpenGsFrame() { if( m_gsFrame != NULL ) return; @@ -857,11 +944,13 @@ void Pcsx2App::OnGsFrameClosed() // Writes text to console and updates the window status bar and/or HUD or whateverness. +// FIXME: This probably isn't thread safe. >_< void SysStatus( const wxString& text ) { // mirror output to the console! Console::Status( text.c_str() ); - AppInvoke( MainFrame, SetStatusText( text ) ); + if( HasMainFrame() ) + GetMainFrame().SetStatusText( text ); } // Executes the emulator using a saved/existing virtual machine state and currently @@ -878,18 +967,48 @@ void SysExecute( CDVD_SourceType cdvdsrc ) void SysResume() { - AppInvoke( CoreThread, Resume() ); + if( !HasCoreThread() ) return; + + if( sys_resume_lock ) + { + Console::WriteLn( "SysResume: State is locked, ignoring Resume request!" ); + return; + } + + GetCoreThread().Resume(); } void SysSuspend( bool closePlugins ) { + if( !HasCoreThread() ) return; + if( closePlugins ) - AppInvoke( CoreThread, Suspend(closePlugins) ); + GetCoreThread().Suspend(); else - AppInvoke( CoreThread, ShortSuspend() ); + GetCoreThread().ShortSuspend(); } void SysReset() { wxGetApp().SysReset(); } + +bool HasMainFrame() +{ + return wxGetApp().HasMainFrame(); +} + +bool HasCoreThread() +{ + return wxGetApp().HasCoreThread(); +} + +MainEmuFrame& GetMainFrame() +{ + return wxGetApp().GetMainFrame(); +} + +SysCoreThread& GetCoreThread() +{ + return wxGetApp().GetCoreThread(); +} diff --git a/pcsx2/gui/AppRes.cpp b/pcsx2/gui/AppRes.cpp index 67773c3c45..ce3f412153 100644 --- a/pcsx2/gui/AppRes.cpp +++ b/pcsx2/gui/AppRes.cpp @@ -62,7 +62,7 @@ const wxImage& LoadImageAny( // ------------------------------------------------------------------------ const wxBitmap& Pcsx2App::GetLogoBitmap() { - if( m_Bitmap_Logo != NULL ) + if( m_Bitmap_Logo ) return *m_Bitmap_Logo; wxFileName mess; @@ -90,7 +90,7 @@ const wxBitmap& Pcsx2App::GetLogoBitmap() wxImage img; EmbeddedImage temp; // because gcc can't allow non-const temporaries. LoadImageAny( img, useTheme, mess, L"BackgroundLogo", temp ); - m_Bitmap_Logo.reset( new wxBitmap( img ) ); + m_Bitmap_Logo = new wxBitmap( img ); return *m_Bitmap_Logo; } @@ -137,10 +137,10 @@ wxImageList& Pcsx2App::GetImgList_Config() // ------------------------------------------------------------------------ wxImageList& Pcsx2App::GetImgList_Toolbars() { - if( m_ToolbarImages == NULL ) + if( !m_ToolbarImages ) { const int imgSize = g_Conf->Toolbar_ImageSize ? 64 : 32; - m_ToolbarImages.reset( new wxImageList( imgSize, imgSize ) ); + m_ToolbarImages = new wxImageList( imgSize, imgSize ); wxFileName mess; bool useTheme = (g_Conf->DeskTheme != L"default"); diff --git a/pcsx2/gui/Dialogs/AboutBoxDialog.cpp b/pcsx2/gui/Dialogs/AboutBoxDialog.cpp index 03405eede4..ab0f695fca 100644 --- a/pcsx2/gui/Dialogs/AboutBoxDialog.cpp +++ b/pcsx2/gui/Dialogs/AboutBoxDialog.cpp @@ -72,14 +72,13 @@ Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ): static const wxString LabelGreets = wxString::FromUTF8( "Contributors" "\n\n" - "Hiryu and Sjeep for libcvd (the iso parsing and" - "filesystem driver code), nneeve (fpu and vu help)" + "Hiryu and Sjeep (libcdvd / iso filesystem), nneeve (fpu and vu)" "\n\n" "Plugin Specialists: ChickenLiver (Lilypad), Efp (efp)," "Gabest (Gsdx, Cdvdolio, Xpad), Zeydlitz (ZZogl)" "\n\n" "Special thanks to: black_wd, Belmont, BGome, _Demo_, Dreamtime," - "F|RES, MrBrown, razorblade, Seta-san, Skarmeth" + "F|RES, MrBrown, razorblade, Seta-san, Skarmeth, feal87" ); wxBoxSizer& mainSizer = *new wxBoxSizer( wxVERTICAL ); diff --git a/pcsx2/gui/FrameForGS.cpp b/pcsx2/gui/FrameForGS.cpp index 8550b2af12..0894cb67d0 100644 --- a/pcsx2/gui/FrameForGS.cpp +++ b/pcsx2/gui/FrameForGS.cpp @@ -33,13 +33,13 @@ void GSFrame::InitDefaultAccelerators() m_Accels.Map( AAC( WXK_TAB ), "Framelimiter_TurboToggle" ); m_Accels.Map( AAC( WXK_TAB ).Shift(), "Framelimiter_MasterToggle" ); - m_Accels.Map( AAC( WXK_ESCAPE ), "Emu_Suspend" ); - m_Accels.Map( AAC( WXK_F8 ), "Emu_TakeSnapshot" ); - m_Accels.Map( AAC( WXK_F9 ), "Emu_RenderswitchToggle" ); + m_Accels.Map( AAC( WXK_ESCAPE ), "Sys_Suspend" ); + m_Accels.Map( AAC( WXK_F8 ), "Sys_TakeSnapshot" ); + m_Accels.Map( AAC( WXK_F9 ), "Sys_RenderswitchToggle" ); - m_Accels.Map( AAC( WXK_F10 ), "Emu_LoggingToggle" ); - m_Accels.Map( AAC( WXK_F11 ), "Emu_FreezeGS" ); - m_Accels.Map( AAC( WXK_F12 ), "Emu_RecordingToggle" ); + m_Accels.Map( AAC( WXK_F10 ), "Sys_LoggingToggle" ); + m_Accels.Map( AAC( WXK_F11 ), "Sys_FreezeGS" ); + m_Accels.Map( AAC( WXK_F12 ), "Sys_RecordingToggle" ); } GSFrame::GSFrame(wxWindow* parent, const wxString& title): @@ -54,7 +54,8 @@ GSFrame::GSFrame(wxWindow* parent, const wxString& title): GSFrame::~GSFrame() throw() { - AppInvoke( CoreThread, Suspend() ); // Just in case...! + if( HasCoreThread() ) + GetCoreThread().Suspend(); // Just in case...! } void GSFrame::OnCloseWindow(wxCloseEvent& evt) diff --git a/pcsx2/gui/GlobalCommands.cpp b/pcsx2/gui/GlobalCommands.cpp index 27097e6f32..1be0c86bc3 100644 --- a/pcsx2/gui/GlobalCommands.cpp +++ b/pcsx2/gui/GlobalCommands.cpp @@ -69,31 +69,32 @@ namespace Implementations { } - void Emu_Suspend() + void Sys_Suspend() { - AppInvoke( CoreThread, Suspend() ); - AppInvoke( MainFrame, ApplySettings() ); + if( HasCoreThread() ) + GetCoreThread().Suspend(); } - void Emu_Resume() + void Sys_Resume() { - AppInvoke( CoreThread, Resume() ); - AppInvoke( MainFrame, ApplySettings() ); + if( HasCoreThread() ) + GetCoreThread().Resume(); } - void Emu_TakeSnapshot() + void Sys_TakeSnapshot() { GSmakeSnapshot( g_Conf->Folders.Snapshots.ToAscii().data() ); } - void Emu_RenderToggle() + void Sys_RenderToggle() { - AppInvoke( CoreThread, Suspend() ); + if( !HasCoreThread() ) return; + SysSuspend(); renderswitch = !renderswitch; - AppInvoke( CoreThread, Resume() ); + SysResume(); } - void Emu_LoggingToggle() + void Sys_LoggingToggle() { #ifdef PCSX2_DEVBUILD // There's likely a better way to implement this, but this seemed useful. @@ -108,7 +109,7 @@ namespace Implementations #endif } - void Emu_FreezeGS() + void Sys_FreezeGS() { // fixme : fix up gsstate mess and make it mtgs compatible -- air #ifdef _STGS_GSSTATE_CODE @@ -137,7 +138,7 @@ namespace Implementations } - void Emu_RecordingToggle() + void Sys_RecordingToggle() { g_Pcsx2Recording ^= 1; @@ -206,37 +207,37 @@ static const GlobalCommandDescriptor CommandDeclarations[] = NULL, }, - { "Emu_Suspend", - Implementations::Emu_Suspend, + { "Sys_Suspend", + Implementations::Sys_Suspend, NULL, NULL, }, - { "Emu_TakeSnapshot", - Implementations::Emu_TakeSnapshot, + { "Sys_TakeSnapshot", + Implementations::Sys_TakeSnapshot, NULL, NULL, }, - { "Emu_RenderswitchToggle", - Implementations::Emu_RenderToggle, + { "Sys_RenderswitchToggle", + Implementations::Sys_RenderToggle, NULL, NULL, }, - { "Emu_LoggingToggle", - Implementations::Emu_LoggingToggle, + { "Sys_LoggingToggle", + Implementations::Sys_LoggingToggle, NULL, NULL, }, - { "Emu_FreezeGS", - Implementations::Emu_FreezeGS, + { "Sys_FreezeGS", + Implementations::Sys_FreezeGS, NULL, NULL, }, - { "Emu_RecordingToggle", - Implementations::Emu_RecordingToggle, + { "Sys_RecordingToggle", + Implementations::Sys_RecordingToggle, NULL, NULL, }, @@ -304,11 +305,11 @@ void Pcsx2App::InitDefaultGlobalAccelerators() GlobalAccels.Map( AAC( WXK_TAB ), "Framelimiter_TurboToggle" ); GlobalAccels.Map( AAC( WXK_TAB ).Shift(), "Framelimiter_MasterToggle" ); - GlobalAccels.Map( AAC( WXK_ESCAPE ), "Emu_Suspend"); - GlobalAccels.Map( AAC( WXK_F8 ), "Emu_TakeSnapshot"); - GlobalAccels.Map( AAC( WXK_F9 ), "Emu_RenderswitchToggle"); + GlobalAccels.Map( AAC( WXK_ESCAPE ), "Sys_Suspend"); + GlobalAccels.Map( AAC( WXK_F8 ), "Sys_TakeSnapshot"); + GlobalAccels.Map( AAC( WXK_F9 ), "Sys_RenderswitchToggle"); - GlobalAccels.Map( AAC( WXK_F10 ), "Emu_LoggingToggle"); - GlobalAccels.Map( AAC( WXK_F11 ), "Emu_FreezeGS"); - GlobalAccels.Map( AAC( WXK_F12 ), "Emu_RecordingToggle"); + GlobalAccels.Map( AAC( WXK_F10 ), "Sys_LoggingToggle"); + GlobalAccels.Map( AAC( WXK_F11 ), "Sys_FreezeGS"); + GlobalAccels.Map( AAC( WXK_F12 ), "Sys_RecordingToggle"); } diff --git a/pcsx2/gui/MainFrame.cpp b/pcsx2/gui/MainFrame.cpp index f0b991d0f6..1026875668 100644 --- a/pcsx2/gui/MainFrame.cpp +++ b/pcsx2/gui/MainFrame.cpp @@ -211,8 +211,8 @@ void MainEmuFrame::ConnectMenus() ConnectMenu( MenuId_SkipBiosToggle, Menu_SkipBiosToggle_Click ); ConnectMenu( MenuId_Exit, Menu_Exit_Click ); - ConnectMenu( MenuId_Emu_SuspendResume, Menu_SuspendResume_Click ); - ConnectMenu( MenuId_Emu_Reset, Menu_EmuReset_Click ); + ConnectMenu( MenuId_Sys_SuspendResume, Menu_SuspendResume_Click ); + ConnectMenu( MenuId_Sys_Reset, Menu_EmuReset_Click ); ConnectMenu( MenuId_State_LoadOther, Menu_LoadStateOther_Click ); @@ -367,23 +367,23 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title): _("Closing PCSX2 may be hazardous to your health")); // ------------------------------------------------------------------------ - m_menuEmu.Append(MenuId_Emu_SuspendResume, _("Suspend"), + m_menuEmu.Append(MenuId_Sys_SuspendResume, _("Suspend"), _("Stops emulation dead in its tracks") )->Enable( SysHasValidState() ); m_menuEmu.AppendSeparator(); - //m_menuEmu.Append(MenuId_Emu_Close, _("Close"), + //m_menuEmu.Append(MenuId_Sys_Close, _("Close"), // _("Stops emulation and closes the GS window.")); - m_menuEmu.Append(MenuId_Emu_LoadStates, _("Load state"), &m_LoadStatesSubmenu); - m_menuEmu.Append(MenuId_Emu_SaveStates, _("Save state"), &m_SaveStatesSubmenu); + m_menuEmu.Append(MenuId_Sys_LoadStates, _("Load state"), &m_LoadStatesSubmenu); + m_menuEmu.Append(MenuId_Sys_SaveStates, _("Save state"), &m_SaveStatesSubmenu); m_menuEmu.AppendSeparator(); m_menuEmu.Append(MenuId_EnablePatches, _("Enable Patches"), wxEmptyString, wxITEM_CHECK); m_menuEmu.AppendSeparator(); - m_menuEmu.Append(MenuId_Emu_Reset, _("Reset"), + m_menuEmu.Append(MenuId_Sys_Reset, _("Reset"), _("Resets emulation state and re-runs current image")); // ------------------------------------------------------------------------ @@ -470,9 +470,7 @@ void MainEmuFrame::ReloadRecentLists() if( m_RecentIsoList ) m_RecentIsoList->Save( *cfg ); - m_RecentIsoList.reset(); - m_RecentIsoList.reset( new wxFileHistory( g_Conf->RecentFileCount ) ); - m_RecentIsoList->Load( *cfg ); + m_RecentIsoList.Reassign( new wxFileHistory(g_Conf->RecentFileCount) )->Load( *cfg ); UpdateIsoSrcFile(); cfg->Flush(); } @@ -484,12 +482,11 @@ void MainEmuFrame::ApplySettings() GetMenuBar()->Check( MenuId_Config_Multitap0Toggle, g_Conf->EmuOptions.MultitapPort0_Enabled ); GetMenuBar()->Check( MenuId_Config_Multitap1Toggle, g_Conf->EmuOptions.MultitapPort1_Enabled ); - GetMenuBar()->Enable( MenuId_Emu_SuspendResume, SysHasValidState() ); - - bool result = false; - AppInvokeBool( CoreThread, IsSuspended(), result ); - GetMenuBar()->SetLabel( MenuId_Emu_SuspendResume, result ? _("Resume") :_("Suspend") ); + GetMenuBar()->Enable( MenuId_Sys_SuspendResume, SysHasValidState() ); + if( HasCoreThread() ) + GetMenuBar()->SetLabel( MenuId_Sys_SuspendResume, GetCoreThread().IsSuspended() ? _("Resume") :_("Suspend") ); + if( m_RecentIsoList ) { if( m_RecentIsoList->GetMaxFiles() != g_Conf->RecentFileCount ) diff --git a/pcsx2/gui/MainFrame.h b/pcsx2/gui/MainFrame.h index f64b92befd..d51b396b22 100644 --- a/pcsx2/gui/MainFrame.h +++ b/pcsx2/gui/MainFrame.h @@ -43,7 +43,7 @@ class MainEmuFrame : public wxFrame // ------------------------------------------------------------------------ protected: - wxScopedPtr m_RecentIsoList; + ScopedPtr m_RecentIsoList; wxStatusBar& m_statusbar; wxStaticBitmap m_background; @@ -74,7 +74,7 @@ public: void OnLogBoxHidden(); - bool IsPaused() const { return GetMenuBar()->IsChecked( MenuId_Emu_SuspendResume ); } + bool IsPaused() const { return GetMenuBar()->IsChecked( MenuId_Sys_SuspendResume ); } void UpdateIsoSrcFile(); void UpdateIsoSrcSelection(); void ApplySettings(); diff --git a/pcsx2/gui/MainMenuClicks.cpp b/pcsx2/gui/MainMenuClicks.cpp index 90c1e617b9..5da37c5680 100644 --- a/pcsx2/gui/MainMenuClicks.cpp +++ b/pcsx2/gui/MainMenuClicks.cpp @@ -93,10 +93,10 @@ void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event ) } } - if( EmulationInProgress() ) + if( SysHasValidState() ) { // [TODO] : Add one of 'dems checkboxes that read like "[x] don't show this stupid shit again, kthx." - bool result = Msgbox::OkCancel( pxE( ".Popup:ConfirmEmuReset", L"This will reset the emulator and your current emulation session will be lost. Are you sure?") ); + bool result = Msgbox::OkCancel( pxE( ".Popup:ConfirmSysReset", L"This will reset the emulator and your current emulation session will be lost. Are you sure?") ); if( !result ) { @@ -190,11 +190,9 @@ void MainEmuFrame::Menu_Exit_Click(wxCommandEvent &event) void MainEmuFrame::Menu_SuspendResume_Click(wxCommandEvent &event) { if( !SysHasValidState() ) return; + if( !HasCoreThread() ) return; - bool result = false; - AppInvokeBool( CoreThread, IsSuspended(), result ); - - if( result ) + if( GetCoreThread().IsSuspended() ) SysResume(); else SysSuspend(); @@ -202,15 +200,13 @@ void MainEmuFrame::Menu_SuspendResume_Click(wxCommandEvent &event) void MainEmuFrame::Menu_EmuReset_Click(wxCommandEvent &event) { - bool wasInProgress = EmulationInProgress(); - bool wasSuspended; - - AppInvokeBool( CoreThread, IsSuspended(), wasSuspended ); + if( !SysHasValidState() ) return; + bool wasSuspended = HasCoreThread() ? GetCoreThread().IsSuspended() : true; SysReset(); - if( !wasInProgress || wasSuspended ) return; - SysExecute(); + if( !wasSuspended ) + SysExecute(); } void MainEmuFrame::Menu_ConfigPlugin_Click(wxCommandEvent &event) diff --git a/pcsx2/gui/Panels/BiosSelectorPanel.cpp b/pcsx2/gui/Panels/BiosSelectorPanel.cpp index 09962e50df..d9fec0ddee 100644 --- a/pcsx2/gui/Panels/BiosSelectorPanel.cpp +++ b/pcsx2/gui/Panels/BiosSelectorPanel.cpp @@ -95,15 +95,15 @@ bool Panels::BiosSelectorPanel::ValidateEnumerationStatus() // Impl Note: ScopedPtr used so that resources get cleaned up if an exception // occurs during file enumeration. - wxScopedPtr bioslist( new wxArrayString() ); + ScopedPtr bioslist( new wxArrayString() ); if( m_FolderPicker.GetPath().Exists() ) - wxDir::GetAllFiles( m_FolderPicker.GetPath().ToString(), bioslist.get(), L"*.bin", wxDIR_FILES ); + wxDir::GetAllFiles( m_FolderPicker.GetPath().ToString(), bioslist, L"*.bin", wxDIR_FILES ); if( !m_BiosList || (*bioslist != *m_BiosList) ) validated = false; - m_BiosList.swap( bioslist ); + m_BiosList.SwapPtr( bioslist ); return validated; } diff --git a/pcsx2/gui/Panels/ConfigurationPanels.h b/pcsx2/gui/Panels/ConfigurationPanels.h index dd55b4d87f..a744330d44 100644 --- a/pcsx2/gui/Panels/ConfigurationPanels.h +++ b/pcsx2/gui/Panels/ConfigurationPanels.h @@ -55,7 +55,7 @@ namespace Exception Panels::BaseApplicableConfigPanel* m_Panel; public: - virtual ~CannotApplySettings() throw() {} + DEFINE_EXCEPTION_COPYTORS( CannotApplySettings ) explicit CannotApplySettings( Panels::BaseApplicableConfigPanel* thispanel, const char* msg=wxLt("Cannot apply new settings, one of the settings is invalid.") ) { @@ -351,7 +351,7 @@ namespace Panels class BiosSelectorPanel : public BaseSelectorPanel { protected: - wxScopedPtr m_BiosList; + ScopedPtr m_BiosList; wxListBox& m_ComboBox; DirPickerPanel& m_FolderPicker; @@ -454,8 +454,8 @@ namespace Panels ComboBoxPanel& m_ComponentBoxes; bool m_Canceled; - wxScopedPtr m_FileList; // list of potential plugin files - wxScopedPtr m_EnumeratorThread; + ScopedPtr m_FileList; // list of potential plugin files + ScopedPtr m_EnumeratorThread; public: virtual ~PluginSelectorPanel(); diff --git a/pcsx2/gui/Panels/PluginSelectorPanel.cpp b/pcsx2/gui/Panels/PluginSelectorPanel.cpp index 64aa8cc08e..bb672c13a7 100644 --- a/pcsx2/gui/Panels/PluginSelectorPanel.cpp +++ b/pcsx2/gui/Panels/PluginSelectorPanel.cpp @@ -364,9 +364,7 @@ void Panels::PluginSelectorPanel::DoRefresh() wxCommandEvent evt( pxEVT_ShowStatusBar ); GetEventHandler()->AddPendingEvent( evt ); - // Use a thread to load plugins. - m_EnumeratorThread.reset( NULL ); - m_EnumeratorThread.reset( new EnumThread( *this ) ); + m_EnumeratorThread.Delete() = new EnumThread( *this ); if( DisableThreading ) m_EnumeratorThread->DoNextPlugin( 0 ); @@ -376,7 +374,7 @@ void Panels::PluginSelectorPanel::DoRefresh() bool Panels::PluginSelectorPanel::ValidateEnumerationStatus() { - m_EnumeratorThread.reset(); // make sure the thread is STOPPED, just in case... + m_EnumeratorThread = NULL; // make sure the thread is STOPPED, just in case... bool validated = true; @@ -385,20 +383,20 @@ bool Panels::PluginSelectorPanel::ValidateEnumerationStatus() // Impl Note: ScopedPtr used so that resources get cleaned up if an exception // occurs during file enumeration. - wxScopedPtr pluginlist( new wxArrayString() ); + ScopedPtr pluginlist( new wxArrayString() ); - int pluggers = EnumeratePluginsInFolder( m_ComponentBoxes.GetPluginsPath(), pluginlist.get() ); + int pluggers = EnumeratePluginsInFolder( m_ComponentBoxes.GetPluginsPath(), pluginlist ); if( !m_FileList || (*pluginlist != *m_FileList) ) validated = false; if( pluggers == 0 ) { - m_FileList.reset(); + m_FileList = NULL; return validated; } - m_FileList.swap( pluginlist ); + m_FileList.SwapPtr( pluginlist ); m_StatusPanel.SetGaugeLength( pluggers ); @@ -428,7 +426,7 @@ void Panels::PluginSelectorPanel::OnShowStatusBar( wxCommandEvent& evt ) void Panels::PluginSelectorPanel::OnEnumComplete( wxCommandEvent& evt ) { - m_EnumeratorThread.reset(); + m_EnumeratorThread = NULL; // fixme: Default plugins should be picked based on the timestamp of the DLL or something? // (for now we just force it to selection zero if nothing's selected) diff --git a/pcsx2/gui/Plugins.cpp b/pcsx2/gui/Plugins.cpp index 62aee125a6..fd852de38a 100644 --- a/pcsx2/gui/Plugins.cpp +++ b/pcsx2/gui/Plugins.cpp @@ -63,13 +63,15 @@ protected: int ExecuteTask(); }; -static wxScopedPtr _loadTask; +static ScopedPtr _loadTask; LoadPluginsTask::~LoadPluginsTask() throw() { - _loadTask.release(); + if( _loadTask ) + _loadTask.DetachPtr(); // avoids recursive deletion + PersistentThread::Cancel(); - _loadTask.reset(); + _loadTask = NULL; } int LoadPluginsTask::ExecuteTask() @@ -111,10 +113,10 @@ int LoadPluginsTask::ExecuteTask() int EnumeratePluginsInFolder( const wxDirName& searchpath, wxArrayString* dest ) { - wxScopedPtr placebo; + ScopedPtr placebo; wxArrayString* realdest = dest; if( realdest == NULL ) - placebo.reset( realdest = new wxArrayString() ); + placebo = realdest = new wxArrayString(); // placebo is our /dev/null -- gets deleted when done #ifdef __WXMSW__ // Windows pretty well has a strict "must end in .dll" rule. @@ -137,8 +139,8 @@ void Pcsx2App::OnReloadPlugins( wxCommandEvent& evt ) void Pcsx2App::OnLoadPluginsComplete( wxCommandEvent& evt ) { // scoped ptr ensures the thread object is cleaned up even on exception: - wxScopedPtr killTask( (LoadPluginsTask*)evt.GetClientData() ); - m_CorePlugins.reset( killTask->Result ); + ScopedPtr killTask( (LoadPluginsTask*)evt.GetClientData() ); + m_CorePlugins = killTask->Result; if( !m_CorePlugins ) { @@ -153,8 +155,8 @@ void Pcsx2App::ReloadPlugins() { if( _loadTask ) return; - m_CoreThread.reset(); - m_CorePlugins.reset(); + m_CoreThread = NULL; + m_CorePlugins = NULL; wxString passins[PluginId_Count]; @@ -166,7 +168,7 @@ void Pcsx2App::ReloadPlugins() passins[pi->id] = g_Conf->FullpathTo( pi->id ); } while( ++pi, pi->shortname != NULL ); - _loadTask.reset( new LoadPluginsTask( passins ) ); + _loadTask.Delete() = new LoadPluginsTask( passins ); // ... and when it finishes it posts up a OnLoadPluginsComplete(). Bye. :) } @@ -203,5 +205,5 @@ void LoadPluginsImmediate() void UnloadPlugins() { - wxGetApp().m_CorePlugins.reset(); + wxGetApp().m_CorePlugins = NULL; } diff --git a/pcsx2/gui/Saveslots.cpp b/pcsx2/gui/Saveslots.cpp index 1eb4a0bc2f..99ebe3a7af 100644 --- a/pcsx2/gui/Saveslots.cpp +++ b/pcsx2/gui/Saveslots.cpp @@ -23,16 +23,6 @@ StartupParams g_Startup; -////////////////////////////////////////////////////////////////////////////////////////// -// Save Slot Detection System - -bool States_isSlotUsed(int num) -{ - if (ElfCRC == 0) - return false; - else - return wxFileExists( SaveStateBase::GetFilename( num ) ); -} // returns true if the new state was loaded, or false if nothing happened. void States_Load( const wxString& file ) @@ -55,7 +45,7 @@ void States_Load( const wxString& file ) catch( Exception::BaseException& ) { // VM state is probably ruined. We'll need to recover from the in-memory backup. - StateRecovery::Recover(); + //StateRecovery::Recover(); } SysExecute(); @@ -64,7 +54,7 @@ void States_Load( const wxString& file ) // Save state save-to-file (or slot) helpers. void States_Save( const wxString& file ) { - if( !EmulationInProgress() ) + if( !SysHasValidState() ) { Msgbox::Alert( _("You need to start emulation first before you can save it's state.") ); return; @@ -73,7 +63,7 @@ void States_Save( const wxString& file ) try { Console::Status( wxsFormat( L"Saving savestate to file: %s", file.c_str() ) ); - StateRecovery::SaveToFile( file ); + StateCopy_SaveToFile( file ); SysStatus( wxsFormat( _("State saved to file: %s"), wxFileName( file ).GetFullName().c_str() ) ); } catch( Exception::BaseException& ex ) @@ -103,6 +93,14 @@ void States_Save( const wxString& file ) static int StatesC = 0; static const int StateSlotsCount = 10; +bool States_isSlotUsed(int num) +{ + if (ElfCRC == 0) + return false; + else + return wxFileExists( SaveStateBase::GetFilename( num ) ); +} + void States_FreezeCurrentSlot() { Console::Status( "Saving savestate to slot %d...", StatesC ); diff --git a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj index fc65caede7..68c150c80f 100644 --- a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj +++ b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj @@ -1938,10 +1938,6 @@ RelativePath="..\..\gui\GlobalCommands.cpp" > - - @@ -2519,6 +2515,10 @@ RelativePath="..\..\gui\Resources\EmbeddedImage.h" > + + diff --git a/pcsx2/windows/WinConsolePipe.cpp b/pcsx2/windows/WinConsolePipe.cpp index f9e0a12b57..10df8db824 100644 --- a/pcsx2/windows/WinConsolePipe.cpp +++ b/pcsx2/windows/WinConsolePipe.cpp @@ -153,6 +153,7 @@ public: m_outpipe( outpipe ) , m_color( color ) { + m_name = (m_color == Color_Red) ? L"Redirect_Stderr" : L"Redirect_Stdout"; } virtual ~WinPipeThread() throw() @@ -166,8 +167,6 @@ protected: try { SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL ); - SetName( (m_color == Color_Red) ? "Redirect_Stderr" :" Redirect_Stdout" ); - while( true ) { Sleep( 100 );