* Fixed death-by-recursion caused when a thread threw an exception.

* Fixed some minor bugs when processing idle events.
 * Plugin Load/Init/Shutdown/Unload are all called from the Main/UI thread again; this is important for plugins that issue popup warning/error messages.

DevNotes:
 * Added ScopedPtrMT, a multithread-safe version of ScopedPtr.
 * MTGS errors are still not handled as gracefully as they should be; namely the MTGS thread doesn't restart itself (it's on the TODO list).

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2958 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2010-05-07 03:20:58 +00:00
parent 0918cb42a3
commit 12bdd9dc10
16 changed files with 467 additions and 124 deletions

View File

@ -537,6 +537,10 @@
RelativePath="..\..\include\Utilities\ScopedPtr.h" RelativePath="..\..\include\Utilities\ScopedPtr.h"
> >
</File> </File>
<File
RelativePath="..\..\include\Utilities\ScopedPtrMT.h"
>
</File>
<File <File
RelativePath="..\..\include\Utilities\StringHelpers.h" RelativePath="..\..\include\Utilities\StringHelpers.h"
> >

View File

@ -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.") ) 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::runtime_error& ex, const wxString& prefix=wxEmptyString );
RuntimeError( const std::exception& ex, const wxString& prefix=wxEmptyString );
}; };
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------

View File

@ -16,6 +16,7 @@
#pragma once #pragma once
#include "Threading.h" #include "Threading.h"
#include "ScopedPtrMT.h"
#include "EventSource.h" #include "EventSource.h"
namespace Threading namespace Threading
@ -130,7 +131,7 @@ namespace Threading
// exception handle, set non-NULL if the thread terminated with an exception // exception handle, set non-NULL if the thread terminated with an exception
// Use RethrowException() to re-throw the exception using its original exception type. // Use RethrowException() to re-throw the exception using its original exception type.
ScopedPtr<BaseException> m_except; ScopedPtrMT<BaseException> m_except;
EventSource<EventListener_Thread> m_evtsrc_OnDelete; EventSource<EventListener_Thread> m_evtsrc_OnDelete;

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once #pragma once
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -15,10 +30,12 @@ protected:
public: public:
typedef T element_type; typedef T element_type;
wxEXPLICIT ScopedPtr(T * ptr = NULL) : m_ptr(ptr) { } wxEXPLICIT ScopedPtr(T * ptr = NULL)
{
m_ptr = ptr;
}
~ScopedPtr() throw() ~ScopedPtr() throw() { Delete(); }
{ Delete(); }
ScopedPtr& Reassign(T * ptr = NULL) ScopedPtr& Reassign(T * ptr = NULL)
{ {
@ -128,13 +145,14 @@ class ScopedArray
protected: protected:
T* m_array; T* m_array;
uint m_valid_range; uint m_valid_range;
public: public:
typedef T element_type; typedef T element_type;
wxEXPLICIT ScopedArray(T * ptr = NULL) : wxEXPLICIT ScopedArray(T * ptr = NULL) :
m_array(ptr)
, m_valid_range( 0xffffffff )
{ {
m_array = ptr;
m_valid_range = 0xffffffff;
} }
wxEXPLICIT ScopedArray( int size ) : wxEXPLICIT ScopedArray( int size ) :

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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;
}
};

View File

