* Documented some of the event/threading proxy class and its underlying event queue.

* Simplified and improved (slightly) the savestate memory cleanup on error/cancellation.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@4158 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2010-12-27 15:25:39 +00:00
parent ae61d6010c
commit a7fcc3929e
7 changed files with 234 additions and 212 deletions

View File

@ -1,143 +1,143 @@
/* PCSX2 - PS2 Emulator for PCs /* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team * Copyright (C) 2002-2010 PCSX2 Dev Team
* *
* PCSX2 is free software: you can redistribute it and/or modify it under the terms * 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- * 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. * 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; * 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 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details. * 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. * You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>. * If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "Threading.h" #include "Threading.h"
using Threading::ScopedLock; using Threading::ScopedLock;
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// ScopedPtrMT // ScopedPtrMT
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
template< typename T > template< typename T >
class ScopedPtrMT class ScopedPtrMT
{ {
DeclareNoncopyableObject(ScopedPtrMT); DeclareNoncopyableObject(ScopedPtrMT);
typedef T* TPtr; typedef T* TPtr;
protected: protected:
volatile TPtr m_ptr; volatile TPtr m_ptr;
Threading::Mutex m_mtx; Threading::Mutex m_mtx;
public: public:
typedef T element_type; typedef T element_type;
wxEXPLICIT ScopedPtrMT(T * ptr = NULL) wxEXPLICIT ScopedPtrMT(T * ptr = NULL)
{ {
m_ptr = ptr; m_ptr = ptr;
} }
~ScopedPtrMT() throw() { _Delete_unlocked(); } ~ScopedPtrMT() throw() { _Delete_unlocked(); }
ScopedPtrMT& Reassign(T * ptr = NULL) ScopedPtrMT& Reassign(T * ptr = NULL)
{ {
TPtr doh = (TPtr)Threading::AtomicExchangePointer( m_ptr, ptr ); TPtr doh = (TPtr)Threading::AtomicExchangePointer( m_ptr, ptr );
if ( ptr != doh ) delete doh; if ( ptr != doh ) delete doh;
return *this; return *this;
} }
ScopedPtrMT& Delete() throw() ScopedPtrMT& Delete() throw()
{ {
ScopedLock lock( m_mtx ); ScopedLock lock( m_mtx );
_Delete_unlocked(); _Delete_unlocked();
} }
// Removes the pointer from scoped management, but does not delete! // Removes the pointer from scoped management, but does not delete!
// (ScopedPtr will be NULL after this method) // (ScopedPtr will be NULL after this method)
T *DetachPtr() T *DetachPtr()
{ {
ScopedLock lock( m_mtx ); ScopedLock lock( m_mtx );
T *ptr = m_ptr; T *ptr = m_ptr;
m_ptr = NULL; m_ptr = NULL;
return ptr; return ptr;
} }
// Returns the managed pointer. Can return NULL as a valid result if the ScopedPtrMT // Returns the managed pointer. Can return NULL as a valid result if the ScopedPtrMT
// has no object in management. // has no object in management.
T* GetPtr() const T* GetPtr() const
{ {
return m_ptr; return m_ptr;
} }
void SwapPtr(ScopedPtrMT& other) void SwapPtr(ScopedPtrMT& other)
{ {
ScopedLock lock( m_mtx ); ScopedLock lock( m_mtx );
T * const tmp = other.m_ptr; T * const tmp = other.m_ptr;
other.m_ptr = m_ptr; other.m_ptr = m_ptr;
m_ptr = tmp; m_ptr = tmp;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// ScopedPtrMT Operators // ScopedPtrMT Operators
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// I've decided to use the ATL's approach to pointer validity tests, opposed to // 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 // the wx/boost approach (which uses some bizarre member method pointer crap, and can't
// allow the T* implicit casting. // allow the T* implicit casting.
bool operator!() const throw() bool operator!() const throw()
{ {
return m_ptr == NULL; return m_ptr == NULL;
} }
// Equality // Equality
bool operator==(T* pT) const throw() bool operator==(T* pT) const throw()
{ {
return m_ptr == pT; return m_ptr == pT;
} }
// Inequality // Inequality
bool operator!=(T* pT) const throw() bool operator!=(T* pT) const throw()
{ {
return !operator==(pT); return !operator==(pT);
} }
// Convenient assignment operator. ScopedPtrMT = NULL will issue an automatic deletion // Convenient assignment operator. ScopedPtrMT = NULL will issue an automatic deletion
// of the managed pointer. // of the managed pointer.
ScopedPtrMT& operator=( T* src ) ScopedPtrMT& operator=( T* src )
{ {
return Reassign( src ); return Reassign( src );
} }
#if 0 #if 0
operator T*() const operator T*() const
{ {
return m_ptr; return m_ptr;
} }
// Dereference operator, returns a handle to the managed pointer. // Dereference operator, returns a handle to the managed pointer.
// Generates a debug assertion if the object is NULL! // Generates a debug assertion if the object is NULL!
T& operator*() const T& operator*() const
{ {
pxAssert(m_ptr != NULL); pxAssert(m_ptr != NULL);
return *m_ptr; return *m_ptr;
} }
T* operator->() const T* operator->() const
{ {
pxAssert(m_ptr != NULL); pxAssert(m_ptr != NULL);
return m_ptr; return m_ptr;
} }
#endif #endif
protected: protected:
void _Delete_unlocked() throw() void _Delete_unlocked() throw()
{ {
delete m_ptr; delete m_ptr;
m_ptr = NULL; m_ptr = NULL;
} }
}; };

View File

@ -83,6 +83,8 @@ protected:
ScopedPtr<ArchiveDataBuffer> m_data; ScopedPtr<ArchiveDataBuffer> m_data;
public: public:
virtual ~ArchiveEntryList() throw() {}
ArchiveEntryList() {} ArchiveEntryList() {}
ArchiveEntryList( ArchiveDataBuffer* data ) ArchiveEntryList( ArchiveDataBuffer* data )
@ -90,12 +92,10 @@ public:
m_data = data; m_data = data;
} }
ArchiveEntryList( ScopedPtr<ArchiveDataBuffer>& data ) ArchiveEntryList( ArchiveDataBuffer& data )
{ {
m_data = data.DetachPtr(); m_data = &data;
} }
virtual ~ArchiveEntryList() throw() {}
const VmStateBuffer* GetBuffer() const const VmStateBuffer* GetBuffer() const
{ {
@ -148,8 +148,8 @@ class BaseCompressThread
typedef pxThread _parent; typedef pxThread _parent;
protected: protected:
ScopedPtr< ArchiveEntryList > m_src_list; pxOutputStream* m_gzfp;
ScopedPtr< pxOutputStream > m_gzfp; ArchiveEntryList* m_src_list;
bool m_PendingSaveFlag; bool m_PendingSaveFlag;
wxString m_final_filename; wxString m_final_filename;
@ -163,9 +163,9 @@ public:
return *this; return *this;
} }
BaseCompressThread& SetSource( ScopedPtr< ArchiveEntryList >& srcdata ) BaseCompressThread& SetSource( ArchiveEntryList& srcdata )
{ {
m_src_list = srcdata.DetachPtr(); m_src_list = &srcdata;
return *this; return *this;
} }
@ -175,9 +175,9 @@ public:
return *this; return *this;
} }
BaseCompressThread& SetOutStream( ScopedPtr< pxOutputStream >& out ) BaseCompressThread& SetOutStream( pxOutputStream& out )
{ {
m_gzfp = out.DetachPtr(); m_gzfp = &out;
return *this; return *this;
} }

View File

@ -48,9 +48,8 @@ void BaseCompressThread::ExecuteTaskInThread()
// result over the original. // result over the original.
if( !m_src_list ) return; if( !m_src_list ) return;
SetPendingSave(); SetPendingSave();
Yield( 3 ); Yield( 3 );
uint listlen = m_src_list->GetLength(); uint listlen = m_src_list->GetLength();
@ -89,5 +88,8 @@ void BaseCompressThread::OnCleanupInThread()
{ {
_parent::OnCleanupInThread(); _parent::OnCleanupInThread();
wxGetApp().DeleteThread( this ); wxGetApp().DeleteThread( this );
safe_delete(m_gzfp);
safe_delete(m_src_list);
} }

View File

@ -781,7 +781,7 @@ void Pcsx2App::OnDestroyWindow( wxWindowDestroyEvent& evt )
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// SysEventHandler // SysEventHandler
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
class SysEvtHandler : public pxEvtHandler class SysEvtHandler : public pxEvtQueue
{ {
public: public:
wxString GetEvtHandlerName() const { return L"SysExecutor"; } wxString GetEvtHandlerName() const { return L"SysExecutor"; }

View File

@ -22,13 +22,13 @@ using namespace pxSizerFlags;
// ConsoleLogSource_Event (implementations) // ConsoleLogSource_Event (implementations)
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
bool ConsoleLogSource_Event::Write( const pxEvtHandler* evtHandler, const SysExecEvent* evt, const wxChar* msg ) { bool ConsoleLogSource_Event::Write( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg ) {
return _parent::Write( pxsFmt(L"(%s:%s) ", evtHandler->GetEventHandlerName().c_str(), evt->GetEventName().c_str()) + msg ); return _parent::Write( pxsFmt(L"(%s:%s) ", evtHandler->GetEventHandlerName().c_str(), evt->GetEventName().c_str()) + msg );
} }
bool ConsoleLogSource_Event::Warn( const pxEvtHandler* evtHandler, const SysExecEvent* evt, const wxChar* msg ) { bool ConsoleLogSource_Event::Warn( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg ) {
return _parent::Write( pxsFmt(L"(%s:%s) ", evtHandler->GetEventHandlerName().c_str(), evt->GetEventName().c_str()) + msg ); return _parent::Write( pxsFmt(L"(%s:%s) ", evtHandler->GetEventHandlerName().c_str(), evt->GetEventName().c_str()) + msg );
} }
bool ConsoleLogSource_Event::Error( const pxEvtHandler* evtHandler, const SysExecEvent* evt, const wxChar* msg ) { bool ConsoleLogSource_Event::Error( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg ) {
return _parent::Write( pxsFmt(L"(%s:%s) ", evtHandler->GetEventHandlerName().c_str(), evt->GetEventName().c_str()) + msg ); return _parent::Write( pxsFmt(L"(%s:%s) ", evtHandler->GetEventHandlerName().c_str(), evt->GetEventName().c_str()) + msg );
} }
@ -84,7 +84,7 @@ void SysExecEvent::SetException( BaseException* ex )
{ {
if( !ex ) return; if( !ex ) return;
ex->DiagMsg() += wxsFormat(L"(%s) ", GetEventName().c_str()); ex->DiagMsg() += pxsFmt(L"(%s) ", GetEventName().c_str());
//ex->UserMsg() = prefix + ex->UserMsg(); //ex->UserMsg() = prefix + ex->UserMsg();
if( m_sync ) if( m_sync )
@ -153,9 +153,9 @@ void SysExecEvent::PostResult() const
} }
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// pxEvtHandler Implementations // pxEvtQueue Implementations
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
pxEvtHandler::pxEvtHandler() pxEvtQueue::pxEvtQueue()
{ {
AtomicExchange( m_Quitting, false ); AtomicExchange( m_Quitting, false );
m_qpc_Start = 0; m_qpc_Start = 0;
@ -167,7 +167,7 @@ pxEvtHandler::pxEvtHandler()
// (typically these are shutdown events critical to closing the app cleanly). Once // (typically these are shutdown events critical to closing the app cleanly). Once
// all such events have been processed, the thread is stopped. // all such events have been processed, the thread is stopped.
// //
void pxEvtHandler::ShutdownQueue() void pxEvtQueue::ShutdownQueue()
{ {
if( m_Quitting ) return; if( m_Quitting ) return;
AtomicExchange( m_Quitting, true ); AtomicExchange( m_Quitting, true );
@ -190,7 +190,7 @@ struct ScopedThreadCancelDisable
}; };
// isIdle - parameter is useful for logging only (currently) // isIdle - parameter is useful for logging only (currently)
void pxEvtHandler::ProcessEvents( pxEvtList& list, bool isIdle ) void pxEvtQueue::ProcessEvents( pxEvtList& list, bool isIdle )
{ {
ScopedLock synclock( m_mtx_pending ); ScopedLock synclock( m_mtx_pending );
@ -238,19 +238,19 @@ void pxEvtHandler::ProcessEvents( pxEvtList& list, bool isIdle )
} }
} }
void pxEvtHandler::ProcessIdleEvents() void pxEvtQueue::ProcessIdleEvents()
{ {
ProcessEvents( m_idleEvents, true ); ProcessEvents( m_idleEvents, true );
} }
void pxEvtHandler::ProcessPendingEvents() void pxEvtQueue::ProcessPendingEvents()
{ {
ProcessEvents( m_pendingEvents ); ProcessEvents( m_pendingEvents );
} }
// This method is provided for wxWidgets API conformance. I like to use PostEvent instead // This method is provided for wxWidgets API conformance. I like to use PostEvent instead
// since it's reminiscent of PostMessage in Windows (and behaves rather similarly). // since it's reminiscent of PostMessage in Windows (and behaves rather similarly).
void pxEvtHandler::AddPendingEvent( SysExecEvent& evt ) void pxEvtQueue::AddPendingEvent( SysExecEvent& evt )
{ {
PostEvent( evt ); PostEvent( evt );
} }
@ -261,7 +261,7 @@ void pxEvtHandler::AddPendingEvent( SysExecEvent& evt )
// to it automatically when the event has been executed. If you are using a scoped event // to it automatically when the event has been executed. If you are using a scoped event
// you should use the Reference/Handle overload instead! // you should use the Reference/Handle overload instead!
// //
void pxEvtHandler::PostEvent( SysExecEvent* evt ) void pxEvtQueue::PostEvent( SysExecEvent* evt )
{ {
ScopedPtr<SysExecEvent> sevt( evt ); ScopedPtr<SysExecEvent> sevt( evt );
if( !sevt ) return; if( !sevt ) return;
@ -274,19 +274,19 @@ void pxEvtHandler::PostEvent( SysExecEvent* evt )
ScopedLock synclock( m_mtx_pending ); ScopedLock synclock( m_mtx_pending );
pxEvtLog.Write( this, evt, wxsFormat(L"Posting event! (pending=%d, idle=%d)", m_pendingEvents.size(), m_idleEvents.size()) ); pxEvtLog.Write( this, evt, pxsFmt(L"Posting event! (pending=%d, idle=%d)", m_pendingEvents.size(), m_idleEvents.size()) );
m_pendingEvents.push_back( sevt.DetachPtr() ); m_pendingEvents.push_back( sevt.DetachPtr() );
if( m_pendingEvents.size() == 1) if( m_pendingEvents.size() == 1)
m_wakeup.Post(); m_wakeup.Post();
} }
void pxEvtHandler::PostEvent( const SysExecEvent& evt ) void pxEvtQueue::PostEvent( const SysExecEvent& evt )
{ {
PostEvent( evt.Clone() ); PostEvent( evt.Clone() );
} }
void pxEvtHandler::PostIdleEvent( SysExecEvent* evt ) void pxEvtQueue::PostIdleEvent( SysExecEvent* evt )
{ {
if( !evt ) return; if( !evt ) return;
@ -298,7 +298,7 @@ void pxEvtHandler::PostIdleEvent( SysExecEvent* evt )
ScopedLock synclock( m_mtx_pending ); ScopedLock synclock( m_mtx_pending );
pxEvtLog.Write( this, evt, wxsFormat(L"Posting event! (pending=%d, idle=%d) [idle]", m_pendingEvents.size(), m_idleEvents.size()) ); pxEvtLog.Write( this, evt, pxsFmt(L"Posting event! (pending=%d, idle=%d) [idle]", m_pendingEvents.size(), m_idleEvents.size()) );
if( m_pendingEvents.size() == 0) if( m_pendingEvents.size() == 0)
{ {
@ -309,12 +309,12 @@ void pxEvtHandler::PostIdleEvent( SysExecEvent* evt )
m_idleEvents.push_back( evt ); m_idleEvents.push_back( evt );
} }
void pxEvtHandler::PostIdleEvent( const SysExecEvent& evt ) void pxEvtQueue::PostIdleEvent( const SysExecEvent& evt )
{ {
PostIdleEvent( evt.Clone() ); PostIdleEvent( evt.Clone() );
} }
void pxEvtHandler::ProcessEvent( SysExecEvent& evt ) void pxEvtQueue::ProcessEvent( SysExecEvent& evt )
{ {
if( wxThread::GetCurrentId() != m_OwnerThreadId ) if( wxThread::GetCurrentId() != m_OwnerThreadId )
{ {
@ -327,7 +327,7 @@ void pxEvtHandler::ProcessEvent( SysExecEvent& evt )
evt._DoInvokeEvent(); evt._DoInvokeEvent();
} }
void pxEvtHandler::ProcessEvent( SysExecEvent* evt ) void pxEvtQueue::ProcessEvent( SysExecEvent* evt )
{ {
if( !evt ) return; if( !evt ) return;
@ -345,7 +345,7 @@ void pxEvtHandler::ProcessEvent( SysExecEvent* evt )
} }
} }
bool pxEvtHandler::Rpc_TryInvokeAsync( FnType_Void* method, const wxChar* traceName ) bool pxEvtQueue::Rpc_TryInvokeAsync( FnType_Void* method, const wxChar* traceName )
{ {
if( wxThread::GetCurrentId() != m_OwnerThreadId ) if( wxThread::GetCurrentId() != m_OwnerThreadId )
{ {
@ -356,7 +356,7 @@ bool pxEvtHandler::Rpc_TryInvokeAsync( FnType_Void* method, const wxChar* traceN
return false; return false;
} }
bool pxEvtHandler::Rpc_TryInvoke( FnType_Void* method, const wxChar* traceName ) bool pxEvtQueue::Rpc_TryInvoke( FnType_Void* method, const wxChar* traceName )
{ {
if( wxThread::GetCurrentId() != m_OwnerThreadId ) if( wxThread::GetCurrentId() != m_OwnerThreadId )
{ {
@ -376,19 +376,17 @@ bool pxEvtHandler::Rpc_TryInvoke( FnType_Void* method, const wxChar* traceName )
// This method invokes the derived class Idle implementations (if any) and then enters // This method invokes the derived class Idle implementations (if any) and then enters
// the sleep state until such time that new messages are received. // the sleep state until such time that new messages are received.
// //
// FUTURE: Processes idle messages from the idle message queue (not implemented yet).
//
// Extending: Derived classes should override _DoIdle instead, unless it is necessary // Extending: Derived classes should override _DoIdle instead, unless it is necessary
// to implement post-wakeup behavior. // to implement post-wakeup behavior.
// //
void pxEvtHandler::Idle() void pxEvtQueue::Idle()
{ {
ProcessIdleEvents(); ProcessIdleEvents();
_DoIdle(); _DoIdle();
m_wakeup.WaitWithoutYield(); m_wakeup.WaitWithoutYield();
} }
void pxEvtHandler::SetActiveThread() void pxEvtQueue::SetActiveThread()
{ {
m_OwnerThreadId = wxThread::GetCurrentId(); m_OwnerThreadId = wxThread::GetCurrentId();
} }
@ -454,7 +452,7 @@ void WaitingForThreadedTaskDialog::OnTerminateApp_Clicked( wxCommandEvent& evt )
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// ExecutorThread Implementations // ExecutorThread Implementations
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
ExecutorThread::ExecutorThread( pxEvtHandler* evthandler ) ExecutorThread::ExecutorThread( pxEvtQueue* evthandler )
{ {
m_EvtHandler = evthandler; m_EvtHandler = evthandler;
} }
@ -465,7 +463,7 @@ bool ExecutorThread::IsRunning() const
return( !m_EvtHandler->IsShuttingDown() ); return( !m_EvtHandler->IsShuttingDown() );
} }
// Exposes the internal pxEvtHandler::ShutdownQueue API. See pxEvtHandler for details. // Exposes the internal pxEvtQueue::ShutdownQueue API. See pxEvtQueue for details.
void ExecutorThread::ShutdownQueue() void ExecutorThread::ShutdownQueue()
{ {
if( !m_EvtHandler ) return; if( !m_EvtHandler ) return;
@ -476,7 +474,7 @@ void ExecutorThread::ShutdownQueue()
Block(); Block();
} }
// Exposes the internal pxEvtHandler::PostEvent API. See pxEvtHandler for details. // Exposes the internal pxEvtQueue::PostEvent API. See pxEvtQueue for details.
void ExecutorThread::PostEvent( SysExecEvent* evt ) void ExecutorThread::PostEvent( SysExecEvent* evt )
{ {
if( !pxAssert( m_EvtHandler ) ) { delete evt; return; } if( !pxAssert( m_EvtHandler ) ) { delete evt; return; }
@ -489,7 +487,7 @@ void ExecutorThread::PostEvent( const SysExecEvent& evt )
m_EvtHandler->PostEvent( evt ); m_EvtHandler->PostEvent( evt );
} }
// Exposes the internal pxEvtHandler::PostIdleEvent API. See pxEvtHandler for details. // Exposes the internal pxEvtQueue::PostIdleEvent API. See pxEvtQueue for details.
void ExecutorThread::PostIdleEvent( SysExecEvent* evt ) void ExecutorThread::PostIdleEvent( SysExecEvent* evt )
{ {
if( !pxAssert( m_EvtHandler ) ) return; if( !pxAssert( m_EvtHandler ) ) return;
@ -502,7 +500,7 @@ void ExecutorThread::PostIdleEvent( const SysExecEvent& evt )
m_EvtHandler->PostIdleEvent( evt ); m_EvtHandler->PostIdleEvent( evt );
} }
// Exposes the internal pxEvtHandler::ProcessEvent API. See pxEvtHandler for details. // Exposes the internal pxEvtQueue::ProcessEvent API. See pxEvtQueue for details.
void ExecutorThread::ProcessEvent( SysExecEvent* evt ) void ExecutorThread::ProcessEvent( SysExecEvent* evt )
{ {
if( m_EvtHandler ) if( m_EvtHandler )

View File

@ -403,10 +403,10 @@ public:
SysExecEvent_ZipToDisk* Clone() const { return new SysExecEvent_ZipToDisk( *this ); } SysExecEvent_ZipToDisk* Clone() const { return new SysExecEvent_ZipToDisk( *this ); }
SysExecEvent_ZipToDisk( ScopedPtr<ArchiveEntryList>& srclist, const wxString& filename ) SysExecEvent_ZipToDisk( ArchiveEntryList& srclist, const wxString& filename )
: m_filename( filename ) : m_filename( filename )
{ {
m_src_list = srclist.DetachPtr(); m_src_list = &srclist;
} }
SysExecEvent_ZipToDisk( ArchiveEntryList* srclist, const wxString& filename ) SysExecEvent_ZipToDisk( ArchiveEntryList* srclist, const wxString& filename )
@ -454,13 +454,14 @@ protected:
} }
(*new VmStateCompressThread()) (*new VmStateCompressThread())
.SetSource(m_src_list) .SetSource(elist)
.SetOutStream(out) .SetOutStream(out)
.SetFinishedPath(m_filename) .SetFinishedPath(m_filename)
.Start(); .Start();
// No errors? Release cleanup handlers: // No errors? Release cleanup handlers:
elist.DetachPtr(); elist.DetachPtr();
out.DetachPtr();
} }
void CleanupEvent() void CleanupEvent()
@ -630,6 +631,8 @@ void StateCopy_SaveToFile( const wxString& file )
GetSysExecutorThread().PostEvent(new SysExecEvent_DownloadState ( ziplist )); GetSysExecutorThread().PostEvent(new SysExecEvent_DownloadState ( ziplist ));
GetSysExecutorThread().PostEvent(new SysExecEvent_ZipToDisk ( ziplist, file )); GetSysExecutorThread().PostEvent(new SysExecEvent_ZipToDisk ( ziplist, file ));
ziplist.DetachPtr();
} }
void StateCopy_LoadFromFile( const wxString& file ) void StateCopy_LoadFromFile( const wxString& file )

View File

@ -22,7 +22,7 @@
// TODO!! Make the system defined in this header system a bit more generic, and then move // TODO!! Make the system defined in this header system a bit more generic, and then move
// it to the Utilities library. // it to the Utilities library.
class pxEvtHandler; class pxEvtQueue;
class SysExecEvent; class SysExecEvent;
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -38,9 +38,9 @@ public:
ConsoleLogSource_Event(); ConsoleLogSource_Event();
bool Write( const pxEvtHandler* evtHandler, const SysExecEvent* evt, const wxChar* msg ); bool Write( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg );
bool Warn( const pxEvtHandler* evtHandler, const SysExecEvent* evt, const wxChar* msg ); bool Warn( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg );
bool Error( const pxEvtHandler* evtHandler, const SysExecEvent* evt, const wxChar* msg ); bool Error( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg );
}; };
extern ConsoleLogSource_Event pxConLog_Event; extern ConsoleLogSource_Event pxConLog_Event;
@ -51,7 +51,7 @@ extern ConsoleLogSource_Event pxConLog_Event;
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// SysExecEvent // SysExecEvent
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Base class for all pxEvtHandler processable events. // Base class for all pxEvtQueue processable events.
// //
// Rules for deriving: // Rules for deriving:
// * Override InvokeEvent(), *NOT* _DoInvokeEvent(). _DoInvokeEvent() performs setup and // * Override InvokeEvent(), *NOT* _DoInvokeEvent(). _DoInvokeEvent() performs setup and
@ -67,7 +67,11 @@ extern ConsoleLogSource_Event pxConLog_Event;
// awaking the invoking thread as soon as the queue has caught up to and processed // awaking the invoking thread as soon as the queue has caught up to and processed
// the event. // the event.
// //
// * Avoid using virtual class inheritence. It's unreliable at best. // * Avoid using virtual class inheritence to 'cleverly' bind a SysExecEvent to another
// existing class. Multiple inheritence is unreliable at best, and virtual inheritence
// is even worse. Create a SysExecEvent wrapper class instead, and embed an instance of
// the other class you want to turn into an event within it. It might feel like more work
// but it *will* be less work in the long run.
// //
class SysExecEvent : public ICloneable class SysExecEvent : public ICloneable
{ {
@ -174,13 +178,15 @@ protected:
#endif #endif
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// pxEvtHandler // pxEvtQueue
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Purpose: To provide a safe environment for queuing tasks that must be executed in // Thread-safe platform-independent message queue, intended to act as a proxy between
// sequential order (in blocking fashion). Unlike the wxWidgets event handlers, instances // control threads (such as the Main/UI thread) and worker threads.
// of this handler can be stalled for extended periods of time without affecting the //
// responsiveness of the GUI or frame updates of the DirectX output windows. This class // Proxy message queues provide a safe environment for queuing tasks that must be executed
// is mostly intended to be used from the context of an ExecutorThread. // in sequential order (in blocking fashion) on one or more worker threads. The queue is
// deadlock-free, which means the Main/UI thread can queue events into this EventQueue without
// fear of causing unresponsiveness within the user interface.
// //
// Rationales: // Rationales:
// * Using the main event handler of wxWidgets is dangerous because it must call itself // * Using the main event handler of wxWidgets is dangerous because it must call itself
@ -189,12 +195,11 @@ protected:
// running events that expect the suspend to be complete while the suspend was still // running events that expect the suspend to be complete while the suspend was still
// pending. // pending.
// //
// * wxWidgets Event Queue (wxEvtHandler) isn't thread-safe and isn't even // * wxWidgets Event Queue (wxEvtHandler) isn't thread-safe and isn't even intended for
// intended for use for anything other than wxWindow events (it uses static vars // use for anything other than wxWindow events (it uses static vars and checks/modifies
// and checks/modifies wxApp globals while processing), so it's useless to us. // wxApp globals while processing), so it's useless to us. Have to roll our own. -_-
// Have to roll our own. -_-
// //
class pxEvtHandler class pxEvtQueue
{ {
protected: protected:
pxEvtList m_pendingEvents; pxEvtList m_pendingEvents;
@ -210,10 +215,10 @@ protected:
volatile u64 m_qpc_Start; volatile u64 m_qpc_Start;
public: public:
pxEvtHandler(); pxEvtQueue();
virtual ~pxEvtHandler() throw() {} virtual ~pxEvtQueue() throw() {}
virtual wxString GetEventHandlerName() const { return L"pxEvtHandler"; } virtual wxString GetEventHandlerName() const { return L"pxEvtQueue"; }
virtual void ShutdownQueue(); virtual void ShutdownQueue();
bool IsShuttingDown() const { return !!m_Quitting; } bool IsShuttingDown() const { return !!m_Quitting; }
@ -243,8 +248,22 @@ protected:
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// ExecutorThread // ExecutorThread
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Threaded wrapper class for implementing pxEvtHandler. Simply create the desired // Threaded wrapper class for implementing a pxEvtQueue on its own thread, to serve as a
// EvtHandler, start the thread, and enjoy queued event execution in fully blocking fashion. // proxy between worker threads and response threads (which cannot be allowed to stall or
// deadlock). Simply create the desired EvtHandler, start the thread, and enjoy queued
// event execution in fully blocking fashion.
//
// Deadlock Protection Notes:
// The ExecutorThread utilizes an underlying pxEventQueue, which only locks the queue for
// adding and removing items only. Events are processed during an unlocked queue state,
// which allows other threads to add events with a guarantee that the add operation will
// take very little time.
//
// Implementation Notes:
// We use object encapsulation instead of multiple inheritance in order to avoid the many
// unavoidable catastrophes and pitfalls involved in multi-inheritance that would ruin our
// will to live. The alternative requires manually exposing the interface of the underlying
// instance of pxEventQueue; and that's ok really when you consider the alternative. --air
// //
class ExecutorThread : public Threading::pxThread class ExecutorThread : public Threading::pxThread
{ {
@ -252,10 +271,10 @@ class ExecutorThread : public Threading::pxThread
protected: protected:
ScopedPtr<wxTimer> m_ExecutorTimer; ScopedPtr<wxTimer> m_ExecutorTimer;
ScopedPtr<pxEvtHandler> m_EvtHandler; ScopedPtr<pxEvtQueue> m_EvtHandler;
public: public:
ExecutorThread( pxEvtHandler* evtandler = NULL ); ExecutorThread( pxEvtQueue* evtandler = NULL );
virtual ~ExecutorThread() throw() { } virtual ~ExecutorThread() throw() { }
virtual void ShutdownQueue(); virtual void ShutdownQueue();