mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-01-31 01:15:24 +01:00
513 lines
14 KiB
C++
513 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"
|
|
#include <memory>
|
|
|
|
using namespace pxSizerFlags;
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// ConsoleLogSource_Event (implementations)
|
|
// --------------------------------------------------------------------------------------
|
|
|
|
bool ConsoleLogSource_Event::Write( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg ) {
|
|
return _parent::Write( pxsFmt(L"(%s:%s) ", WX_STR(evtHandler->GetEventHandlerName()), WX_STR(evt->GetEventName())) + msg );
|
|
}
|
|
bool ConsoleLogSource_Event::Warn( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg ) {
|
|
return _parent::Write( pxsFmt(L"(%s:%s) ", WX_STR(evtHandler->GetEventHandlerName()), WX_STR(evt->GetEventName())) + msg );
|
|
}
|
|
bool ConsoleLogSource_Event::Error( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg ) {
|
|
return _parent::Write( pxsFmt(L"(%s:%s) ", WX_STR(evtHandler->GetEventHandlerName()), WX_STR(evt->GetEventName())) + msg );
|
|
}
|
|
|
|
ConsoleLogSource_Event::ConsoleLogSource_Event()
|
|
{
|
|
static const TraceLogDescriptor myDesc =
|
|
{
|
|
L"SysEvents", L"S&ysVM Control Events",
|
|
pxLt("Logs events as they are passed to the PS2 virtual machine."),
|
|
};
|
|
|
|
m_Descriptor = &myDesc;
|
|
}
|
|
|
|
ConsoleLogSource_Event pxConLog_Event;
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// SysExecEvent (implementations)
|
|
// --------------------------------------------------------------------------------------
|
|
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() += pxsFmt(L"(%s) ", WX_STR(GetEventName()));
|
|
//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();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// pxEvtQueue Implementations
|
|
// --------------------------------------------------------------------------------------
|
|
pxEvtQueue::pxEvtQueue() :
|
|
m_OwnerThreadId(), 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 pxEvtQueue::ShutdownQueue()
|
|
{
|
|
if( m_Quitting ) return;
|
|
m_Quitting = true;
|
|
m_wakeup.Post();
|
|
}
|
|
|
|
struct ScopedThreadCancelDisable
|
|
{
|
|
ScopedThreadCancelDisable()
|
|
{
|
|
int oldstate;
|
|
pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
|
|
}
|
|
|
|
~ScopedThreadCancelDisable()
|
|
{
|
|
int oldstate;
|
|
pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate );
|
|
}
|
|
};
|
|
|
|
// isIdle - parameter is useful for logging only (currently)
|
|
void pxEvtQueue::ProcessEvents( pxEvtList& list, bool isIdle )
|
|
{
|
|
ScopedLock synclock( m_mtx_pending );
|
|
|
|
pxEvtList::iterator node;
|
|
while( node = list.begin(), node != list.end() )
|
|
{
|
|
std::unique_ptr<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();
|
|
|
|
pxEvtLog.Write( this, deleteMe.get(), wxsFormat(L"Executing... [%s]%s",
|
|
deleteMe->AllowCancelOnExit() ? L"Cancelable" : L"Noncancelable", isIdle ? L"(Idle)" : wxEmptyString).wc_str()
|
|
);
|
|
|
|
if( deleteMe->AllowCancelOnExit() )
|
|
deleteMe->_DoInvokeEvent();
|
|
else
|
|
{
|
|
ScopedThreadCancelDisable thr_cancel_scope;
|
|
deleteMe->_DoInvokeEvent();
|
|
}
|
|
|
|
u64 qpc_end = GetCPUTicks();
|
|
pxEvtLog.Write( this, deleteMe.get(), wxsFormat(L"Event completed in %ums",
|
|
(u32)(((qpc_end-m_qpc_Start)*1000) / GetTickFrequency())).wc_str()
|
|
);
|
|
|
|
synclock.Acquire();
|
|
m_qpc_Start = 0; // lets the main thread know the message completed.
|
|
}
|
|
else
|
|
{
|
|
pxEvtLog.Write( this, deleteMe.get(), L"Skipping Event: %s" );
|
|
deleteMe->PostResult();
|
|
}
|
|
}
|
|
}
|
|
|
|
void pxEvtQueue::ProcessIdleEvents()
|
|
{
|
|
ProcessEvents( m_idleEvents, true );
|
|
}
|
|
|
|
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 pxEvtQueue::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 pxEvtQueue::PostEvent( SysExecEvent* evt )
|
|
{
|
|
std::unique_ptr<SysExecEvent> sevt(evt);
|
|
if( !sevt ) return;
|
|
|
|
if( m_Quitting )
|
|
{
|
|
sevt->PostResult();
|
|
return;
|
|
}
|
|
|
|
ScopedLock synclock( m_mtx_pending );
|
|
|
|
pxEvtLog.Write( this, evt, pxsFmt(L"Posting event! (pending=%d, idle=%d)", m_pendingEvents.size(), m_idleEvents.size()) );
|
|
|
|
m_pendingEvents.push_back( sevt.release() );
|
|
if( m_pendingEvents.size() == 1)
|
|
m_wakeup.Post();
|
|
}
|
|
|
|
void pxEvtQueue::PostEvent( const SysExecEvent& evt )
|
|
{
|
|
PostEvent( evt.Clone() );
|
|
}
|
|
|
|
void pxEvtQueue::PostIdleEvent( SysExecEvent* evt )
|
|
{
|
|
if( !evt ) return;
|
|
|
|
if( m_Quitting )
|
|
{
|
|
evt->PostResult();
|
|
return;
|
|
}
|
|
|
|
ScopedLock synclock( m_mtx_pending );
|
|
|
|
pxEvtLog.Write( this, evt, pxsFmt(L"Posting event! (pending=%d, idle=%d) [idle]", m_pendingEvents.size(), m_idleEvents.size()) );
|
|
|
|
if( m_pendingEvents.empty() )
|
|
{
|
|
m_pendingEvents.push_back( evt );
|
|
m_wakeup.Post();
|
|
}
|
|
else
|
|
m_idleEvents.push_back( evt );
|
|
}
|
|
|
|
void pxEvtQueue::PostIdleEvent( const SysExecEvent& evt )
|
|
{
|
|
PostIdleEvent( evt.Clone() );
|
|
}
|
|
|
|
void pxEvtQueue::ProcessEvent( SysExecEvent& evt )
|
|
{
|
|
if( wxThread::GetCurrentId() != m_OwnerThreadId )
|
|
{
|
|
SynchronousActionState sync;
|
|
evt.SetSyncState( sync );
|
|
PostEvent( evt );
|
|
sync.WaitForResult();
|
|
}
|
|
else
|
|
evt._DoInvokeEvent();
|
|
}
|
|
|
|
void pxEvtQueue::ProcessEvent( SysExecEvent* evt )
|
|
{
|
|
if( !evt ) return;
|
|
|
|
if( wxThread::GetCurrentId() != m_OwnerThreadId )
|
|
{
|
|
SynchronousActionState sync;
|
|
evt->SetSyncState( sync );
|
|
PostEvent( evt );
|
|
sync.WaitForResult();
|
|
}
|
|
else
|
|
{
|
|
std::unique_ptr<SysExecEvent> deleteMe(evt);
|
|
deleteMe->_DoInvokeEvent();
|
|
}
|
|
}
|
|
|
|
bool pxEvtQueue::Rpc_TryInvokeAsync( FnType_Void* method, const wxChar* traceName )
|
|
{
|
|
if( wxThread::GetCurrentId() != m_OwnerThreadId )
|
|
{
|
|
PostEvent( new SysExecEvent_MethodVoid(method, traceName) );
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool pxEvtQueue::Rpc_TryInvoke( FnType_Void* method, const wxChar* traceName )
|
|
{
|
|
if( wxThread::GetCurrentId() != m_OwnerThreadId )
|
|
{
|
|
SynchronousActionState sync;
|
|
SysExecEvent_MethodVoid evt(method);
|
|
evt.Critical();
|
|
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.
|
|
//
|
|
// Extending: Derived classes should override _DoIdle instead, unless it is necessary
|
|
// to implement post-wakeup behavior.
|
|
//
|
|
void pxEvtQueue::Idle()
|
|
{
|
|
ProcessIdleEvents();
|
|
_DoIdle();
|
|
m_wakeup.WaitWithoutYield();
|
|
}
|
|
|
|
void pxEvtQueue::SetActiveThread()
|
|
{
|
|
m_OwnerThreadId = wxThread::GetCurrentId();
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------
|
|
// ExecutorThread Implementations
|
|
// --------------------------------------------------------------------------------------
|
|
ExecutorThread::ExecutorThread(pxEvtQueue* evthandler)
|
|
: m_EvtHandler(evthandler)
|
|
{
|
|
}
|
|
|
|
bool ExecutorThread::IsRunning() const
|
|
{
|
|
if( !m_EvtHandler ) return false;
|
|
return( !m_EvtHandler->IsShuttingDown() );
|
|
}
|
|
|
|
// Exposes the internal pxEvtQueue::ShutdownQueue API. See pxEvtQueue for details.
|
|
void ExecutorThread::ShutdownQueue()
|
|
{
|
|
if( !m_EvtHandler ) return;
|
|
|
|
if( !m_EvtHandler->IsShuttingDown() )
|
|
m_EvtHandler->ShutdownQueue();
|
|
|
|
Block();
|
|
}
|
|
|
|
// Exposes the internal pxEvtQueue::PostEvent API. See pxEvtQueue 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 pxEvtQueue::PostIdleEvent API. See pxEvtQueue 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 pxEvtQueue::ProcessEvent API. See pxEvtQueue for details.
|
|
void ExecutorThread::ProcessEvent( SysExecEvent* evt )
|
|
{
|
|
if( m_EvtHandler )
|
|
m_EvtHandler->ProcessEvent( evt );
|
|
else
|
|
{
|
|
std::unique_ptr<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();
|
|
}
|