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