Re-implemented Ping() into wxApp, this time using Idle events (for once I found a use for them!). This because the YieldToMain logic wasn't ever going to work right since wxWidgets lacks a way to poll the event loop for pending events. End result: More responsive GUI dialogs and junk. ;)

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2376 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-12-21 11:52:14 +00:00
parent f071faa642
commit 500c1dbfeb
12 changed files with 124 additions and 65 deletions

View File

@ -33,8 +33,12 @@ namespace Threading
{
class PersistentThread;
PersistentThread* pxGetCurrentThread();
wxString pxGetCurrentThreadName();
extern PersistentThread* pxGetCurrentThread();
extern wxString pxGetCurrentThreadName();
// Yields the current thread and provides cancelation points if the thread is managed by
// PersistentThread. Unmanaged threads use standard Sleep.
extern void pxYield( int ms );
}
namespace Exception
@ -127,10 +131,6 @@ namespace Exception
#endif
}
// Yields this thread against the main thread *if* the main thread's message pump has pending
// messages. If the main thread is idle then no yield is performed.
extern void pxYieldToMain();
namespace Threading
{
// --------------------------------------------------------------------------------------
@ -348,6 +348,8 @@ namespace Threading
{
DeclareNoncopyableObject(PersistentThread);
friend void pxYield( int ms );
protected:
wxString m_name; // diagnostic name for our thread.
@ -402,7 +404,6 @@ namespace Threading
virtual void ExecuteTaskInThread()=0;
void TestCancel() const;
void YieldToMain() const;
// Yields this thread to other threads and checks for cancellation. A sleeping thread should
// always test for cancellation, however if you really don't want to, you can use Threading::Sleep()

View File

@ -51,17 +51,4 @@ __forceinline void Threading::DisableHiresScheduler()
{
}
void pxYieldToMain()
{
// Linux/GTK+ Implementation Notes:
// I have no idea if wxEventLoop::Pending() is thread safe or not, nor do I have
// any idea how to properly obtain the message queue status of GTK+. So let's
// just play dumb (and slow) and sleep for a couple milliseconds regardless, until
// a better fix is found. --air
// (FIXME : Find a more correct implementation for this?)
Threading::Sleep( 2 );
}
#endif

View File

@ -92,6 +92,14 @@ wxString Threading::pxGetCurrentThreadName()
return L"Unknown";
}
void Threading::pxYield( int ms )
{
if( PersistentThread* thr = pxGetCurrentThread() )
thr->Yield( ms );
else
Sleep( ms );
}
// (intended for internal use only)
// Returns true if the Wait is recursive, or false if the Wait is safe and should be
// handled via normal yielding methods.
@ -465,12 +473,6 @@ void Threading::PersistentThread::TestCancel() const
pthread_testcancel();
}
void Threading::PersistentThread::YieldToMain() const
{
pxYieldToMain();
TestCancel();
}
// Executes the virtual member method
void Threading::PersistentThread::_try_virtual_invoke( void (PersistentThread::*method)() )
{

View File

@ -52,26 +52,5 @@ __forceinline void Threading::DisableHiresScheduler()
timeEndPeriod( 1 );
}
void pxYieldToMain()
{
// Windows Implementation Note:
// wxWidgets has a wxEventLoop::Pending() function, however it uses PeekMessage internally
// which is a multithreaded NO-NO. So instead we must use GetQueueStatus. This is ok
// anyway since I get extra fancy and scale the sleep duration based on the type of messages
// in the queue. User input (key and mouse button) cue longer worker thread yields since
// maintaining a responsive gui is typically a high priority.
DWORD result = GetQueueStatus( QS_ALLEVENTS );
uint hiword = HIWORD( result );
if( hiword == 0 ) return;
int sleepdur = 1;
if( (hiword & (QS_MOUSEBUTTON | QS_KEY)) != 0 )
sleepdur += 3;
Sleep( sleepdur );
}
#endif

View File

@ -733,6 +733,9 @@ PluginManager::PluginManager( const wxString (&folders)[PluginId_Count] )
wxsFormat( L"Plugin Test failure, return code: %d", testres ),
_( "The plugin reports that your hardware or software/drivers are not supported." )
);
pxYield( 2 );
} while( ++pi, pi->shortname != NULL );
CDVDapi_Plugin.newDiskCB( cdvdNewDiskCB );

View File