@ -156,15 +156,11 @@ namespace Threading
extern bool AtomicBitTestAndReset( volatile u32& bitset, u8 bit ); extern bool AtomicBitTestAndReset( volatile u32& bitset, u8 bit );
extern void* _AtomicExchangePointer( void * volatile * const target, void* const value ); extern void* _AtomicExchangePointer( volatile uptr& target, uptr value );
extern void* _AtomicCompareExchangePointer( void * volatile * const target, void* const value, void* const comparand ); extern void* _AtomicCompareExchangePointer( volatile uptr& target, uptr value, uptr comparand );
#define AtomicExchangePointer( target, value ) \
_InterlockedExchangePointer( &target, value )
#define AtomicCompareExchangePointer( target, value, comparand ) \
_InterlockedCompareExchangePointer( &target, value, 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. // 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) // Let's not use it. Use mutexes and semaphores instead to create waits. (Air)

View File

@ -43,6 +43,8 @@ public:
typedef void FnType_Void(); typedef void FnType_Void();
typedef std::list<wxEvent*> wxEventList;
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// wxAppWithHelpers // wxAppWithHelpers
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -53,8 +55,8 @@ class wxAppWithHelpers : public wxApp
DECLARE_DYNAMIC_CLASS(wxAppWithHelpers) DECLARE_DYNAMIC_CLASS(wxAppWithHelpers)
protected: protected:
std::vector<wxEvent*> m_IdleEventQueue; wxEventList m_IdleEventQueue;
Threading::Mutex m_IdleEventMutex; Threading::MutexRecursive m_IdleEventMutex;
wxTimer m_IdleEventTimer; wxTimer m_IdleEventTimer;
public: public:
@ -105,7 +107,7 @@ public:
bool ProcessEvent( pxInvokeActionEvent* evt ); bool ProcessEvent( pxInvokeActionEvent* evt );
protected: protected:
void IdleEventDispatcher( const char* action ); void IdleEventDispatcher( const wxChar* action );
void OnIdleEvent( wxIdleEvent& evt ); void OnIdleEvent( wxIdleEvent& evt );
void OnStartIdleEventTimer( wxEvent& evt ); void OnStartIdleEventTimer( wxEvent& evt );

View File

@ -174,8 +174,19 @@ wxString BaseException::FormatDiagnosticMessage() const
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
Exception::RuntimeError::RuntimeError( const std::runtime_error& ex, const wxString& prefix ) Exception::RuntimeError::RuntimeError( const std::runtime_error& ex, const wxString& prefix )
{ {
const wxString msg( wxsFormat( L"%sSTL Runtime Error: %s", const wxString msg( wxsFormat( L"STL Runtime Error%s: %s",
(prefix.IsEmpty() ? prefix.c_str() : wxsFormat(L"(%s) ", prefix.c_str()).c_str()), (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() fromUTF8( ex.what() ).c_str()
) ); ) );

View File

@ -380,8 +380,14 @@ void Threading::PersistentThread::AddListener( EventListener_Thread& evt )
// the thread will have allowed itself to terminate properly. // the thread will have allowed itself to terminate properly.
void Threading::PersistentThread::RethrowException() const void Threading::PersistentThread::RethrowException() const
{ {
if( !m_except ) return; // Thread safety note: always detach the m_except pointer. If we checked it for NULL, the
m_except->Rethrow(); // pointer might still be invalid after detachment, so might as well just detach and check
// after.
ScopedPtr<BaseException> ptr( const_cast<PersistentThread*>(this)->m_except.DetachPtr() );
if( ptr ) ptr->Rethrow();
//m_except->Rethrow();
} }
static bool m_BlockDeletions = false; static bool m_BlockDeletions = false;
@ -533,8 +539,9 @@ void Threading::PersistentThread::_try_virtual_invoke( void (PersistentThread::*
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
catch( Exception::RuntimeError& ex ) catch( Exception::RuntimeError& ex )
{ {
m_except = ex.Clone(); BaseException* woot = ex.Clone();
m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName().c_str() ) + m_except->DiagMsg(); woot->DiagMsg() += wxsFormat( L"(thread:%s)", GetName().c_str() );
m_except = woot;
} }
#ifndef PCSX2_DEVBUILD #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). // the MSVC debugger (or by silent random annoying fail on debug-less linux).
/*catch( std::logic_error& ex ) /*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() ) GetName().c_str(), fromUTF8( ex.what() ).c_str() )
); );
} }
catch( std::exception& ex ) 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() ) 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 ); 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 // BaseThreadError
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------

View File

@ -420,27 +420,37 @@ void wxAppWithHelpers::OnStartIdleEventTimer( wxEvent& evt )
m_IdleEventTimer.Start( 100, true ); 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 ); ScopedLock lock( m_IdleEventMutex );
size_t size = m_IdleEventQueue.size(); while( node = m_IdleEventQueue.begin(), node != m_IdleEventQueue.end() )
if( size == 0 ) return;
DbgCon.WriteLn( Color_Gray, "App IdleQueue (%s) -> %u events.", action, size );
std::vector<wxEvent*> postponed;
for( size_t i=0; i<size; ++i )
{ {
if( !Threading::AllowDeletions() && (m_IdleEventQueue[i]->GetEventType() == pxEvt_DeleteThread) ) ScopedPtr<wxEvent> deleteMe(*node);
postponed.push_back(m_IdleEventQueue[i]); m_IdleEventQueue.erase( node );
lock.Release();
if( !Threading::AllowDeletions() && (deleteMe->GetEventType() == pxEvt_DeleteThread) )
{
postponed.push_back(deleteMe.DetachPtr());
}
else else
{ {
lock.Release(); DbgCon.WriteLn( Color_Gray, L"(AppIdleQueue:%s) -> Dispatching event '%s'", action, deleteMe->GetClassInfo()->GetClassName() );
ProcessEvent( *m_IdleEventQueue[i] ); ProcessEvent( *deleteMe ); // dereference to prevent auto-deletion by ProcessEvent
lock.Acquire();
} }
lock.Acquire();
} }
m_IdleEventQueue = postponed; m_IdleEventQueue = postponed;
@ -449,12 +459,12 @@ void wxAppWithHelpers::IdleEventDispatcher( const char* action )
void wxAppWithHelpers::OnIdleEvent( wxIdleEvent& evt ) void wxAppWithHelpers::OnIdleEvent( wxIdleEvent& evt )
{ {
m_IdleEventTimer.Stop(); m_IdleEventTimer.Stop();
IdleEventDispatcher( "Idle" ); IdleEventDispatcher( L"Idle" );
} }
void wxAppWithHelpers::OnIdleEventTimeout( wxTimerEvent& evt ) void wxAppWithHelpers::OnIdleEventTimeout( wxTimerEvent& evt )
{ {
IdleEventDispatcher( "Timeout" ); IdleEventDispatcher( L"Timeout" );
} }
void wxAppWithHelpers::Ping() void wxAppWithHelpers::Ping()

View File

@ -523,8 +523,11 @@ void SysMtgsThread::WaitGS()
if( volatize(m_RingPos) != m_WritePos ) if( volatize(m_RingPos) != m_WritePos )
{ {
SetEvent(); SetEvent();
RethrowException();
do { do {
m_lock_RingBufferBusy.Wait(); m_lock_RingBufferBusy.Wait();
RethrowException();
} while( volatize(m_RingPos) != m_WritePos ); } while( volatize(m_RingPos) != m_WritePos );
} }
} }

View File

@ -867,19 +867,17 @@ void PluginManager::Load( PluginsEnum_t pid, const wxString& srcfile )
{ {
ScopedLock lock( m_mtx_PluginStatus ); ScopedLock lock( m_mtx_PluginStatus );
pxAssume( (uint)pid < PluginId_Count ); 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 ); m_info[pid] = new PluginStatus_t( pid, srcfile );
} }
void PluginManager::Load( const wxString (&folders)[PluginId_Count] ) void PluginManager::Load( const wxString (&folders)[PluginId_Count] )
{ {
ScopedLock lock( m_mtx_PluginStatus );
if( !NeedsLoad() ) return; if( !NeedsLoad() ) return;
wxDoNotLogInThisScope please; wxDoNotLogInThisScope please;
Console.WriteLn( Color_StrongBlue, "Loading plugins..." ); Console.WriteLn( Color_StrongBlue, "\nLoading plugins..." );
ConsoleIndentScope indent; ConsoleIndentScope indent;
const PluginInfo* pi = tbl_PluginInfo; do const PluginInfo* pi = tbl_PluginInfo; do
@ -939,8 +937,6 @@ void PluginManager::Unload(PluginsEnum_t pid)
void PluginManager::Unload() void PluginManager::Unload()
{ {
ScopedLock lock( m_mtx_PluginStatus );
if( NeedsShutdown() ) if( NeedsShutdown() )
Console.Warning( "(SysCorePlugins) Warning: Unloading plugins prior to shutdown!" ); Console.Warning( "(SysCorePlugins) Warning: Unloading plugins prior to shutdown!" );
@ -954,7 +950,6 @@ void PluginManager::Unload()
Unload( tbl_PluginInfo[i].id ); Unload( tbl_PluginInfo[i].id );
DbgCon.WriteLn( Color_StrongBlue, "Plugins unloaded successfully." ); DbgCon.WriteLn( Color_StrongBlue, "Plugins unloaded successfully." );
} }
// Exceptions: // Exceptions:
@ -1167,6 +1162,29 @@ void PluginManager::Close()
DbgCon.WriteLn( Color_StrongBlue, "Plugins closed successfully." ); 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 // 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 // session. During a session emulation can be paused/resumed using Open/Close, and should be
// terminated using Shutdown(). // terminated using Shutdown().
@ -1177,26 +1195,11 @@ void PluginManager::Close()
// //
void PluginManager::Init() void PluginManager::Init()
{ {
ScopedLock lock( m_mtx_PluginStatus );
if( !NeedsInit() ) return; if( !NeedsInit() ) return;
bool printlog = false; Console.WriteLn( Color_StrongBlue, "\nInitializing plugins..." );
const PluginInfo* pi = tbl_PluginInfo; do const PluginInfo* pi = tbl_PluginInfo; do {
{ Init( pi->id );
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;
} while( ++pi, pi->shortname != NULL ); } while( ++pi, pi->shortname != NULL );
if( SysPlugins.Mcd == 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. // 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 // 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() void PluginManager::Shutdown()
{ {
ScopedLock lock( m_mtx_PluginStatus );
if( !NeedsShutdown() ) return; if( !NeedsShutdown() ) return;
pxAssumeDev( !NeedsClose(), "Cannot shut down plugins prior to Close()" ); 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 ) for( int i=PluginId_Count-1; i>=0; --i )
{ {
const PluginsEnum_t pid = tbl_PluginInfo[i].id; Shutdown( 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();
} }
// More memorycard hacks!! // More memorycard hacks!!
@ -1448,6 +1446,8 @@ bool PluginManager::NeedsUnload() const
bool PluginManager::NeedsInit() const bool PluginManager::NeedsInit() const
{ {
ScopedLock lock( m_mtx_PluginStatus );
const PluginInfo* pi = tbl_PluginInfo; do { const PluginInfo* pi = tbl_PluginInfo; do {
if( !IsInitialized(pi->id) ) return true; if( !IsInitialized(pi->id) ) return true;
} while( ++pi, pi->shortname != NULL ); } while( ++pi, pi->shortname != NULL );
@ -1457,6 +1457,8 @@ bool PluginManager::NeedsInit() const
bool PluginManager::NeedsShutdown() const bool PluginManager::NeedsShutdown() const
{ {
ScopedLock lock( m_mtx_PluginStatus );
const PluginInfo* pi = tbl_PluginInfo; do { const PluginInfo* pi = tbl_PluginInfo; do {
if( IsInitialized(pi->id) ) return true; if( IsInitialized(pi->id) ) return true;
} while( ++pi, pi->shortname != NULL ); } while( ++pi, pi->shortname != NULL );

View File

@ -275,10 +275,10 @@ public:
PluginManager(); PluginManager();
virtual ~PluginManager() throw(); virtual ~PluginManager() throw();
void Load( PluginsEnum_t pid, const wxString& srcfile ); virtual void Load( PluginsEnum_t pid, const wxString& srcfile );
void Load( const wxString (&folders)[PluginId_Count] ); virtual void Load( const wxString (&folders)[PluginId_Count] );
void Unload(); virtual void Unload();
void Unload( PluginsEnum_t pid ); virtual void Unload( PluginsEnum_t pid );
bool AreLoaded() const; bool AreLoaded() const;
bool AreAnyLoaded() const; bool AreAnyLoaded() const;
@ -287,6 +287,8 @@ public:
Threading::Mutex& GetMutex() { return m_mtx_PluginStatus; } Threading::Mutex& GetMutex() { return m_mtx_PluginStatus; }
virtual void Init(); virtual void Init();
virtual void Init( PluginsEnum_t pid );
virtual void Shutdown( PluginsEnum_t pid );
virtual void Shutdown(); virtual void Shutdown();
virtual void Open(); virtual void Open();
virtual void Open( PluginsEnum_t pid ); virtual void Open( PluginsEnum_t pid );

View File

@ -98,46 +98,8 @@ static void ConvertPluginFilenames( wxString (&passins)[PluginId_Count] )
} while( ++pi, pi->shortname != NULL ); } 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_AppPluginManager)();
typedef void (AppPluginManager::*FnPtr_AppPluginPid)( PluginsEnum_t pid );
class SysExecEvent_AppPluginManager : public SysExecEvent 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() void AppPluginManager::Close()
{ {
AffinityAssert_AllowFrom_CoreThread(); AffinityAssert_AllowFrom_CoreThread();
@ -413,8 +531,8 @@ SaveSinglePluginHelper::~SaveSinglePluginHelper() throw()
Console.WriteLn( Color_Green, L"Recovering single plugin: " + tbl_PluginInfo[m_pid].GetShortname() ); Console.WriteLn( Color_Green, L"Recovering single plugin: " + tbl_PluginInfo[m_pid].GetShortname() );
memLoadingState load( m_plugstore ); memLoadingState load( m_plugstore );
//if( m_plugstore.IsDisposed() ) load.SeekToSection( m_pid ); //if( m_plugstore.IsDisposed() ) load.SeekToSection( m_pid );
GetCorePlugins().Open( m_pid );
GetCorePlugins().Freeze( m_pid, load ); GetCorePlugins().Freeze( m_pid, load );
GetCorePlugins().Close( m_pid );
} }
s_DisableGsWindow = false; s_DisableGsWindow = false;
@ -422,14 +540,18 @@ SaveSinglePluginHelper::~SaveSinglePluginHelper() throw()
catch( BaseException& ex ) catch( BaseException& ex )
{ {
allowResume = false; allowResume = false;
Console.Error( "Unhandled BaseException in %s (ignored!):", __pxFUNCTION__ ); wxGetApp().PostEvent( pxExceptionEvent( ex ) );
Console.Error( ex.FormatDiagnosticMessage() );
//Console.Error( "Unhandled BaseException in %s (ignored!):", __pxFUNCTION__ );
//Console.Error( ex.FormatDiagnosticMessage() );
} }
catch( std::exception& ex ) catch( std::exception& ex )
{ {
allowResume = false; allowResume = false;
Console.Error( "Unhandled std::exception in %s (ignored!):", __pxFUNCTION__ ); wxGetApp().PostEvent( pxExceptionEvent(new Exception::RuntimeError( ex, L"SaveSinglePlugin" )) );
Console.Error( ex.what() );
//Console.Error( "Unhandled std::exception in %s (ignored!):", __pxFUNCTION__ );
//Console.Error( ex.what() );
} }
s_DisableGsWindow = false; s_DisableGsWindow = false;

View File

@ -35,9 +35,13 @@ public:
virtual ~AppPluginManager() throw(); virtual ~AppPluginManager() throw();
void Load( const wxString (&folders)[PluginId_Count] ); void Load( const wxString (&folders)[PluginId_Count] );
void Load( PluginsEnum_t pid, const wxString& srcfile );
void Unload( PluginsEnum_t pid );
void Unload(); void Unload();
void Init(); void Init();
void Init( PluginsEnum_t pid );
void Shutdown( PluginsEnum_t pid );
void Shutdown(); void Shutdown();
void Close(); void Close();
void Open(); void Open();

View File

@ -488,7 +488,7 @@ void Pcsx2App::CleanupRestartable()
//PingDispatcher( "Cleanup" ); //PingDispatcher( "Cleanup" );
//DeletionDispatcher(); //DeletionDispatcher();
IdleEventDispatcher( "Cleanup" ); IdleEventDispatcher( L"Cleanup" );
if( g_Conf ) if( g_Conf )
AppSaveSettings(); AppSaveSettings();