mirror of https://github.com/PCSX2/pcsx2.git
* 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:
parent
ae61d6010c
commit
a7fcc3929e
|
@ -1,143 +1,143 @@
|
|||
/* 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
|
||||
|
||||
/* 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"
|
||||
using Threading::ScopedLock;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// 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)Threading::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;
|
||||
}
|
||||
};
|
||||
using Threading::ScopedLock;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// 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)Threading::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;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -83,6 +83,8 @@ protected:
|
|||
ScopedPtr<ArchiveDataBuffer> m_data;
|
||||
|
||||
public:
|
||||
virtual ~ArchiveEntryList() throw() {}
|
||||
|
||||
ArchiveEntryList() {}
|
||||
|
||||
ArchiveEntryList( ArchiveDataBuffer* data )
|
||||
|
@ -90,12 +92,10 @@ public:
|
|||
m_data = data;
|
||||
}
|
||||
|
||||
ArchiveEntryList( ScopedPtr<ArchiveDataBuffer>& data )
|
||||
ArchiveEntryList( ArchiveDataBuffer& data )
|
||||
{
|
||||
m_data = data.DetachPtr();
|
||||
m_data = &data;
|
||||
}
|
||||
|
||||
virtual ~ArchiveEntryList() throw() {}
|
||||
|
||||
const VmStateBuffer* GetBuffer() const
|
||||
{
|
||||
|
@ -148,8 +148,8 @@ class BaseCompressThread
|
|||
typedef pxThread _parent;
|
||||
|
||||
protected:
|
||||
ScopedPtr< ArchiveEntryList > m_src_list;
|
||||
ScopedPtr< pxOutputStream > m_gzfp;
|
||||
pxOutputStream* m_gzfp;
|
||||
ArchiveEntryList* m_src_list;
|
||||
bool m_PendingSaveFlag;
|
||||
|
||||
wxString m_final_filename;
|
||||
|
@ -163,9 +163,9 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
BaseCompressThread& SetSource( ScopedPtr< ArchiveEntryList >& srcdata )
|
||||
BaseCompressThread& SetSource( ArchiveEntryList& srcdata )
|
||||
{
|
||||
m_src_list = srcdata.DetachPtr();
|
||||
m_src_list = &srcdata;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -175,9 +175,9 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
BaseCompressThread& SetOutStream( ScopedPtr< pxOutputStream >& out )
|
||||
BaseCompressThread& SetOutStream( pxOutputStream& out )
|
||||
{
|
||||
m_gzfp = out.DetachPtr();
|
||||
m_gzfp = &out;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,9 +48,8 @@ void BaseCompressThread::ExecuteTaskInThread()
|
|||
// result over the original.
|
||||
|
||||
if( !m_src_list ) return;
|
||||
|
||||
SetPendingSave();
|
||||
|
||||
|
||||
Yield( 3 );
|
||||
|
||||
uint listlen = m_src_list->GetLength();
|
||||
|
@ -89,5 +88,8 @@ void BaseCompressThread::OnCleanupInThread()
|
|||
{
|
||||
_parent::OnCleanupInThread();
|
||||
wxGetApp().DeleteThread( this );
|
||||
|
||||
safe_delete(m_gzfp);
|
||||
safe_delete(m_src_list);
|
||||
}
|
||||
|
||||
|
|
|
@ -781,7 +781,7 @@ void Pcsx2App::OnDestroyWindow( wxWindowDestroyEvent& evt )
|
|||
// --------------------------------------------------------------------------------------
|
||||
// SysEventHandler
|
||||
// --------------------------------------------------------------------------------------
|
||||
class SysEvtHandler : public pxEvtHandler
|
||||
class SysEvtHandler : public pxEvtQueue
|
||||
{
|
||||
public:
|
||||
wxString GetEvtHandlerName() const { return L"SysExecutor"; }
|
||||
|
|
|
@ -22,13 +22,13 @@ using namespace pxSizerFlags;
|
|||
// 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 );
|
||||
}
|
||||
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 );
|
||||
}
|
||||
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 );
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ void SysExecEvent::SetException( BaseException* ex )
|
|||
{
|
||||
if( !ex ) return;
|
||||
|
||||
ex->DiagMsg() += wxsFormat(L"(%s) ", GetEventName().c_str());
|
||||
ex->DiagMsg() += pxsFmt(L"(%s) ", GetEventName().c_str());
|
||||
//ex->UserMsg() = prefix + ex->UserMsg();
|
||||
|
||||
if( m_sync )
|
||||
|
@ -153,9 +153,9 @@ void SysExecEvent::PostResult() const
|
|||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxEvtHandler Implementations
|
||||
// pxEvtQueue Implementations
|
||||
// --------------------------------------------------------------------------------------
|
||||
pxEvtHandler::pxEvtHandler()
|
||||
pxEvtQueue::pxEvtQueue()
|
||||
{
|
||||
AtomicExchange( m_Quitting, false );
|
||||
m_qpc_Start = 0;
|
||||
|
@ -167,7 +167,7 @@ pxEvtHandler::pxEvtHandler()
|
|||
// (typically these are shutdown events critical to closing the app cleanly). Once
|
||||
// all such events have been processed, the thread is stopped.
|
||||
//
|
||||
void pxEvtHandler::ShutdownQueue()
|
||||
void pxEvtQueue::ShutdownQueue()
|
||||
{
|
||||
if( m_Quitting ) return;
|
||||
AtomicExchange( m_Quitting, true );
|
||||
|
@ -190,7 +190,7 @@ struct ScopedThreadCancelDisable
|
|||
};
|
||||
|
||||
// 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 );
|
||||
|
||||
|
@ -238,19 +238,19 @@ void pxEvtHandler::ProcessEvents( pxEvtList& list, bool isIdle )
|
|||
}
|
||||
}
|
||||
|
||||
void pxEvtHandler::ProcessIdleEvents()
|
||||
void pxEvtQueue::ProcessIdleEvents()
|
||||
{
|
||||
ProcessEvents( m_idleEvents, true );
|
||||
}
|
||||
|
||||
void pxEvtHandler::ProcessPendingEvents()
|
||||
void pxEvtQueue::ProcessPendingEvents()
|
||||
{
|
||||
ProcessEvents( m_pendingEvents );
|
||||
}
|
||||
|
||||
// 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).
|
||||
void pxEvtHandler::AddPendingEvent( SysExecEvent& evt )
|
||||
void pxEvtQueue::AddPendingEvent( SysExecEvent& 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
|
||||
// you should use the Reference/Handle overload instead!
|
||||
//
|
||||
void pxEvtHandler::PostEvent( SysExecEvent* evt )
|
||||
void pxEvtQueue::PostEvent( SysExecEvent* evt )
|
||||
{
|
||||
ScopedPtr<SysExecEvent> sevt( evt );
|
||||
if( !sevt ) return;
|
||||
|
@ -274,19 +274,19 @@ void pxEvtHandler::PostEvent( SysExecEvent* evt )
|
|||
|
||||
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() );
|
||||
if( m_pendingEvents.size() == 1)
|
||||
m_wakeup.Post();
|
||||
}
|
||||
|
||||
void pxEvtHandler::PostEvent( const SysExecEvent& evt )
|
||||
void pxEvtQueue::PostEvent( const SysExecEvent& evt )
|
||||
{
|
||||
PostEvent( evt.Clone() );
|
||||
}
|
||||
|
||||
void pxEvtHandler::PostIdleEvent( SysExecEvent* evt )
|
||||
void pxEvtQueue::PostIdleEvent( SysExecEvent* evt )
|
||||
{
|
||||
if( !evt ) return;
|
||||
|
||||
|
@ -298,7 +298,7 @@ void pxEvtHandler::PostIdleEvent( SysExecEvent* evt )
|
|||
|
||||
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)
|
||||
{
|
||||
|
@ -309,12 +309,12 @@ void pxEvtHandler::PostIdleEvent( SysExecEvent* evt )
|
|||
m_idleEvents.push_back( evt );
|
||||
}
|
||||
|
||||
void pxEvtHandler::PostIdleEvent( const SysExecEvent& evt )
|
||||
void pxEvtQueue::PostIdleEvent( const SysExecEvent& evt )
|
||||
{
|
||||
PostIdleEvent( evt.Clone() );
|
||||
}
|
||||
|
||||
void pxEvtHandler::ProcessEvent( SysExecEvent& evt )
|
||||
void pxEvtQueue::ProcessEvent( SysExecEvent& evt )
|
||||
{
|
||||
if( wxThread::GetCurrentId() != m_OwnerThreadId )
|
||||
{
|
||||
|
@ -327,7 +327,7 @@ void pxEvtHandler::ProcessEvent( SysExecEvent& evt )
|
|||
evt._DoInvokeEvent();
|
||||
}
|
||||
|
||||
void pxEvtHandler::ProcessEvent( SysExecEvent* evt )
|
||||
void pxEvtQueue::ProcessEvent( SysExecEvent* evt )
|
||||
{
|
||||
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 )
|
||||
{
|
||||
|
@ -356,7 +356,7 @@ bool pxEvtHandler::Rpc_TryInvokeAsync( FnType_Void* method, const wxChar* traceN
|
|||
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 )
|
||||
{
|
||||
|
@ -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
|
||||
// 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
|
||||
// to implement post-wakeup behavior.
|
||||
//
|
||||
void pxEvtHandler::Idle()
|
||||
void pxEvtQueue::Idle()
|
||||
{
|
||||
ProcessIdleEvents();
|
||||
_DoIdle();
|
||||
m_wakeup.WaitWithoutYield();
|
||||
}
|
||||
|
||||
void pxEvtHandler::SetActiveThread()
|
||||
void pxEvtQueue::SetActiveThread()
|
||||
{
|
||||
m_OwnerThreadId = wxThread::GetCurrentId();
|
||||
}
|
||||
|
@ -454,7 +452,7 @@ void WaitingForThreadedTaskDialog::OnTerminateApp_Clicked( wxCommandEvent& evt )
|
|||
// --------------------------------------------------------------------------------------
|
||||
// ExecutorThread Implementations
|
||||
// --------------------------------------------------------------------------------------
|
||||
ExecutorThread::ExecutorThread( pxEvtHandler* evthandler )
|
||||
ExecutorThread::ExecutorThread( pxEvtQueue* evthandler )
|
||||
{
|
||||
m_EvtHandler = evthandler;
|
||||
}
|
||||
|
@ -465,7 +463,7 @@ bool ExecutorThread::IsRunning() const
|
|||
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()
|
||||
{
|
||||
if( !m_EvtHandler ) return;
|
||||
|
@ -476,7 +474,7 @@ void ExecutorThread::ShutdownQueue()
|
|||
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 )
|
||||
{
|
||||
if( !pxAssert( m_EvtHandler ) ) { delete evt; return; }
|
||||
|
@ -489,7 +487,7 @@ void ExecutorThread::PostEvent( const SysExecEvent& 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 )
|
||||
{
|
||||
if( !pxAssert( m_EvtHandler ) ) return;
|
||||
|
@ -502,7 +500,7 @@ void ExecutorThread::PostIdleEvent( const SysExecEvent& 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 )
|
||||
{
|
||||
if( m_EvtHandler )
|
||||
|
|
|
@ -403,10 +403,10 @@ public:
|
|||
|
||||
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_src_list = srclist.DetachPtr();
|
||||
m_src_list = &srclist;
|
||||
}
|
||||
|
||||
SysExecEvent_ZipToDisk( ArchiveEntryList* srclist, const wxString& filename )
|
||||
|
@ -454,13 +454,14 @@ protected:
|
|||
}
|
||||
|
||||
(*new VmStateCompressThread())
|
||||
.SetSource(m_src_list)
|
||||
.SetSource(elist)
|
||||
.SetOutStream(out)
|
||||
.SetFinishedPath(m_filename)
|
||||
.Start();
|
||||
|
||||
// No errors? Release cleanup handlers:
|
||||
elist.DetachPtr();
|
||||
out.DetachPtr();
|
||||
}
|
||||
|
||||
void CleanupEvent()
|
||||
|
@ -630,6 +631,8 @@ void StateCopy_SaveToFile( const wxString& file )
|
|||
|
||||
GetSysExecutorThread().PostEvent(new SysExecEvent_DownloadState ( ziplist ));
|
||||
GetSysExecutorThread().PostEvent(new SysExecEvent_ZipToDisk ( ziplist, file ));
|
||||
|
||||
ziplist.DetachPtr();
|
||||
}
|
||||
|
||||
void StateCopy_LoadFromFile( const wxString& file )
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
// TODO!! Make the system defined in this header system a bit more generic, and then move
|
||||
// it to the Utilities library.
|
||||
|
||||
class pxEvtHandler;
|
||||
class pxEvtQueue;
|
||||
class SysExecEvent;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -38,9 +38,9 @@ public:
|
|||
|
||||
ConsoleLogSource_Event();
|
||||
|
||||
bool Write( const pxEvtHandler* evtHandler, const SysExecEvent* evt, const wxChar* msg );
|
||||
bool Warn( const pxEvtHandler* evtHandler, const SysExecEvent* evt, const wxChar* msg );
|
||||
bool Error( const pxEvtHandler* evtHandler, const SysExecEvent* evt, const wxChar* msg );
|
||||
bool Write( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg );
|
||||
bool Warn( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg );
|
||||
bool Error( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg );
|
||||
};
|
||||
|
||||
extern ConsoleLogSource_Event pxConLog_Event;
|
||||
|
@ -51,7 +51,7 @@ extern ConsoleLogSource_Event pxConLog_Event;
|
|||
// --------------------------------------------------------------------------------------
|
||||
// SysExecEvent
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Base class for all pxEvtHandler processable events.
|
||||
// Base class for all pxEvtQueue processable events.
|
||||
//
|
||||
// Rules for deriving:
|
||||
// * 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
|
||||
// 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
|
||||
{
|
||||
|
@ -174,13 +178,15 @@ protected:
|
|||
#endif
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// pxEvtHandler
|
||||
// pxEvtQueue
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Purpose: To provide a safe environment for queuing tasks that must be executed in
|
||||
// sequential order (in blocking fashion). Unlike the wxWidgets event handlers, instances
|
||||
// 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
|
||||
// is mostly intended to be used from the context of an ExecutorThread.
|
||||
// Thread-safe platform-independent message queue, intended to act as a proxy between
|
||||
// control threads (such as the Main/UI thread) and worker threads.
|
||||
//
|
||||
// Proxy message queues provide a safe environment for queuing tasks that must be executed
|
||||
// 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:
|
||||
// * 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
|
||||
// pending.
|
||||
//
|
||||
// * wxWidgets Event Queue (wxEvtHandler) isn't thread-safe and isn't even
|
||||
// intended for use for anything other than wxWindow events (it uses static vars
|
||||
// and checks/modifies wxApp globals while processing), so it's useless to us.
|
||||
// Have to roll our own. -_-
|
||||
// * wxWidgets Event Queue (wxEvtHandler) isn't thread-safe and isn't even intended for
|
||||
// use for anything other than wxWindow events (it uses static vars and checks/modifies
|
||||
// wxApp globals while processing), so it's useless to us. Have to roll our own. -_-
|
||||
//
|
||||
class pxEvtHandler
|
||||
class pxEvtQueue
|
||||
{
|
||||
protected:
|
||||
pxEvtList m_pendingEvents;
|
||||
|
@ -210,10 +215,10 @@ protected:
|
|||
volatile u64 m_qpc_Start;
|
||||
|
||||
public:
|
||||
pxEvtHandler();
|
||||
virtual ~pxEvtHandler() throw() {}
|
||||
pxEvtQueue();
|
||||
virtual ~pxEvtQueue() throw() {}
|
||||
|
||||
virtual wxString GetEventHandlerName() const { return L"pxEvtHandler"; }
|
||||
virtual wxString GetEventHandlerName() const { return L"pxEvtQueue"; }
|
||||
|
||||
virtual void ShutdownQueue();
|
||||
bool IsShuttingDown() const { return !!m_Quitting; }
|
||||
|
@ -243,8 +248,22 @@ protected:
|
|||
// --------------------------------------------------------------------------------------
|
||||
// ExecutorThread
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Threaded wrapper class for implementing pxEvtHandler. Simply create the desired
|
||||
// EvtHandler, start the thread, and enjoy queued event execution in fully blocking fashion.
|
||||
// Threaded wrapper class for implementing a pxEvtQueue on its own thread, to serve as a
|
||||
// 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
|
||||
{
|
||||
|
@ -252,10 +271,10 @@ class ExecutorThread : public Threading::pxThread
|
|||
|
||||
protected:
|
||||
ScopedPtr<wxTimer> m_ExecutorTimer;
|
||||
ScopedPtr<pxEvtHandler> m_EvtHandler;
|
||||
ScopedPtr<pxEvtQueue> m_EvtHandler;
|
||||
|
||||
public:
|
||||
ExecutorThread( pxEvtHandler* evtandler = NULL );
|
||||
ExecutorThread( pxEvtQueue* evtandler = NULL );
|
||||
virtual ~ExecutorThread() throw() { }
|
||||
|
||||
virtual void ShutdownQueue();
|
||||
|
|
Loading…
Reference in New Issue