@ -39,6 +39,7 @@ BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE( pxEVT_LoadPluginsComplete, -1 )
DECLARE_EVENT_TYPE( pxEVT_CoreThreadStatus, -1 )
DECLARE_EVENT_TYPE( pxEVT_FreezeThreadFinished, -1 )
DECLARE_EVENT_TYPE( pxEVT_Ping, -1 )
END_DECLARE_EVENT_TYPES()
// This is used when the GS plugin is handling its own window. Messages from the PAD
@ -327,8 +328,7 @@ public:
class Pcsx2App : public wxApp
{
// ----------------------------------------------------------------------------
// Event Sources!
// ----------------------------------------------------------------------------
// Event Sources!
// These need to be at the top of the App class, because a lot of other things depend
// on them and they are, themselves, fairly self-contained.
@ -345,7 +345,8 @@ public:
EventSource<AppEventType>& Source_AppStatus() { return m_evtsrc_AppStatus; }
EventSource<PluginEventType>& Source_CorePluginStatus() { return m_evtsrc_CorePluginStatus; }
EventSource<IniInterface>& Source_SettingsLoadSave() { return m_evtsrc_SettingsLoadSave; }
// ----------------------------------------------------------------------------
public:
CommandDictionary GlobalCommands;
AcceleratorDictionary GlobalAccels;
@ -353,7 +354,6 @@ public:
protected:
ScopedPtr<PipeRedirectionBase> m_StdoutRedirHandle;
ScopedPtr<PipeRedirectionBase> m_StderrRedirHandle;
ScopedPtr<pxAppResources> m_Resources;
public:
@ -363,14 +363,18 @@ public:
protected:
// Note: Pointers to frames should not be scoped because wxWidgets handles deletion
// of these objects internally.
MainEmuFrame* m_MainFrame;
GSFrame* m_gsFrame;
ConsoleLogFrame* m_ProgramLogBox;
MainEmuFrame* m_MainFrame;
GSFrame* m_gsFrame;
ConsoleLogFrame* m_ProgramLogBox;
std::vector<Semaphore*> m_PingWhenIdle;
public:
Pcsx2App();
virtual ~Pcsx2App();
void Ping();
void PostPadKey( wxKeyEvent& evt );
void PostMenuAction( MenuIdentifiers menu_id ) const;
int IssueModalDialog( const wxString& dlgName );
@ -441,7 +445,8 @@ protected:
bool TryOpenConfigCwd();
void CleanupMess();
void OpenWizardConsole();
void PingDispatch( const char* action );
void HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent& event) const;
void HandleEvent(wxEvtHandler* handler, wxEventFunction func, wxEvent& event);
@ -456,6 +461,9 @@ protected:
void OnMessageBox( pxMessageBoxEvent& evt );
void OnEmuKeyDown( wxKeyEvent& evt );
void OnIdleEvent( wxIdleEvent& evt );
void OnPingEvent( pxPingEvent& evt );
// ----------------------------------------------------------------------------
// Override wx default exception handling behavior
// ----------------------------------------------------------------------------

View File

