From 500c1dbfebfbf42c0944dfcd9b18b7ffd020906f Mon Sep 17 00:00:00 2001 From: "Jake.Stine" Date: Mon, 21 Dec 2009 11:52:14 +0000 Subject: [PATCH] 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 --- common/include/Utilities/Threading.h | 15 ++--- common/src/Utilities/Linux/LnxThreads.cpp | 13 ---- common/src/Utilities/ThreadTools.cpp | 14 +++-- common/src/Utilities/Windows/WinThreads.cpp | 21 ------- pcsx2/PluginManager.cpp | 3 + pcsx2/gui/App.h | 24 ++++--- pcsx2/gui/AppCommon.h | 1 + pcsx2/gui/AppInit.cpp | 9 +++ pcsx2/gui/AppMain.cpp | 69 +++++++++++++++++++++ pcsx2/gui/ConsoleLogger.cpp | 6 +- pcsx2/gui/Panels/PluginSelectorPanel.cpp | 12 ++-- pcsx2/gui/Plugins.cpp | 2 +- 12 files changed, 124 insertions(+), 65 deletions(-) diff --git a/common/include/Utilities/Threading.h b/common/include/Utilities/Threading.h index d9018c7ea5..edcf289d09 100644 --- a/common/include/Utilities/Threading.h +++ b/common/include/Utilities/Threading.h @@ -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() diff --git a/common/src/Utilities/Linux/LnxThreads.cpp b/common/src/Utilities/Linux/LnxThreads.cpp index f22153ba69..ac455c3869 100644 --- a/common/src/Utilities/Linux/LnxThreads.cpp +++ b/common/src/Utilities/Linux/LnxThreads.cpp @@ -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 diff --git a/common/src/Utilities/ThreadTools.cpp b/common/src/Utilities/ThreadTools.cpp index ef696e7624..b7fa076bf2 100644 --- a/common/src/Utilities/ThreadTools.cpp +++ b/common/src/Utilities/ThreadTools.cpp @@ -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)() ) { diff --git a/common/src/Utilities/Windows/WinThreads.cpp b/common/src/Utilities/Windows/WinThreads.cpp index ce1d94651d..1e70288ee6 100644 --- a/common/src/Utilities/Windows/WinThreads.cpp +++ b/common/src/Utilities/Windows/WinThreads.cpp @@ -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 diff --git a/pcsx2/PluginManager.cpp b/pcsx2/PluginManager.cpp index ce6d6063ab..2bb9b1baf6 100644 --- a/pcsx2/PluginManager.cpp +++ b/pcsx2/PluginManager.cpp @@ -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 ); diff --git a/pcsx2/gui/App.h b/pcsx2/gui/App.h index 665660bfda..939e3a22cd 100644 --- a/pcsx2/gui/App.h +++ b/pcsx2/gui/App.h @@ -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& Source_AppStatus() { return m_evtsrc_AppStatus; } EventSource& Source_CorePluginStatus() { return m_evtsrc_CorePluginStatus; } EventSource& Source_SettingsLoadSave() { return m_evtsrc_SettingsLoadSave; } - + // ---------------------------------------------------------------------------- + public: CommandDictionary GlobalCommands; AcceleratorDictionary GlobalAccels; @@ -353,7 +354,6 @@ public: protected: ScopedPtr m_StdoutRedirHandle; ScopedPtr m_StderrRedirHandle; - ScopedPtr 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 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 // ---------------------------------------------------------------------------- diff --git a/pcsx2/gui/AppCommon.h b/pcsx2/gui/AppCommon.h index 603f015572..e624da8285 100644 --- a/pcsx2/gui/AppCommon.h +++ b/pcsx2/gui/AppCommon.h @@ -30,6 +30,7 @@ class GSFrame; class ConsoleLogFrame; class PipeRedirectionBase; class AppCoreThread; +class pxPingEvent; enum AppEventType { diff --git a/pcsx2/gui/AppInit.cpp b/pcsx2/gui/AppInit.cpp index 67cede5dfe..7247a4ae4d 100644 --- a/pcsx2/gui/AppInit.cpp +++ b/pcsx2/gui/AppInit.cpp @@ -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 ) diff --git a/pcsx2/gui/AppMain.cpp b/pcsx2/gui/AppMain.cpp index 839b7d3094..34b2006fe1 100644 --- a/pcsx2/gui/AppMain.cpp +++ b/pcsx2/gui/AppMain.cpp @@ -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; iPost(); + } + + 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 diff --git a/pcsx2/gui/ConsoleLogger.cpp b/pcsx2/gui/ConsoleLogger.cpp index 2f7374e470..555583b08f 100644 --- a/pcsx2/gui/ConsoleLogger.cpp +++ b/pcsx2/gui/ConsoleLogger.cpp @@ -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(); } } } diff --git a/pcsx2/gui/Panels/PluginSelectorPanel.cpp b/pcsx2/gui/Panels/PluginSelectorPanel.cpp index 3532a2f2c0..b84f415439 100644 --- a/pcsx2/gui/Panels/PluginSelectorPanel.cpp +++ b/pcsx2/gui/Panels/PluginSelectorPanel.cpp @@ -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. } diff --git a/pcsx2/gui/Plugins.cpp b/pcsx2/gui/Plugins.cpp index 4228bdfda2..c87d4a59c5 100644 --- a/pcsx2/gui/Plugins.cpp +++ b/pcsx2/gui/Plugins.cpp @@ -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!" );