From 2b3d596d867dcf6eb2ed9d7b0a4923041ca62ce2 Mon Sep 17 00:00:00 2001 From: "Jake.Stine" Date: Thu, 17 Sep 2009 16:49:04 +0000 Subject: [PATCH] Minor fixes for interlocking against the threaded plugin loader (running a game too quickly could cause crashes) git-svn-id: http://pcsx2.googlecode.com/svn/trunk@1843 96395faa-99c1-11dd-bbfe-3dabce05a288 --- pcsx2/gui/App.h | 2 + pcsx2/gui/AppMain.cpp | 17 ++-- pcsx2/gui/MainFrame.cpp | 11 +-- pcsx2/gui/MainFrame.h | 2 +- pcsx2/gui/Plugins.cpp | 177 ++++++++++++++++++++++++---------------- 5 files changed, 124 insertions(+), 85 deletions(-) diff --git a/pcsx2/gui/App.h b/pcsx2/gui/App.h index ca9e799702..fedcf73262 100644 --- a/pcsx2/gui/App.h +++ b/pcsx2/gui/App.h @@ -36,6 +36,7 @@ BEGIN_DECLARE_EVENT_TYPES() DECLARE_EVENT_TYPE( pxEVT_SemaphorePing, -1 ) DECLARE_EVENT_TYPE( pxEVT_OpenModalDialog, -1 ) DECLARE_EVENT_TYPE( pxEVT_ReloadPlugins, -1 ) + DECLARE_EVENT_TYPE( pxEVT_LoadPluginsComplete, -1 ) END_DECLARE_EVENT_TYPES() // ------------------------------------------------------------------------ @@ -365,6 +366,7 @@ protected: void HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent& event) const; void OnReloadPlugins( wxCommandEvent& evt ); + void OnLoadPluginsComplete( wxCommandEvent& evt ); void OnSemaphorePing( wxCommandEvent& evt ); void OnOpenModalDialog( wxCommandEvent& evt ); void OnMessageBox( pxMessageBoxEvent& evt ); diff --git a/pcsx2/gui/AppMain.cpp b/pcsx2/gui/AppMain.cpp index 61741ee988..04dfc0ebae 100644 --- a/pcsx2/gui/AppMain.cpp +++ b/pcsx2/gui/AppMain.cpp @@ -27,13 +27,14 @@ #include #include -#include +#include IMPLEMENT_APP(Pcsx2App) DEFINE_EVENT_TYPE( pxEVT_SemaphorePing ); DEFINE_EVENT_TYPE( pxEVT_OpenModalDialog ); DEFINE_EVENT_TYPE( pxEVT_ReloadPlugins ); +DEFINE_EVENT_TYPE( pxEVT_LoadPluginsComplete ); bool UseAdminMode = false; wxDirName SettingsFolder; @@ -427,6 +428,7 @@ bool Pcsx2App::OnInit() Connect( pxEVT_SemaphorePing, wxCommandEventHandler( Pcsx2App::OnSemaphorePing ) ); Connect( pxEVT_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) ); Connect( pxEVT_ReloadPlugins, wxCommandEventHandler( Pcsx2App::OnReloadPlugins ) ); + Connect( pxEVT_LoadPluginsComplete, wxCommandEventHandler( Pcsx2App::OnLoadPluginsComplete ) ); Connect( pxID_Window_GS, wxEVT_KEY_DOWN, wxKeyEventHandler( Pcsx2App::OnEmuKeyDown ) ); @@ -630,12 +632,13 @@ void Pcsx2App::OnMessageBox( pxMessageBoxEvent& evt ) Msgbox::OnEvent( evt ); } -#include - void Pcsx2App::CleanupMess() { - m_CorePlugins->Close(); - m_CorePlugins->Shutdown(); + if( m_CorePlugins ) + { + m_CorePlugins->Close(); + m_CorePlugins->Shutdown(); + } // Notice: deleting the plugin manager (unloading plugins) here causes Lilypad to crash, // likely due to some pending message in the queue that references lilypad procs. @@ -768,7 +771,7 @@ void Pcsx2App::LoadSettings() IniLoader loader( *conf ); g_Conf->LoadSave( loader ); - if( m_MainFrame != NULL ) + if( m_MainFrame != NULL && m_MainFrame->m_RecentIsoList ) m_MainFrame->m_RecentIsoList->Load( *conf ); } @@ -780,6 +783,6 @@ void Pcsx2App::SaveSettings() IniSaver saver( *conf ); g_Conf->LoadSave( saver ); - if( m_MainFrame != NULL ) + if( m_MainFrame != NULL && m_MainFrame->m_RecentIsoList ) m_MainFrame->m_RecentIsoList->Save( *conf ); } diff --git a/pcsx2/gui/MainFrame.cpp b/pcsx2/gui/MainFrame.cpp index dcb7654650..13691b0390 100644 --- a/pcsx2/gui/MainFrame.cpp +++ b/pcsx2/gui/MainFrame.cpp @@ -432,11 +432,8 @@ MainEmuFrame::~MainEmuFrame() throw() { try { - if( m_RecentIsoList != NULL ) - { + if( m_RecentIsoList ) m_RecentIsoList->Save( *wxConfigBase::Get( false ) ); - safe_delete( m_RecentIsoList ); - } } DESTRUCTOR_CATCHALL } @@ -450,10 +447,10 @@ void MainEmuFrame::ApplySettings() wxConfigBase* cfg = wxConfigBase::Get( false ); wxASSERT( cfg != NULL ); - if( m_RecentIsoList != NULL ) + if( m_RecentIsoList ) m_RecentIsoList->Save( *cfg ); - safe_delete( m_RecentIsoList ); - m_RecentIsoList = new wxFileHistory( g_Conf->RecentFileCount ); + m_RecentIsoList.reset(); + m_RecentIsoList.reset( new wxFileHistory( g_Conf->RecentFileCount ) ); m_RecentIsoList->Load( *cfg ); UpdateIsoSrcFile(); diff --git a/pcsx2/gui/MainFrame.h b/pcsx2/gui/MainFrame.h index de5e9b5874..f5b115634c 100644 --- a/pcsx2/gui/MainFrame.h +++ b/pcsx2/gui/MainFrame.h @@ -37,7 +37,7 @@ class MainEmuFrame : public wxFrame // ------------------------------------------------------------------------ protected: - wxFileHistory* m_RecentIsoList; + wxScopedPtr m_RecentIsoList; wxStatusBar& m_statusbar; wxStaticBitmap m_background; diff --git a/pcsx2/gui/Plugins.cpp b/pcsx2/gui/Plugins.cpp index db13783456..04eaf186c7 100644 --- a/pcsx2/gui/Plugins.cpp +++ b/pcsx2/gui/Plugins.cpp @@ -25,6 +25,90 @@ #include "HostGui.h" #include "AppConfig.h" +using namespace Threading; + +// -------------------------------------------------------------------------------------- +// LoadPluginsTask +// -------------------------------------------------------------------------------------- +// On completion the thread sends a pxEVT_LoadPluginsComplete message, which contains a +// handle to this thread object. If the load is successful, the Result var is set to +// non-NULL. If NULL, an error occurred and the thread loads the exception into either +// Ex_PluginError or Ex_RuntimeError. +// +class LoadPluginsTask : public Threading::PersistentThread +{ +public: + Exception::PluginError* Ex_PluginError; + Exception::RuntimeError* Ex_RuntimeError; + PluginManager* Result; + +protected: + wxString m_folders[PluginId_Count]; + +public: + LoadPluginsTask( const wxString (&folders)[PluginId_Count] ) : + Ex_PluginError( NULL ) + , Ex_RuntimeError( NULL ) + , Result( NULL ) + { + for(int i=0; i _loadTask; + +LoadPluginsTask::~LoadPluginsTask() throw() +{ + _loadTask.release(); + PersistentThread::Cancel(); + _loadTask.reset(); +} + +int LoadPluginsTask::ExecuteTask() +{ + wxGetApp().Ping(); + Sleep(3); + + wxCommandEvent evt( pxEVT_LoadPluginsComplete ); + evt.SetClientData( this ); + + try + { + // This is for testing of the error handler... uncomment for fun? + //throw Exception::PluginError( PluginId_PAD, "This one is for testing the error handler!" ); + + Result = PluginManager_Create( m_folders ); + } + catch( Exception::PluginError& ex ) + { + Ex_PluginError = new Exception::PluginError( ex ); + } + catch( Exception::RuntimeError& innerEx ) + { + // Runtime errors are typically recoverable, so handle them here + // and prep them for re-throw on the main thread. + Ex_RuntimeError = new Exception::RuntimeError( + L"A runtime error occurred on the LoadPlugins thread.\n" + innerEx.FormatDiagnosticMessage(), + innerEx.FormatDisplayMessage() + ); + } + // anything else leave unhandled so that the debugger catches it! + + wxGetApp().AddPendingEvent( evt ); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + + int EnumeratePluginsInFolder( const wxDirName& searchpath, wxArrayString* dest ) { wxScopedPtr placebo; @@ -45,69 +129,29 @@ int EnumeratePluginsInFolder( const wxDirName& searchpath, wxArrayString* dest ) wxDir::GetAllFiles( searchpath.ToString(), realdest, wxsFormat( pattern, wxDynamicLibrary::GetDllExt()), wxDIR_FILES ) : 0; } -using namespace Threading; - void Pcsx2App::OnReloadPlugins( wxCommandEvent& evt ) { ReloadPlugins(); } +void Pcsx2App::OnLoadPluginsComplete( wxCommandEvent& evt ) +{ + // scoped ptr ensures the thread object is cleaned up even on exception: + wxScopedPtr killTask( (LoadPluginsTask*)evt.GetClientData() ); + m_CorePlugins.reset( killTask->Result ); + + if( !m_CorePlugins ) + { + if( killTask->Ex_PluginError != NULL ) + throw *killTask->Ex_PluginError; + if( killTask->Ex_RuntimeError != NULL ) + throw *killTask->Ex_RuntimeError; // Re-Throws generic threaded errors + } +} + void Pcsx2App::ReloadPlugins() { - class LoadPluginsTask : public Threading::BaseTaskThread - { - public: - PluginManager* Result; - Exception::PluginError* Ex_PluginError; - Exception::RuntimeError* Ex_RuntimeError; - - protected: - const wxString (&m_folders)[PluginId_Count]; - - public: - LoadPluginsTask( const wxString (&folders)[PluginId_Count] ) : - Result( NULL ) - , Ex_PluginError( NULL ) - , Ex_RuntimeError( NULL ) - , m_folders( folders ) - { - } - - virtual ~LoadPluginsTask() throw() - { - BaseTaskThread::Cancel(); - } - - protected: - void Task() - { - wxGetApp().Ping(); - Sleep(3); - - try - { - //throw Exception::PluginError( PluginId_PAD, "This one is for testing the error handler!" ); - Result = PluginManager_Create( m_folders ); - } - catch( Exception::PluginError& ex ) - { - Result = NULL; - Ex_PluginError = new Exception::PluginError( ex ); - } - catch( Exception::RuntimeError& innerEx ) - { - // Runtime errors are typically recoverable, so handle them here - // and prep them for re-throw on the main thread. - Result = NULL; - Ex_RuntimeError = new Exception::RuntimeError( - L"A runtime error occurred on the LoadPlugins thread.\n" + innerEx.FormatDiagnosticMessage(), - innerEx.FormatDisplayMessage() - ); - } - - // anything else leave unhandled so that the debugger catches it! - } - }; + if( _loadTask ) return; m_CoreThread.reset(); m_CorePlugins.reset(); @@ -123,20 +167,8 @@ void Pcsx2App::ReloadPlugins() passins[pi->id] = g_Conf->FullpathTo( pi->id ); } - LoadPluginsTask task( passins ); - task.Start(); - task.PostTask(); - task.WaitForResult(); - - if( task.Result == NULL ) - { - if( task.Ex_PluginError != NULL ) - throw *task.Ex_PluginError; - if( task.Ex_RuntimeError != NULL ) - throw *task.Ex_RuntimeError; // Re-Throws generic threaded errors - } - - m_CorePlugins.reset( task.Result ); + _loadTask.reset( new LoadPluginsTask( passins ) ); + // ... and when it finishes it posts up a OnLoadPluginsComplete(). Bye. :) } // Posts a message to the App to reload plugins. Plugins are loaded via a background thread @@ -158,6 +190,11 @@ void LoadPluginsImmediate() wxASSERT( !guard.IsReentrant() ); wxGetApp().ReloadPlugins(); + while( _loadTask ) + { + Sleep( 10 ); + wxGetApp().ProcessPendingEvents(); + } } void UnloadPlugins()