diff --git a/common/build/Utilities/utilities.vcproj b/common/build/Utilities/utilities.vcproj index aa730eed50..0dc4a724d3 100644 --- a/common/build/Utilities/utilities.vcproj +++ b/common/build/Utilities/utilities.vcproj @@ -537,6 +537,10 @@ RelativePath="..\..\include\Utilities\ScopedPtr.h" > + + diff --git a/common/include/Utilities/Exceptions.h b/common/include/Utilities/Exceptions.h index cd9fd463f0..28a5d91444 100644 --- a/common/include/Utilities/Exceptions.h +++ b/common/include/Utilities/Exceptions.h @@ -170,6 +170,7 @@ namespace Exception DEFINE_RUNTIME_EXCEPTION( RuntimeError, wxLt("An unhandled runtime error has occurred, somewhere in the depths of Pcsx2's cluttered brain-matter.") ) RuntimeError( const std::runtime_error& ex, const wxString& prefix=wxEmptyString ); + RuntimeError( const std::exception& ex, const wxString& prefix=wxEmptyString ); }; // -------------------------------------------------------------------------------------- diff --git a/common/include/Utilities/PersistentThread.h b/common/include/Utilities/PersistentThread.h index 58e723ce07..89ec379416 100644 --- a/common/include/Utilities/PersistentThread.h +++ b/common/include/Utilities/PersistentThread.h @@ -16,6 +16,7 @@ #pragma once #include "Threading.h" +#include "ScopedPtrMT.h" #include "EventSource.h" namespace Threading @@ -130,7 +131,7 @@ namespace Threading // 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; + ScopedPtrMT m_except; EventSource m_evtsrc_OnDelete; diff --git a/common/include/Utilities/ScopedPtr.h b/common/include/Utilities/ScopedPtr.h index 10710a482f..2a218259b9 100644 --- a/common/include/Utilities/ScopedPtr.h +++ b/common/include/Utilities/ScopedPtr.h @@ -1,3 +1,18 @@ +/* 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 // -------------------------------------------------------------------------------------- @@ -15,10 +30,12 @@ protected: public: typedef T element_type; - wxEXPLICIT ScopedPtr(T * ptr = NULL) : m_ptr(ptr) { } + wxEXPLICIT ScopedPtr(T * ptr = NULL) + { + m_ptr = ptr; + } - ~ScopedPtr() throw() - { Delete(); } + ~ScopedPtr() throw() { Delete(); } ScopedPtr& Reassign(T * ptr = NULL) { @@ -128,13 +145,14 @@ class ScopedArray protected: T* m_array; uint m_valid_range; + public: typedef T element_type; wxEXPLICIT ScopedArray(T * ptr = NULL) : - m_array(ptr) - , m_valid_range( 0xffffffff ) { + m_array = ptr; + m_valid_range = 0xffffffff; } wxEXPLICIT ScopedArray( int size ) : diff --git a/common/include/Utilities/ScopedPtrMT.h b/common/include/Utilities/ScopedPtrMT.h new file mode 100644 index 0000000000..89bd3012a5 --- /dev/null +++ b/common/include/Utilities/ScopedPtrMT.h @@ -0,0 +1,142 @@ +/* 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 "Threading.h" + +// -------------------------------------------------------------------------------------- +// ScopedPtrMT +// -------------------------------------------------------------------------------------- + +template< typename T > +class ScopedPtrMT +{ + DeclareNoncopyableObject(ScopedPtrMT); + + typedef T* TPtr; + +protected: + volatile TPtr m_ptr; + Threading::Mutex m_mtx; + +public: + typedef T element_type; + + wxEXPLICIT ScopedPtrMT(T * ptr = NULL) + { + m_ptr = ptr; + } + + ~ScopedPtrMT() throw() { _Delete_unlocked(); } + + ScopedPtrMT& Reassign(T * ptr = NULL) + { + TPtr doh = (TPtr)AtomicExchangePointer( m_ptr, ptr ); + if ( ptr != doh ) delete doh; + return *this; + } + + ScopedPtrMT& Delete() throw() + { + ScopedLock lock( m_mtx ); + _Delete_unlocked(); + } + + // Removes the pointer from scoped management, but does not delete! + // (ScopedPtr will be NULL after this method) + T *DetachPtr() + { + ScopedLock lock( m_mtx ); + + T *ptr = m_ptr; + m_ptr = NULL; + return ptr; + } + + // Returns the managed pointer. Can return NULL as a valid result if the ScopedPtrMT + // has no object in management. + T* GetPtr() const + { + return m_ptr; + } + + void SwapPtr(ScopedPtrMT& other) + { + ScopedLock lock( m_mtx ); + T * const tmp = other.m_ptr; + other.m_ptr = m_ptr; + m_ptr = tmp; + } + + // ---------------------------------------------------------------------------- + // ScopedPtrMT 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; + } + + // Equality + bool operator==(T* pT) const throw() + { + return m_ptr == pT; + } + + // Inequality + bool operator!=(T* pT) const throw() + { + return !operator==(pT); + } + + // Convenient assignment operator. ScopedPtrMT = NULL will issue an automatic deletion + // of the managed pointer. + ScopedPtrMT& operator=( T* src ) + { + return Reassign( src ); + } + + #if 0 + operator T*() const + { + return m_ptr; + } + + // Dereference operator, returns a handle to the managed pointer. + // Generates a debug assertion if the object is NULL! + T& operator*() const + { + pxAssert(m_ptr != NULL); + return *m_ptr; + } + + T* operator->() const + { + pxAssert(m_ptr != NULL); + return m_ptr; + } + #endif + +protected: + void _Delete_unlocked() throw() + { + delete m_ptr; + m_ptr = NULL; + } +}; diff --git a/common/include/Utilities/Threading.h b/common/include/Utilities/Threading.h index 5d8428d8ef..4436a89e18 100644 --- a/common/include/Utilities/Threading.h +++ b/common/include/Utilities/Threading.h @@ -156,15 +156,11 @@ namespace Threading extern bool AtomicBitTestAndReset( volatile u32& bitset, u8 bit ); - extern void* _AtomicExchangePointer( void * volatile * const target, void* const value ); - extern void* _AtomicCompareExchangePointer( void * volatile * const target, void* const value, void* const comparand ); - -#define AtomicExchangePointer( target, value ) \ - _InterlockedExchangePointer( &target, value ) - -#define AtomicCompareExchangePointer( target, value, comparand ) \ - _InterlockedCompareExchangePointer( &target, value, comparand ) + extern void* _AtomicExchangePointer( volatile uptr& target, uptr value ); + extern void* _AtomicCompareExchangePointer( volatile uptr& target, uptr value, uptr comparand ); +#define AtomicExchangePointer( dest, src ) _AtomicExchangePointer( (uptr&)dest, (uptr)src ) +#define AtomicCompareExchangePointer( dest, comp, src ) _AtomicExchangePointer( (uptr&)dest, (uptr)comp, (uptr)src ) // pthread Cond is an evil api that is not suited for Pcsx2 needs. // Let's not use it. Use mutexes and semaphores instead to create waits. (Air) diff --git a/common/include/Utilities/wxAppWithHelpers.h b/common/include/Utilities/wxAppWithHelpers.h index fb9d60291d..95bb62ecaa 100644 --- a/common/include/Utilities/wxAppWithHelpers.h +++ b/common/include/Utilities/wxAppWithHelpers.h @@ -43,6 +43,8 @@ public: typedef void FnType_Void(); +typedef std::list wxEventList; + // -------------------------------------------------------------------------------------- // wxAppWithHelpers // -------------------------------------------------------------------------------------- @@ -53,9 +55,9 @@ class wxAppWithHelpers : public wxApp DECLARE_DYNAMIC_CLASS(wxAppWithHelpers) protected: - std::vector m_IdleEventQueue; - Threading::Mutex m_IdleEventMutex; - wxTimer m_IdleEventTimer; + wxEventList m_IdleEventQueue; + Threading::MutexRecursive m_IdleEventMutex; + wxTimer m_IdleEventTimer; public: wxAppWithHelpers(); @@ -105,7 +107,7 @@ public: bool ProcessEvent( pxInvokeActionEvent* evt ); protected: - void IdleEventDispatcher( const char* action ); + void IdleEventDispatcher( const wxChar* action ); void OnIdleEvent( wxIdleEvent& evt ); void OnStartIdleEventTimer( wxEvent& evt ); diff --git a/common/src/Utilities/Exceptions.cpp b/common/src/Utilities/Exceptions.cpp index ba54ab4d02..b5bbc91647 100644 --- a/common/src/Utilities/Exceptions.cpp +++ b/common/src/Utilities/Exceptions.cpp @@ -174,8 +174,19 @@ wxString BaseException::FormatDiagnosticMessage() const // ------------------------------------------------------------------------ Exception::RuntimeError::RuntimeError( const std::runtime_error& ex, const wxString& prefix ) { - const wxString msg( wxsFormat( L"%sSTL Runtime Error: %s", - (prefix.IsEmpty() ? prefix.c_str() : wxsFormat(L"(%s) ", prefix.c_str()).c_str()), + const wxString msg( wxsFormat( L"STL Runtime Error%s: %s", + (prefix.IsEmpty() ? prefix.c_str() : wxsFormat(L" (%s)", prefix.c_str()).c_str()), + fromUTF8( ex.what() ).c_str() + ) ); + + BaseException::InitBaseEx( msg, msg ); +} + +// ------------------------------------------------------------------------ +Exception::RuntimeError::RuntimeError( const std::exception& ex, const wxString& prefix ) +{ + const wxString msg( wxsFormat( L"STL Exception%s: %s", + (prefix.IsEmpty() ? prefix.c_str() : wxsFormat(L" (%s)", prefix.c_str()).c_str()), fromUTF8( ex.what() ).c_str() ) ); diff --git a/common/src/Utilities/ThreadTools.cpp b/common/src/Utilities/ThreadTools.cpp index 0fdc71f975..fb83db6152 100644 --- a/common/src/Utilities/ThreadTools.cpp +++ b/common/src/Utilities/ThreadTools.cpp @@ -380,8 +380,14 @@ void Threading::PersistentThread::AddListener( EventListener_Thread& evt ) // the thread will have allowed itself to terminate properly. void Threading::PersistentThread::RethrowException() const { - if( !m_except ) return; - m_except->Rethrow(); + // Thread safety note: always detach the m_except pointer. If we checked it for NULL, the + // pointer might still be invalid after detachment, so might as well just detach and check + // after. + + ScopedPtr ptr( const_cast(this)->m_except.DetachPtr() ); + if( ptr ) ptr->Rethrow(); + + //m_except->Rethrow(); } static bool m_BlockDeletions = false; @@ -533,8 +539,9 @@ void Threading::PersistentThread::_try_virtual_invoke( void (PersistentThread::* // ---------------------------------------------------------------------------- catch( Exception::RuntimeError& ex ) { - m_except = ex.Clone(); - m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName().c_str() ) + m_except->DiagMsg(); + BaseException* woot = ex.Clone(); + woot->DiagMsg() += wxsFormat( L"(thread:%s)", GetName().c_str() ); + m_except = woot; } #ifndef PCSX2_DEVBUILD // ---------------------------------------------------------------------------- @@ -543,13 +550,13 @@ void Threading::PersistentThread::_try_virtual_invoke( void (PersistentThread::* // the MSVC debugger (or by silent random annoying fail on debug-less linux). /*catch( std::logic_error& ex ) { - throw BaseException( wxsFormat( L"(thread: %s) STL Logic Error: %s", + throw BaseException( wxsFormat( L"STL Logic Error (thread:%s): %s", GetName().c_str(), fromUTF8( ex.what() ).c_str() ) ); } catch( std::exception& ex ) { - throw BaseException( wxsFormat( L"(thread: %s) STL exception: %s", + throw BaseException( wxsFormat( L"STL exception (thread:%s): %s", GetName().c_str(), fromUTF8( ex.what() ).c_str() ) ); }*/ @@ -805,6 +812,24 @@ __forceinline s32 Threading::AtomicDecrement( volatile s32& Target ) return _InterlockedExchangeAdd( (volatile long*)&Target, -1 ); } +__forceinline void* Threading::_AtomicExchangePointer( volatile uptr& target, uptr value ) +{ +#ifdef _M_AMD64 // high-level atomic ops, please leave these 64 bit checks in place. + return (void*)_InterlockedExchange64( &(volatile s64&)target, value ); +#else + return (void*)_InterlockedExchange( (volatile long*)&target, value ); +#endif +} + +__forceinline void* Threading::_AtomicCompareExchangePointer( volatile uptr& target, uptr value, uptr comparand ) +{ +#ifdef _M_AMD64 // high-level atomic ops, please leave these 64 bit checks in place. + return (void*)_InterlockedCompareExchange64( &(volatile s64&)target, value ); +#else + return (void*)_InterlockedCompareExchange( &(volatile long&)target, value, comparand ); +#endif +} + // -------------------------------------------------------------------------------------- // BaseThreadError // -------------------------------------------------------------------------------------- diff --git a/common/src/Utilities/wxAppWithHelpers.cpp b/common/src/Utilities/wxAppWithHelpers.cpp index 400dff8717..c53d204492 100644 --- a/common/src/Utilities/wxAppWithHelpers.cpp +++ b/common/src/Utilities/wxAppWithHelpers.cpp @@ -420,27 +420,37 @@ void wxAppWithHelpers::OnStartIdleEventTimer( wxEvent& evt ) m_IdleEventTimer.Start( 100, true ); } -void wxAppWithHelpers::IdleEventDispatcher( const char* action ) +void wxAppWithHelpers::IdleEventDispatcher( const wxChar* action ) { + // Recursion is possible thanks to modal dialogs being issued from the idle event handler. + // (recursion shouldn't hurt anything anyway, since the node system re-creates the iterator + // on each pass) + + //static int __guard=0; + //RecursionGuard guard(__guard); + //if( !pxAssertDev(!guard.IsReentrant(), "Re-entrant call to IdleEventdispatcher caught on camera!") ) return; + + wxEventList postponed; + wxEventList::iterator node; + ScopedLock lock( m_IdleEventMutex ); - size_t size = m_IdleEventQueue.size(); - if( size == 0 ) return; - - DbgCon.WriteLn( Color_Gray, "App IdleQueue (%s) -> %u events.", action, size ); - - std::vector postponed; - - for( size_t i=0; iGetEventType() == pxEvt_DeleteThread) ) - postponed.push_back(m_IdleEventQueue[i]); + ScopedPtr deleteMe(*node); + m_IdleEventQueue.erase( node ); + + lock.Release(); + if( !Threading::AllowDeletions() && (deleteMe->GetEventType() == pxEvt_DeleteThread) ) + { + postponed.push_back(deleteMe.DetachPtr()); + } else { - lock.Release(); - ProcessEvent( *m_IdleEventQueue[i] ); - lock.Acquire(); + DbgCon.WriteLn( Color_Gray, L"(AppIdleQueue:%s) -> Dispatching event '%s'", action, deleteMe->GetClassInfo()->GetClassName() ); + ProcessEvent( *deleteMe ); // dereference to prevent auto-deletion by ProcessEvent } + lock.Acquire(); } m_IdleEventQueue = postponed; @@ -449,12 +459,12 @@ void wxAppWithHelpers::IdleEventDispatcher( const char* action ) void wxAppWithHelpers::OnIdleEvent( wxIdleEvent& evt ) { m_IdleEventTimer.Stop(); - IdleEventDispatcher( "Idle" ); + IdleEventDispatcher( L"Idle" ); } void wxAppWithHelpers::OnIdleEventTimeout( wxTimerEvent& evt ) { - IdleEventDispatcher( "Timeout" ); + IdleEventDispatcher( L"Timeout" ); } void wxAppWithHelpers::Ping() diff --git a/pcsx2/MTGS.cpp b/pcsx2/MTGS.cpp index f444a93320..94a111d5f5 100644 --- a/pcsx2/MTGS.cpp +++ b/pcsx2/MTGS.cpp @@ -523,8 +523,11 @@ void SysMtgsThread::WaitGS() if( volatize(m_RingPos) != m_WritePos ) { SetEvent(); + RethrowException(); + do { m_lock_RingBufferBusy.Wait(); + RethrowException(); } while( volatize(m_RingPos) != m_WritePos ); } } diff --git a/pcsx2/PluginManager.cpp b/pcsx2/PluginManager.cpp index ce7265681f..20a3ad5c97 100644 --- a/pcsx2/PluginManager.cpp +++ b/pcsx2/PluginManager.cpp @@ -867,19 +867,17 @@ void PluginManager::Load( PluginsEnum_t pid, const wxString& srcfile ) { ScopedLock lock( m_mtx_PluginStatus ); pxAssume( (uint)pid < PluginId_Count ); - Console.WriteLn( L"Binding %s\t: %s ", tbl_PluginInfo[pid].GetShortname().c_str(), srcfile.c_str() ); + Console.Indent().WriteLn( L"Binding %s\t: %s ", tbl_PluginInfo[pid].GetShortname().c_str(), srcfile.c_str() ); m_info[pid] = new PluginStatus_t( pid, srcfile ); } void PluginManager::Load( const wxString (&folders)[PluginId_Count] ) { - ScopedLock lock( m_mtx_PluginStatus ); - if( !NeedsLoad() ) return; wxDoNotLogInThisScope please; - Console.WriteLn( Color_StrongBlue, "Loading plugins..." ); + Console.WriteLn( Color_StrongBlue, "\nLoading plugins..." ); ConsoleIndentScope indent; const PluginInfo* pi = tbl_PluginInfo; do @@ -939,8 +937,6 @@ void PluginManager::Unload(PluginsEnum_t pid) void PluginManager::Unload() { - ScopedLock lock( m_mtx_PluginStatus ); - if( NeedsShutdown() ) Console.Warning( "(SysCorePlugins) Warning: Unloading plugins prior to shutdown!" ); @@ -954,7 +950,6 @@ void PluginManager::Unload() Unload( tbl_PluginInfo[i].id ); DbgCon.WriteLn( Color_StrongBlue, "Plugins unloaded successfully." ); - } // Exceptions: @@ -1167,6 +1162,29 @@ void PluginManager::Close() DbgCon.WriteLn( Color_StrongBlue, "Plugins closed successfully." ); } +void PluginManager::Init( PluginsEnum_t pid ) +{ + ScopedLock lock( m_mtx_PluginStatus ); + + if( !m_info[pid] || m_info[pid]->IsInitialized ) return; + + Console.Indent().WriteLn( "Init %s", tbl_PluginInfo[pid].shortname ); + if( NULL != m_info[pid]->CommonBindings.Init() ) + throw Exception::PluginInitError( pid ); + + m_info[pid]->IsInitialized = true; +} + +void PluginManager::Shutdown( PluginsEnum_t pid ) +{ + ScopedLock lock( m_mtx_PluginStatus ); + + if( !m_info[pid] || !m_info[pid]->IsInitialized ) return; + DevCon.Indent().WriteLn( "Shutdown %s", tbl_PluginInfo[pid].shortname ); + m_info[pid]->IsInitialized = false; + m_info[pid]->CommonBindings.Shutdown(); +} + // Initializes all plugins. Plugin initialization should be done once for every new emulation // session. During a session emulation can be paused/resumed using Open/Close, and should be // terminated using Shutdown(). @@ -1177,26 +1195,11 @@ void PluginManager::Close() // void PluginManager::Init() { - ScopedLock lock( m_mtx_PluginStatus ); - if( !NeedsInit() ) return; - bool printlog = false; - const PluginInfo* pi = tbl_PluginInfo; do - { - const PluginsEnum_t pid = pi->id; - - if( !m_info[pid] || m_info[pid]->IsInitialized ) continue; - if( !printlog ) - { - Console.WriteLn( Color_StrongBlue, "Initializing plugins..." ); - printlog = true; - } - Console.Indent().WriteLn( "Init %s", tbl_PluginInfo[pid].shortname ); - if( 0 != m_info[pid]->CommonBindings.Init() ) - throw Exception::PluginInitError( pid ); - - m_info[pid]->IsInitialized = true; + Console.WriteLn( Color_StrongBlue, "\nInitializing plugins..." ); + const PluginInfo* pi = tbl_PluginInfo; do { + Init( pi->id ); } while( ++pi, pi->shortname != NULL ); if( SysPlugins.Mcd == NULL ) @@ -1209,10 +1212,10 @@ void PluginManager::Init() } } - if( printlog ) - Console.WriteLn( Color_StrongBlue, "Plugins initialized successfully.\n" ); + Console.WriteLn( Color_StrongBlue, "Plugins initialized successfully.\n" ); } + // Shuts down all plugins. Plugins are closed first, if necessary. // // In a purist emulation sense, Init() and Shutdown() should only ever need be called for when @@ -1221,7 +1224,6 @@ void PluginManager::Init() // void PluginManager::Shutdown() { - ScopedLock lock( m_mtx_PluginStatus ); if( !NeedsShutdown() ) return; pxAssumeDev( !NeedsClose(), "Cannot shut down plugins prior to Close()" ); @@ -1235,11 +1237,7 @@ void PluginManager::Shutdown() for( int i=PluginId_Count-1; i>=0; --i ) { - const PluginsEnum_t pid = tbl_PluginInfo[i].id; - if( !m_info[pid] || !m_info[pid]->IsInitialized ) continue; - DevCon.Indent().WriteLn( "Shutdown %s", tbl_PluginInfo[pid].shortname ); - m_info[pid]->IsInitialized = false; - m_info[pid]->CommonBindings.Shutdown(); + Shutdown( tbl_PluginInfo[i].id ); } // More memorycard hacks!! @@ -1448,6 +1446,8 @@ bool PluginManager::NeedsUnload() const bool PluginManager::NeedsInit() const { + ScopedLock lock( m_mtx_PluginStatus ); + const PluginInfo* pi = tbl_PluginInfo; do { if( !IsInitialized(pi->id) ) return true; } while( ++pi, pi->shortname != NULL ); @@ -1457,6 +1457,8 @@ bool PluginManager::NeedsInit() const bool PluginManager::NeedsShutdown() const { + ScopedLock lock( m_mtx_PluginStatus ); + const PluginInfo* pi = tbl_PluginInfo; do { if( IsInitialized(pi->id) ) return true; } while( ++pi, pi->shortname != NULL ); diff --git a/pcsx2/Plugins.h b/pcsx2/Plugins.h index 4e00ece7b1..6750ceae3e 100644 --- a/pcsx2/Plugins.h +++ b/pcsx2/Plugins.h @@ -275,10 +275,10 @@ public: PluginManager(); virtual ~PluginManager() throw(); - void Load( PluginsEnum_t pid, const wxString& srcfile ); - void Load( const wxString (&folders)[PluginId_Count] ); - void Unload(); - void Unload( PluginsEnum_t pid ); + virtual void Load( PluginsEnum_t pid, const wxString& srcfile ); + virtual void Load( const wxString (&folders)[PluginId_Count] ); + virtual void Unload(); + virtual void Unload( PluginsEnum_t pid ); bool AreLoaded() const; bool AreAnyLoaded() const; @@ -287,6 +287,8 @@ public: Threading::Mutex& GetMutex() { return m_mtx_PluginStatus; } virtual void Init(); + virtual void Init( PluginsEnum_t pid ); + virtual void Shutdown( PluginsEnum_t pid ); virtual void Shutdown(); virtual void Open(); virtual void Open( PluginsEnum_t pid ); diff --git a/pcsx2/gui/AppCorePlugins.cpp b/pcsx2/gui/AppCorePlugins.cpp index dc4bf49801..581fcc20bf 100644 --- a/pcsx2/gui/AppCorePlugins.cpp +++ b/pcsx2/gui/AppCorePlugins.cpp @@ -98,46 +98,8 @@ static void ConvertPluginFilenames( wxString (&passins)[PluginId_Count] ) } while( ++pi, pi->shortname != NULL ); } -// -------------------------------------------------------------------------------------- -// AppPluginManager -// -------------------------------------------------------------------------------------- -AppPluginManager::AppPluginManager() -{ -} - -AppPluginManager::~AppPluginManager() throw() -{ -} - -void AppPluginManager::Load( const wxString (&folders)[PluginId_Count] ) -{ - if( !pxAssert(!AreLoaded()) ) return; - - SetSettingsFolder( GetSettingsFolder().ToString() ); - _parent::Load( folders ); - PostPluginStatus( CorePlugins_Loaded ); -} - -void AppPluginManager::Unload() -{ - _parent::Unload(); - PostPluginStatus( CorePlugins_Unloaded ); -} - -void AppPluginManager::Init() -{ - SetSettingsFolder( GetSettingsFolder().ToString() ); - _parent::Init(); - PostPluginStatus( CorePlugins_Init ); -} - -void AppPluginManager::Shutdown() -{ - _parent::Shutdown(); - PostPluginStatus( CorePlugins_Shutdown ); -} - typedef void (AppPluginManager::*FnPtr_AppPluginManager)(); +typedef void (AppPluginManager::*FnPtr_AppPluginPid)( PluginsEnum_t pid ); class SysExecEvent_AppPluginManager : public SysExecEvent { @@ -160,6 +122,162 @@ protected: } }; +class LoadSinglePluginEvent : public pxInvokeActionEvent +{ + typedef pxInvokeActionEvent _parent; + DECLARE_DYNAMIC_CLASS_NO_ASSIGN(LoadSinglePluginEvent) + +protected: + wxString m_filename; + PluginsEnum_t m_pid; + +public: + virtual ~LoadSinglePluginEvent() throw() { } + virtual LoadSinglePluginEvent *Clone() const { return new LoadSinglePluginEvent(*this); } + + LoadSinglePluginEvent( PluginsEnum_t pid = PluginId_GS, const wxString& filename=wxEmptyString ) + : m_filename( filename ) + { + m_pid = pid; + } + +protected: + void InvokeEvent() + { + GetCorePlugins().Load( m_pid, m_filename ); + } +}; + +class SinglePluginMethodEvent : public pxInvokeActionEvent +{ + typedef pxInvokeActionEvent _parent; + DECLARE_DYNAMIC_CLASS_NO_ASSIGN(SinglePluginMethodEvent) + +protected: + PluginsEnum_t m_pid; + FnPtr_AppPluginPid m_method; + +public: + virtual ~SinglePluginMethodEvent() throw() { } + virtual SinglePluginMethodEvent *Clone() const { return new SinglePluginMethodEvent(*this); } + + SinglePluginMethodEvent( FnPtr_AppPluginPid method=NULL, PluginsEnum_t pid = PluginId_GS ) + { + m_pid = pid; + m_method = method; + } + +protected: + void InvokeEvent() + { + //GetCorePlugins().Unload( m_pid ); + if( m_method ) (CorePlugins.*m_method)( m_pid ); + } +}; + +IMPLEMENT_DYNAMIC_CLASS( LoadSinglePluginEvent, pxInvokeActionEvent ); +IMPLEMENT_DYNAMIC_CLASS( SinglePluginMethodEvent, pxInvokeActionEvent ); + +// -------------------------------------------------------------------------------------- +// AppPluginManager +// -------------------------------------------------------------------------------------- +// +// Thread Affinity Notes: +// It's important to ensure that Load/Unload/Init/Shutdown are all called from the +// MAIN/UI Thread only. Those APIs are allowed to issue modal popups, and as such +// are only safe when invoked form the UI thread. Under windows the popups themselves +// will typically work from any thread, but some common control activities will fail +// (such as opening the browser windows). On Linux it's probably just highly unsafe, period. +// +// My implementation is to execute the main Load/Init/Shutdown/Unload procedure on the +// SysExecutor, and then dispatch each individual plugin to the main thread. This keeps +// the main thread from being completely busy while plugins are loaded and initialized. +// (responsiveness is bliss!!) -- air +// +AppPluginManager::AppPluginManager() +{ +} + +AppPluginManager::~AppPluginManager() throw() +{ +} + +void AppPluginManager::Load( PluginsEnum_t pid, const wxString& srcfile ) +{ + if( !wxThread::IsMain() ) + { + wxGetApp().ProcessAction( LoadSinglePluginEvent( pid, srcfile ) ); + Sleep( 5 ); + return; + } + + _parent::Load( pid, srcfile ); +} + +void AppPluginManager::Unload( PluginsEnum_t pid ) +{ + if( !wxThread::IsMain() ) + { + wxGetApp().ProcessAction( SinglePluginMethodEvent( &AppPluginManager::Unload, pid ) ); + Sleep( 5 ); + return; + } + + _parent::Unload( pid ); +} + +void AppPluginManager::Load( const wxString (&folders)[PluginId_Count] ) +{ + if( !pxAssert(!AreLoaded()) ) return; + + SetSettingsFolder( GetSettingsFolder().ToString() ); + _parent::Load( folders ); + PostPluginStatus( CorePlugins_Loaded ); +} + +void AppPluginManager::Unload() +{ + _parent::Unload(); + PostPluginStatus( CorePlugins_Unloaded ); +} + +void AppPluginManager::Init( PluginsEnum_t pid ) +{ + if( !wxThread::IsMain() ) + { + wxGetApp().ProcessAction( SinglePluginMethodEvent( &AppPluginManager::Init, pid ) ); + Sleep( 5 ); + return; + } + + _parent::Init( pid ); +} + +void AppPluginManager::Shutdown( PluginsEnum_t pid ) +{ + if( !wxThread::IsMain() ) + { + wxGetApp().ProcessAction( SinglePluginMethodEvent( &AppPluginManager::Shutdown, pid ) ); + Sleep( 5 ); + return; + } + + _parent::Shutdown( pid ); +} + +void AppPluginManager::Init() +{ + SetSettingsFolder( GetSettingsFolder().ToString() ); + _parent::Init(); + PostPluginStatus( CorePlugins_Init ); +} + +void AppPluginManager::Shutdown() +{ + _parent::Shutdown(); + PostPluginStatus( CorePlugins_Shutdown ); +} + void AppPluginManager::Close() { AffinityAssert_AllowFrom_CoreThread(); @@ -413,8 +531,8 @@ SaveSinglePluginHelper::~SaveSinglePluginHelper() throw() Console.WriteLn( Color_Green, L"Recovering single plugin: " + tbl_PluginInfo[m_pid].GetShortname() ); memLoadingState load( m_plugstore ); //if( m_plugstore.IsDisposed() ) load.SeekToSection( m_pid ); + GetCorePlugins().Open( m_pid ); GetCorePlugins().Freeze( m_pid, load ); - GetCorePlugins().Close( m_pid ); } s_DisableGsWindow = false; @@ -422,14 +540,18 @@ SaveSinglePluginHelper::~SaveSinglePluginHelper() throw() catch( BaseException& ex ) { allowResume = false; - Console.Error( "Unhandled BaseException in %s (ignored!):", __pxFUNCTION__ ); - Console.Error( ex.FormatDiagnosticMessage() ); + wxGetApp().PostEvent( pxExceptionEvent( ex ) ); + + //Console.Error( "Unhandled BaseException in %s (ignored!):", __pxFUNCTION__ ); + //Console.Error( ex.FormatDiagnosticMessage() ); } catch( std::exception& ex ) { allowResume = false; - Console.Error( "Unhandled std::exception in %s (ignored!):", __pxFUNCTION__ ); - Console.Error( ex.what() ); + wxGetApp().PostEvent( pxExceptionEvent(new Exception::RuntimeError( ex, L"SaveSinglePlugin" )) ); + + //Console.Error( "Unhandled std::exception in %s (ignored!):", __pxFUNCTION__ ); + //Console.Error( ex.what() ); } s_DisableGsWindow = false; diff --git a/pcsx2/gui/AppCorePlugins.h b/pcsx2/gui/AppCorePlugins.h index 8703c341d5..c9ef5711df 100644 --- a/pcsx2/gui/AppCorePlugins.h +++ b/pcsx2/gui/AppCorePlugins.h @@ -35,9 +35,13 @@ public: 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(); diff --git a/pcsx2/gui/AppInit.cpp b/pcsx2/gui/AppInit.cpp index 99ad4ea690..8e5d0bb3be 100644 --- a/pcsx2/gui/AppInit.cpp +++ b/pcsx2/gui/AppInit.cpp @@ -488,7 +488,7 @@ void Pcsx2App::CleanupRestartable() //PingDispatcher( "Cleanup" ); //DeletionDispatcher(); - IdleEventDispatcher( "Cleanup" ); + IdleEventDispatcher( L"Cleanup" ); if( g_Conf ) AppSaveSettings();