mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
git-svn-id: http://pcsx2.googlecode.com/svn/trunk@3330 96395faa-99c1-11dd-bbfe-3dabce05a288
518 lines
14 KiB
C++
518 lines
14 KiB
C++
/* 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/>.
|
|
*/
|
|
|
|
#include "PrecompiledHeader.h"
|
|
#include "App.h"
|
|
|
|
using namespace pxSizerFlags;
|
|
|
|
wxString SysExecEvent::GetEventName() const
|
|
{
|
|
pxFail( "Warning: Unnamed SysExecutor Event! Please overload GetEventName() in all SysExecEvent derived classes." );
|
|
return wxEmptyString;
|
|
}
|
|
|
|
wxString SysExecEvent::GetEventMessage() const
|
|
{
|
|
return GetEventName();
|
|
}
|
|
|
|
// This is called from _DoInvokeEvent after various affinity and state checks have verified the
|
|
// message as executable. Override this when possible. Only override _DoInvokeEvent if you
|
|
// need some kind of additional low-level ability.
|
|
void SysExecEvent::InvokeEvent()
|
|
{
|
|
}
|
|
|
|
// This is called by _DoInvokeEvent *always* -- even when exceptions occur during InvokeEvent(),
|
|
// making this function a bit like a C# 'finally' block (try/catch/finally -- a nice feature lacking
|
|
// from C++ prior to the new C++0x standard).
|
|
//
|
|
// This function calls PostResult by default, and should be invoked by derived classes overriding
|
|
// CleanupEvent(), unless you want to change the PostResult behavior.
|
|
void SysExecEvent::CleanupEvent()
|
|
{
|
|
PostResult();
|
|
}
|
|
|
|
// Transports the specified exception to the thread/context that invoked this event.
|
|
// Events are run from a try/catch block in the event handler that automatically transports
|
|
// exceptions as neeeded, so there shouldn't be much need to use this method directly.
|
|
void SysExecEvent::SetException( BaseException* ex )
|
|
{
|
|
if( !ex ) return;
|
|
|
|
ex->DiagMsg() += wxsFormat(L"(%s) ", GetEventName().c_str());
|
|
//ex->UserMsg() = prefix + ex->UserMsg();
|
|
|
|
if( m_sync )
|
|
{
|
|
// Throws the exception inline with the message handler (this makes the exception
|
|
// look like it was thrown quite naturally).
|
|
m_sync->SetException( ex );
|
|
}
|
|
else
|
|
{
|
|
// transport the exception to the main thread, since the message is fully
|
|
// asynchronous, or has already entered an asynchronous state. Message is sent
|
|
// as a non-blocking action since proper handling of user errors on async messages
|
|
// is *usually* to log/ignore it (hah), or to suspend emulation and issue a dialog
|
|
// box to the user.
|
|
|
|
wxGetApp().PostEvent( pxExceptionEvent( ex ) );
|
|
}
|
|
}
|
|
|
|
void SysExecEvent::SetException( const BaseException& ex )
|
|
{
|
|
SetException( ex.Clone() );
|
|
}
|
|
|
|
|
|
// This method calls _DoInvoke after performing some setup, exception handling, and
|
|
// affinity checks. For implementing behavior of your event, override _DoInvoke
|
|
// instead, which is the intended method of implementing derived class invocation.
|
|
void SysExecEvent::_DoInvokeEvent()
|
|
{
|
|
AffinityAssert_AllowFrom_SysExecutor();
|
|
|
|
try {
|
|
InvokeEvent();
|
|
}
|
|
catch( BaseException& ex )
|
|
{
|
|
SetException( ex );
|
|
}
|
|
catch( std::runtime_error& ex )
|
|
{
|
|
SetException( new Exception::RuntimeError(ex) );
|
|
}
|
|
|
|
// Cleanup Execution -- performed regardless of exception or not above.
|
|
try {
|
|
CleanupEvent();
|
|
}
|
|
catch( BaseException& ex )
|
|
{
|
|
SetException( ex );
|
|
}
|
|
catch( std::runtime_error& ex )
|
|
{
|
|
SetException( new Exception::RuntimeError(ex) );
|
|
}
|
|
}
|
|
|
|
// Posts an empty result to the invoking context/thread of this message, if one exists.
|
|
// If the invoking thread posted the event in non-blocking fashion then no action is
|
|
// taken.
|
|
void SysExecEvent::PostResult() const
|
|
{
|
|
if( m_sync ) m_sync->PostResult();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// pxEvtHandler Implementations
|
|
// --------------------------------------------------------------------------------------
|
|
pxEvtHandler::pxEvtHandler()
|
|
{
|
|
AtomicExchange( m_Quitting, false );
|
|
m_qpc_Start = 0;
|
|
}
|
|
|
|
// Puts the event queue into Shutdown mode, which does *not* immediately stop nor cancel
|
|
// the queue's processing. Instead it marks the queue inaccessible to all new events
|
|
// and continues procesing queued events for critical events that should not be ignored.
|
|
// (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()
|
|
{
|
|
if( m_Quitting ) return;
|
|
AtomicExchange( m_Quitting, true );
|
|
m_wakeup.Post();
|
|
}
|
|
|
|
struct ScopedThreadCancelDisable
|
|
{
|
|
ScopedThreadCancelDisable()
|
|
{
|
|
int oldstate;
|
|
pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
|
|
}
|
|
|
|
~ScopedThreadCancelDisable() throw()
|
|
{
|
|
int oldstate;
|
|
pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate );
|
|
}
|
|
};
|
|
|
|
void pxEvtHandler::ProcessEvents( pxEvtList& list )
|
|
{
|
|
ScopedLock synclock( m_mtx_pending );
|
|
|
|
pxEvtList::iterator node;
|
|
while( node = list.begin(), node != list.end() )
|
|
{
|
|
ScopedPtr<SysExecEvent> deleteMe(*node);
|
|
|
|
list.erase( node );
|
|
if( !m_Quitting || deleteMe->IsCriticalEvent() )
|
|
{
|
|
// Some messages can be blocking, so we should release the mutex lock while
|
|
// processing, to avoid having cases where the main thread deadlocks simply
|
|
// trying to add a message to the queue due to the basic mutex acquire needed.
|
|
|
|
m_qpc_Start = GetCPUTicks();
|
|
|
|
synclock.Release();
|
|
|
|
DevCon.WriteLn( L"(pxEvtHandler) Executing Event: %s [%s]", deleteMe->GetEventName().c_str(), deleteMe->AllowCancelOnExit() ? L"Cancelable" : L"Noncancelable" );
|
|
|
|
if( deleteMe->AllowCancelOnExit() )
|
|
deleteMe->_DoInvokeEvent();
|
|
else
|
|
{
|
|
ScopedThreadCancelDisable thr_cancel_scope;
|
|
deleteMe->_DoInvokeEvent();
|
|
}
|
|
|
|
u64 qpc_end = GetCPUTicks();
|
|
DevCon.WriteLn( L"(pxEvtHandler) Event '%s' completed in %ums", deleteMe->GetEventName().c_str(), (u32)(((qpc_end-m_qpc_Start)*1000) / GetTickFrequency()) );
|
|
|
|
synclock.Acquire();
|
|
m_qpc_Start = 0; // lets the main thread know the message completed.
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLn( L"(pxEvtHandler) Skipping Event: %s", deleteMe->GetEventName().c_str() );
|
|
deleteMe->PostResult();
|
|
}
|
|
}
|
|
}
|
|
|
|
void pxEvtHandler::ProcessIdleEvents()
|
|
{
|
|
ProcessEvents( m_idleEvents );
|
|
}
|
|
|
|
void pxEvtHandler::ProcessPendingEvents()
|
|
{
|
|
ProcessEvents( m_pendingEvents );
|
|
}
|
|
|
|
// This method is provided for wxWidgets API conformance. I like to use PostEvent instead
|
|
// since it's remenicient of PostMessage in Windows (and behaves rather similarly).
|
|
void pxEvtHandler::AddPendingEvent( SysExecEvent& evt )
|
|
{
|
|
PostEvent( evt );
|
|
}
|
|
|
|
// Adds an event to the event queue in non-blocking fashion. The thread executing this
|
|
// event queue will be woken up if it's idle/sleeping.
|
|
// IMPORTANT: The pointer version of this function will *DELETE* the event object passed
|
|
// 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 )
|
|
{
|
|
ScopedPtr<SysExecEvent> sevt( evt );
|
|
if( !sevt ) return;
|
|
|
|
if( m_Quitting )
|
|
{
|
|
sevt->PostResult();
|
|
return;
|
|
}
|
|
|
|
ScopedLock synclock( m_mtx_pending );
|
|
|
|
//DbgCon.WriteLn( L"(%s) Posting event: %s (queue count=%d)", GetEventHandlerName().c_str(), evt->GetEventName().c_str(), m_pendingEvents.size() );
|
|
|
|
m_pendingEvents.push_back( sevt.DetachPtr() );
|
|
if( m_pendingEvents.size() == 1)
|
|
m_wakeup.Post();
|
|
}
|
|
|
|
void pxEvtHandler::PostEvent( const SysExecEvent& evt )
|
|
{
|
|
PostEvent( evt.Clone() );
|
|
}
|
|
|
|
void pxEvtHandler::PostIdleEvent( SysExecEvent* evt )
|
|
{
|
|
if( !evt ) return;
|
|
|
|
if( m_Quitting )
|
|
{
|
|
evt->PostResult();
|
|
return;
|
|
}
|
|
|
|
ScopedLock synclock( m_mtx_pending );
|
|
|
|
if( m_idleEvents.size() == 0)
|
|
{
|
|
m_pendingEvents.push_back( evt );
|
|
m_wakeup.Post();
|
|
}
|
|
else
|
|
m_idleEvents.push_back( evt );
|
|
}
|
|
|
|
void pxEvtHandler::PostIdleEvent( const SysExecEvent& evt )
|
|
{
|
|
PostIdleEvent( evt.Clone() );
|
|
}
|
|
|
|
void pxEvtHandler::ProcessEvent( SysExecEvent& evt )
|
|
{
|
|
if( wxThread::GetCurrentId() != m_OwnerThreadId )
|
|
{
|
|
SynchronousActionState sync;
|
|
evt.SetSyncState( sync );
|
|
PostEvent( evt );
|
|
sync.WaitForResult();
|
|
}
|
|
else
|
|
evt._DoInvokeEvent();
|
|
}
|
|
|
|
void pxEvtHandler::ProcessEvent( SysExecEvent* evt )
|
|
{
|
|
if( !evt ) return;
|
|
|
|
if( wxThread::GetCurrentId() != m_OwnerThreadId )
|
|
{
|
|
SynchronousActionState sync;
|
|
evt->SetSyncState( sync );
|
|
PostEvent( evt );
|
|
sync.WaitForResult();
|
|
}
|
|
else
|
|
{
|
|
ScopedPtr<SysExecEvent> deleteMe( evt );
|
|
deleteMe->_DoInvokeEvent();
|
|
}
|
|
}
|
|
|
|
bool pxEvtHandler::ProcessMethodSelf( FnType_Void* method )
|
|
{
|
|
if( wxThread::GetCurrentId() != m_OwnerThreadId )
|
|
{
|
|
SynchronousActionState sync;
|
|
SysExecEvent_MethodVoid evt(method);
|
|
evt.SetSyncState( sync );
|
|
PostEvent( evt );
|
|
sync.WaitForResult();
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// 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()
|
|
{
|
|
ProcessIdleEvents();
|
|
_DoIdle();
|
|
m_wakeup.WaitWithoutYield();
|
|
}
|
|
|
|
void pxEvtHandler::SetActiveThread()
|
|
{
|
|
m_OwnerThreadId = wxThread::GetCurrentId();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// WaitingForThreadedTaskDialog
|
|
// --------------------------------------------------------------------------------------
|
|
// Note: currently unused (legacy code). May be utilized at a later date, so I'm leaving
|
|
// it in (for now!)
|
|
//
|
|
class WaitingForThreadedTaskDialog
|
|
: public wxDialogWithHelpers
|
|
{
|
|
private:
|
|
typedef wxDialogWithHelpers _parent;
|
|
|
|
protected:
|
|
pxThread* m_thread;
|
|
|
|
public:
|
|
WaitingForThreadedTaskDialog( pxThread* thr, wxWindow* parent, const wxString& title, const wxString& content );
|
|
virtual ~WaitingForThreadedTaskDialog() throw() {}
|
|
|
|
protected:
|
|
void OnCancel_Clicked( wxCommandEvent& evt );
|
|
void OnTerminateApp_Clicked( wxCommandEvent& evt );
|
|
};
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// WaitingForThreadedTaskDialog Implementations
|
|
// --------------------------------------------------------------------------------------
|
|
WaitingForThreadedTaskDialog::WaitingForThreadedTaskDialog( pxThread* thr, wxWindow* parent, const wxString& title, const wxString& content )
|
|
: wxDialogWithHelpers( parent, title )
|
|
{
|
|
SetMinWidth( 500 );
|
|
|
|
m_thread = thr;
|
|
|
|
*this += Text( content ) | StdExpand();
|
|
*this += 15;
|
|
*this += Heading(_("Press Cancel to attempt to cancel the action."));
|
|
*this += Heading(AddAppName(_("Press Terminate to kill %s immediately.")));
|
|
|
|
*this += new wxButton( this, wxID_CANCEL );
|
|
*this += new wxButton( this, wxID_ANY, _("Terminate App") );
|
|
}
|
|
|
|
void WaitingForThreadedTaskDialog::OnCancel_Clicked( wxCommandEvent& evt )
|
|
{
|
|
evt.Skip();
|
|
if( !m_thread ) return;
|
|
m_thread->Cancel( false );
|
|
|
|
if( wxWindow* cancel = FindWindowById( wxID_CANCEL ) ) cancel->Disable();
|
|
}
|
|
|
|
void WaitingForThreadedTaskDialog::OnTerminateApp_Clicked( wxCommandEvent& evt )
|
|
{
|
|
// (note: SIGTERM is a "handled" kill that performs shutdown stuff, which typically just crashes anyway)
|
|
wxKill( wxGetProcessId(), wxSIGKILL );
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// ExecutorThread Implementations
|
|
// --------------------------------------------------------------------------------------
|
|
ExecutorThread::ExecutorThread( pxEvtHandler* evthandler )
|
|
{
|
|
m_EvtHandler = evthandler;
|
|
}
|
|
|
|
// Exposes the internal pxEvtHandler::ShutdownQueue API. See pxEvtHandler for details.
|
|
void ExecutorThread::ShutdownQueue()
|
|
{
|
|
if( !m_EvtHandler ) return;
|
|
|
|
if( !m_EvtHandler->IsShuttingDown() )
|
|
m_EvtHandler->ShutdownQueue();
|
|
|
|
Block();
|
|
}
|
|
|
|
// Exposes the internal pxEvtHandler::PostEvent API. See pxEvtHandler for details.
|
|
void ExecutorThread::PostEvent( SysExecEvent* evt )
|
|
{
|
|
if( !pxAssert( m_EvtHandler ) ) { delete evt; return; }
|
|
m_EvtHandler->PostEvent( evt );
|
|
}
|
|
|
|
void ExecutorThread::PostEvent( const SysExecEvent& evt )
|
|
{
|
|
if( !pxAssert( m_EvtHandler ) ) return;
|
|
m_EvtHandler->PostEvent( evt );
|
|
}
|
|
|
|
// Exposes the internal pxEvtHandler::PostIdleEvent API. See pxEvtHandler for details.
|
|
void ExecutorThread::PostIdleEvent( SysExecEvent* evt )
|
|
{
|
|
if( !pxAssert( m_EvtHandler ) ) return;
|
|
m_EvtHandler->PostIdleEvent( evt );
|
|
}
|
|
|
|
void ExecutorThread::PostIdleEvent( const SysExecEvent& evt )
|
|
{
|
|
if( !pxAssert( m_EvtHandler ) ) return;
|
|
m_EvtHandler->PostIdleEvent( evt );
|
|
}
|
|
|
|
// Exposes the internal pxEvtHandler::ProcessEvent API. See pxEvtHandler for details.
|
|
void ExecutorThread::ProcessEvent( SysExecEvent* evt )
|
|
{
|
|
if( m_EvtHandler )
|
|
m_EvtHandler->ProcessEvent( evt );
|
|
else
|
|
{
|
|
ScopedPtr<SysExecEvent> deleteMe( evt );
|
|
deleteMe->_DoInvokeEvent();
|
|
}
|
|
}
|
|
|
|
void ExecutorThread::ProcessEvent( SysExecEvent& evt )
|
|
{
|
|
if( m_EvtHandler )
|
|
m_EvtHandler->ProcessEvent( evt );
|
|
else
|
|
evt._DoInvokeEvent();
|
|
}
|
|
|
|
void ExecutorThread::OnStart()
|
|
{
|
|
//if( !m_ExecutorTimer )
|
|
// m_ExecutorTimer = new wxTimer( wxTheApp, pxEvt_ThreadTaskTimeout_SysExec );
|
|
|
|
m_name = L"SysExecutor";
|
|
_parent::OnStart();
|
|
}
|
|
|
|
void ExecutorThread::ExecuteTaskInThread()
|
|
{
|
|
if( !pxAssertDev( m_EvtHandler, "Gimme a damn Event Handler first, object whore." ) ) return;
|
|
|
|
m_EvtHandler->SetActiveThread();
|
|
|
|
while( true )
|
|
{
|
|
if( !pxAssertDev( m_EvtHandler, "Event handler has been deallocated during SysExecutor thread execution." ) ) return;
|
|
|
|
m_EvtHandler->Idle();
|
|
m_EvtHandler->ProcessPendingEvents();
|
|
if( m_EvtHandler->IsShuttingDown() ) break;
|
|
}
|
|
}
|
|
|
|
void ExecutorThread::OnCleanupInThread()
|
|
{
|
|
//wxGetApp().PostCommand( m_task, pxEvt_ThreadTaskComplete );
|
|
_parent::OnCleanupInThread();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// Threaded Event Handlers (Pcsx2App)
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
// This event is called when the SysExecutorThread's timer triggers, which means the
|
|
// VM/system task has taken an oddly long period of time to complete. The task is able
|
|
// to invoke a modal dialog from here that will grant the user some options for handling
|
|
// the unresponsive task.
|
|
void Pcsx2App::OnSysExecutorTaskTimeout( wxTimerEvent& evt )
|
|
{
|
|
if( !SysExecutorThread.IsRunning() ) return;
|
|
|
|
//BaseThreadedInvocation* task = SysExecutorThread.GetTask();
|
|
//if( !task ) return;
|
|
|
|
//task->ShowModalStatus();
|
|
}
|