@ -30,6 +30,7 @@ class GSFrame;
class ConsoleLogFrame;
class PipeRedirectionBase;
class AppCoreThread;
class pxPingEvent;
enum AppEventType
{

View File

@ -261,6 +261,7 @@ bool Pcsx2App::OnCmdLineParsed( wxCmdLineParser& parser )
}
typedef void (wxEvtHandler::*pxMessageBoxEventFunction)(pxMessageBoxEvent&);
typedef void (wxEvtHandler::*pxPingEventFunction)(pxPingEvent&);
// ------------------------------------------------------------------------
bool Pcsx2App::OnInit()
@ -268,6 +269,9 @@ bool Pcsx2App::OnInit()
#define pxMessageBoxEventThing(func) \
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxMessageBoxEventFunction, &func )
#define pxPingEventHandler(func) \
(wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxPingEventFunction, &func )
Connect( pxEVT_MSGBOX, pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) );
Connect( pxEVT_ASSERTION, pxMessageBoxEventThing( Pcsx2App::OnMessageBox ) );
Connect( pxEVT_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) );
@ -291,6 +295,9 @@ bool Pcsx2App::OnInit()
Connect( pxEVT_CoreThreadStatus, wxCommandEventHandler( Pcsx2App::OnCoreThreadStatus ) );
Connect( pxEVT_FreezeThreadFinished, wxCommandEventHandler( Pcsx2App::OnFreezeThreadFinished ) );
Connect( pxEVT_Ping, pxPingEventHandler( Pcsx2App::OnPingEvent ) );
Connect( wxEVT_IDLE, wxIdleEventHandler( Pcsx2App::OnIdleEvent ) );
Connect( pxID_PadHandler_Keydown, wxEVT_KEY_DOWN, wxKeyEventHandler( Pcsx2App::OnEmuKeyDown ) );
// User/Admin Mode Dual Setup:
@ -465,6 +472,8 @@ void Pcsx2App::CleanupMess()
try
{
sys_resume_lock += 10;
PingDispatch( "Cleanup" );
CoreThread.Cancel();
if( m_CorePlugins )

View File

@ -38,6 +38,7 @@ DEFINE_EVENT_TYPE( pxEVT_SysExecute );
DEFINE_EVENT_TYPE( pxEVT_LoadPluginsComplete );
DEFINE_EVENT_TYPE( pxEVT_CoreThreadStatus );
DEFINE_EVENT_TYPE( pxEVT_FreezeThreadFinished );
DEFINE_EVENT_TYPE( pxEVT_Ping );
#include "Utilities/EventSource.inl"
EventSource_ImplementType( IniInterface );
@ -83,6 +84,74 @@ void Pcsx2App::PostMenuAction( MenuIdentifiers menu_id ) const
m_MainFrame->GetEventHandler()->AddPendingEvent( joe );
}
class pxPingEvent : public wxEvent
{
DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxPingEvent)
protected:
Semaphore* m_PostBack;
public:
virtual ~pxPingEvent() throw() { }
virtual pxPingEvent *Clone() const { return new pxPingEvent(*this); }
explicit pxPingEvent( int msgtype, Semaphore* sema=NULL )
: wxEvent( 0, msgtype )
{
m_PostBack = sema;
}
explicit pxPingEvent( Semaphore* sema=NULL )
: wxEvent( 0, pxEVT_Ping )
{
m_PostBack = sema;
}
pxPingEvent( const pxPingEvent& src )
: wxEvent( src )
{
m_PostBack = src.m_PostBack;
}
Semaphore* GetSemaphore() { return m_PostBack; }
};
IMPLEMENT_DYNAMIC_CLASS( pxPingEvent, wxEvent )
void Pcsx2App::OnPingEvent( pxPingEvent& evt )
{
m_PingWhenIdle.push_back( evt.GetSemaphore() );
}
void Pcsx2App::PingDispatch( const char* action )
{
size_t size = m_PingWhenIdle.size();
if( size == 0 ) return;
DbgCon.WriteLn( Color_Gray, "App Event Ping (%s) -> %u listeners.", action, size );
for( size_t i=0; i<size; ++i )
{
if( Semaphore* sema = m_PingWhenIdle[i] ) sema->Post();
}
m_PingWhenIdle.clear();
}
void Pcsx2App::OnIdleEvent( wxIdleEvent& evt )
{
evt.Skip();
PingDispatch( "Idle" );
}
void Pcsx2App::Ping()
{
Semaphore sema;
pxPingEvent evt( &sema );
AddPendingEvent( evt );
sema.WaitNoCancel();
}
void Pcsx2App::PostPadKey( wxKeyEvent& evt )
{
// HACK: Legacy PAD plugins expect PCSX2 to ignore keyboard messages on the

View File

@ -402,9 +402,9 @@ void ConsoleLogFrame::Write( ConsoleColors color, const wxString& text )
else
{
// give gui thread time to repaint and handle other pending messages.
// (those are prioritized lower than wxEvents, typically, which means we
// can't post a ping event since it'll still just starve out paint msgs.)
pxYieldToMain();
//pxYield( 1 );
wxGetApp().Ping();
}
}
}

View File

@ -672,18 +672,18 @@ void Panels::PluginSelectorPanel::EnumThread::ExecuteTaskInThread()
{
DevCon.WriteLn( "Plugin Enumeration Thread started..." );
YieldToMain();
Sleep( 15 ); // give the window some time to paint.
wxGetApp().Ping();
Sleep( 2 );
for( int curidx=0; curidx < m_master.FileCount(); ++curidx )
{
DoNextPlugin( curidx );
// speed isn't critical here, but the pretty status bar sure is.
// second try yield should give the status bars UI a "good" chance to refresh before we advance. :)
// speed isn't critical here, but the pretty status bar sure is. Sleep off
// some brief cycles to give the status bar time to refresh.
Sleep( 2 );
YieldToMain();
YieldToMain();
//Sleep(150); // uncomment this to slow down the selector, for debugging threading.
}

View File

@ -130,7 +130,7 @@ LoadPluginsTask::~LoadPluginsTask() throw()
void LoadPluginsTask::ExecuteTaskInThread()
{
pxYieldToMain();
wxGetApp().Ping();
// This is for testing of the error handler... uncomment for fun?
//throw Exception::PluginError( PluginId_PAD, "This one is for testing the error handler!" );