* 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"
>
</File>
<File
RelativePath="..\..\include\Utilities\ScopedPtrMT.h"
>
</File>
<File
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.") )
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
#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<BaseException> m_except;
ScopedPtrMT<BaseException> m_except;
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
// --------------------------------------------------------------------------------------
@ -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 ) :

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 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)

View File

@ -43,6 +43,8 @@ public:
typedef void FnType_Void();
typedef std::list<wxEvent*> wxEventList;
// --------------------------------------------------------------------------------------
// wxAppWithHelpers
// --------------------------------------------------------------------------------------
@ -53,8 +55,8 @@ class wxAppWithHelpers : public wxApp
DECLARE_DYNAMIC_CLASS(wxAppWithHelpers)
protected:
std::vector<wxEvent*> m_IdleEventQueue;
Threading::Mutex m_IdleEventMutex;
wxEventList m_IdleEventQueue;
Threading::MutexRecursive m_IdleEventMutex;
wxTimer m_IdleEventTimer;
public:
@ -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 );

View File

@ -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()
) );

View File

@ -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<BaseException> ptr( const_cast<PersistentThread*>(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
// --------------------------------------------------------------------------------------

View File

@ -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<wxEvent*> postponed;
for( size_t i=0; i<size; ++i )
while( node = m_IdleEventQueue.begin(), node != m_IdleEventQueue.end() )
{
if( !Threading::AllowDeletions() && (m_IdleEventQueue[i]->GetEventType() == pxEvt_DeleteThread) )
postponed.push_back(m_IdleEventQueue[i]);
ScopedPtr<wxEvent> 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()

View File

@ -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 );
}
}

View File

@ -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" );
}
// 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 );

View File

@ -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 );

View File

@ -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;

View File

@ -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();

View File

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