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
This commit is contained in:
Jake.Stine 2009-09-17 16:49:04 +00:00
parent e5da378d9a
commit 2b3d596d86
5 changed files with 124 additions and 85 deletions

View File

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

View File

@ -27,13 +27,14 @@
#include <wx/cmdline.h>
#include <wx/stdpaths.h>
#include <wx/evtloop.h>
#include <wx/intl.h>
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 <wx/intl.h>
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 );
}

View File

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

View File

@ -37,7 +37,7 @@ class MainEmuFrame : public wxFrame
// ------------------------------------------------------------------------
protected:
wxFileHistory* m_RecentIsoList;
wxScopedPtr<wxFileHistory> m_RecentIsoList;
wxStatusBar& m_statusbar;
wxStaticBitmap m_background;

View File

@ -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<PluginId_Count; ++i )
m_folders[i] = folders[i];
Start();
}
virtual ~LoadPluginsTask() throw();
protected:
int ExecuteTask();
};
static wxScopedPtr<LoadPluginsTask> _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<wxArrayString> 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<LoadPluginsTask> 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()