From 93e1d87a22fd99a65485d4a2a8d4c45c7ad4080b Mon Sep 17 00:00:00 2001 From: "Jake.Stine" Date: Sat, 5 Sep 2009 09:21:59 +0000 Subject: [PATCH] wxgui: improve single-threaded plugin enumerator for Linux (still a bit buggy) git-svn-id: http://pcsx2.googlecode.com/svn/branches/wxgui@1743 96395faa-99c1-11dd-bbfe-3dabce05a288 --- common/src/Utilities/ThreadTools.cpp | 15 +- pcsx2/Linux/pcsx2.cbp | 826 +++++++++++------------ pcsx2/PluginManager.cpp | 16 +- pcsx2/gui/ConsoleLogger.cpp | 36 +- pcsx2/gui/Dialogs/PickUserModeDialog.cpp | 8 +- pcsx2/gui/Panels/ConfigurationPanels.h | 49 +- pcsx2/gui/Panels/PluginSelectorPanel.cpp | 114 ++-- 7 files changed, 546 insertions(+), 518 deletions(-) diff --git a/common/src/Utilities/ThreadTools.cpp b/common/src/Utilities/ThreadTools.cpp index 80eef62e23..b54582607f 100644 --- a/common/src/Utilities/ThreadTools.cpp +++ b/common/src/Utilities/ThreadTools.cpp @@ -50,6 +50,8 @@ namespace Threading // your sister, and then cheating on her with your daughter. PersistentThread::~PersistentThread() { + if( !m_running ) return; + wxASSERT( !IsSelf() ); // not allowed from our own thread. if( !_InterlockedExchange( &m_detached, true ) ) @@ -62,9 +64,7 @@ namespace Threading // This function should not be called from the owner thread. void PersistentThread::Start() { - wxASSERT( !IsSelf() ); // not allowed from our own thread. if( m_running ) return; - if( pthread_create( &m_thread, NULL, _internal_callback, this ) != 0 ) throw Exception::ThreadCreationError(); @@ -74,8 +74,10 @@ namespace Threading // This function should not be called from the owner thread. void PersistentThread::Detach() { - wxASSERT( !IsSelf() ); // not allowed from our own thread. + if( !m_running ) return; if( _InterlockedExchange( &m_detached, true ) ) return; + + wxASSERT( !IsSelf() ); // not allowed from our own thread. pthread_detach( m_thread ); } @@ -92,7 +94,6 @@ namespace Threading // void PersistentThread::Cancel( bool isBlocking ) { - wxASSERT( !IsSelf() ); if( _InterlockedExchange( &m_detached, true ) ) { if( m_running ) @@ -100,6 +101,7 @@ namespace Threading return; } + wxASSERT( !IsSelf() ); pthread_cancel( m_thread ); if( isBlocking ) @@ -120,8 +122,6 @@ namespace Threading // sptr PersistentThread::Block() { - DevAssert( !IsSelf(), "Thread deadlock detected; Block() should never be called by the owner thread." ); - if( _InterlockedExchange( &m_detached, true ) ) { // already detached: if we're still running then its an invalid operation @@ -132,11 +132,12 @@ namespace Threading } else { + DevAssert( !IsSelf(), "Thread deadlock detected; Block() should never be called by the owner thread." ); pthread_join( m_thread, (void**)&m_returncode ); return m_returncode; } } - + bool PersistentThread::IsSelf() const { return pthread_self() == m_thread; diff --git a/pcsx2/Linux/pcsx2.cbp b/pcsx2/Linux/pcsx2.cbp index 5370d7f721..ace480280d 100644 --- a/pcsx2/Linux/pcsx2.cbp +++ b/pcsx2/Linux/pcsx2.cbp @@ -1,416 +1,410 @@ - - - - - - + + + + + + diff --git a/pcsx2/PluginManager.cpp b/pcsx2/PluginManager.cpp index e9fdb51e53..3497378f8f 100644 --- a/pcsx2/PluginManager.cpp +++ b/pcsx2/PluginManager.cpp @@ -603,7 +603,7 @@ PluginManager::PluginManager( const wxString (&folders)[PluginId_Count] ) // Bind Optional Functions // (leave pointer null and do not generate error) } - + // Hack for PAD's stupid parameter passed on Init PADinit = (_PADinit)m_info[PluginId_PAD].CommonBindings.Init; m_info[PluginId_PAD].CommonBindings.Init = _hack_PADinit; @@ -613,7 +613,7 @@ PluginManager::~PluginManager() { Close(); Shutdown(); - + // All library unloading done automatically. } @@ -627,7 +627,7 @@ void PluginManager::BindCommon( PluginsEnum_t pid ) while( current->MethodName != NULL ) { *target = (VoidMethod*)m_info[pid].Lib.GetSymbol( current->GetMethodName( pid ) ); - + if( *target == NULL ) *target = current->Fallback; @@ -636,7 +636,7 @@ void PluginManager::BindCommon( PluginsEnum_t pid ) throw Exception::PluginLoadError( pid, m_info[pid].Filename, wxLt( "Configured plugin is not a PCSX2 plugin, or is for an older unsupported version of PCSX2." ) ); } - + target++; current++; } @@ -759,7 +759,7 @@ static bool OpenPlugin_FW() void PluginManager::Open( PluginsEnum_t pid ) { if( m_info[pid].IsOpened ) return; - + // Each Open needs to be called explicitly. >_< bool result = true; @@ -793,7 +793,7 @@ void PluginManager::Close( PluginsEnum_t pid ) if( pid == PluginId_GS ) { if( mtgsThread == NULL ) return; - + if( !mtgsThread->IsSelf() ) { // force-close PAD before GS, because the PAD depends on the GS window. @@ -853,7 +853,7 @@ void PluginManager::Shutdown() // Shutdown plugins in reverse order (probably doesn't matter... // ... but what the heck, right?) - + for( int i=PluginId_Count-1; i>=0; --i ) { const PluginsEnum_t pid = tbl_PluginInfo[i].id; @@ -916,7 +916,7 @@ PluginManager* PluginManager_Create( const wxString (&folders)[PluginId_Count] ) PluginManager* PluginManager_Create( const wxChar* (&folders)[PluginId_Count] ) { wxString passins[PluginId_Count]; - + const PluginInfo* pi = tbl_PluginInfo-1; while( ++pi, pi->shortname != NULL ) passins[pi->id] = folders[pi->id]; diff --git a/pcsx2/gui/ConsoleLogger.cpp b/pcsx2/gui/ConsoleLogger.cpp index 554f38cbb8..8957a42e83 100644 --- a/pcsx2/gui/ConsoleLogger.cpp +++ b/pcsx2/gui/ConsoleLogger.cpp @@ -105,7 +105,7 @@ void pxLogConsole::DoLog( wxLogLevel level, const wxChar *szString, time_t t ) sptr ConsoleTestThread::ExecuteTask() { static int numtrack = 0; - + while( !m_done ) { // Two lines, both formatted, and varied colors. This makes for a fairly realistic @@ -195,7 +195,7 @@ void ConsoleLogFrame::ColorArray::Cleanup() { // The contents of m_table were created with placement new, and must be // disposed of manually: - + for( int i=0; i<8; ++i ) m_table[i].~wxTextAttr(); } @@ -259,7 +259,7 @@ ConsoleLogFrame::ConsoleLogFrame( MainEmuFrame *parent, const wxString& title, A wxMenu& menuAppear = *new wxMenu(); menuAppear.Append( wxID_ANY, _("Always on Top"), _("When checked the log window will be visible over other foreground windows."), wxITEM_CHECK ); - menuAppear.Append( wxID_ANY, _("Font Size"), &menuFontSizes ); + menuAppear.Append( wxID_ANY, _("Font Size"), &menuFontSizes ); pMenuBar->Append(&menuLog, _("&Log")); pMenuBar->Append(&menuAppear, _("&Appearance")); @@ -290,7 +290,7 @@ ConsoleLogFrame::ConsoleLogFrame( MainEmuFrame *parent, const wxString& title, A Connect( wxEVT_SetTitleText, wxCommandEventHandler(ConsoleLogFrame::OnSetTitle) ); Connect( wxEVT_DockConsole, wxCommandEventHandler(ConsoleLogFrame::OnDockedMove) ); Connect( wxEVT_SemaphoreWait, wxCommandEventHandler(ConsoleLogFrame::OnSemaphoreWait) ); - + if( m_threadlogger != NULL ) m_threadlogger->Start(); } @@ -324,7 +324,7 @@ void ConsoleLogFrame::Write( const wxString& text ) #endif m_TextCtrl.AppendText( text ); - + // cap at 256k for now... // fixme - 256k runs well on win32 but appears to be very sluggish on linux. Might // need platform dependent defaults here. - air @@ -463,9 +463,9 @@ void ConsoleLogFrame::OnFontSize( wxMenuEvent& evt ) case MenuID_FontSize_Large: ptsize = 10; break; case MenuID_FontSize_Huge: ptsize = 12; break; } - + if( ptsize == m_conf.FontSize ) return; - + m_conf.FontSize = ptsize; m_ColorTable.SetFont( ptsize ); m_TextCtrl.SetDefaultStyle( m_ColorTable[m_curcolor] ); @@ -530,13 +530,13 @@ void ConsoleLogFrame::CountMessage() // Thread Safety note: This function expects to be called from the Main GUI thread // only. If called from a thread other than Main, it will generate an assertion failure. -// +// void ConsoleLogFrame::DoMessage() { wxASSERT_MSG( wxThread::IsMain(), L"DoMessage must only be called from the main gui thread!" ); int cur = _InterlockedDecrement( &m_msgcounter ); - + // We need to freeze the control if there are more than 2 pending messages, // otherwise the redraw of the console will prevent it from ever being able to // catch up with the rate the queue is being filled, and the whole app could @@ -641,7 +641,7 @@ namespace Console return false; } - + bool __fastcall WriteLn( const char* fmt ) { const wxString fmtline( wxString::FromAscii( fmt ) + L"\n" ); @@ -652,7 +652,7 @@ namespace Console // Implementation note: I've duplicated Write+Newline behavior here to avoid polluting // the message pump with lots of erroneous messages (Newlines can be bound into Write message). - + wxCommandEvent evt( wxEVT_LOG_Write ); evt.SetString( fmtline ); evt.SetExtraLong( th_CurrentColor ); @@ -700,7 +700,7 @@ static int pxMessageDialog( const wxString& content, const wxString& caption, lo // 2) Issue the popup with wxSTAY_ON_TOP specified so that the user will see it. // // And in either case the emulation should be paused/suspended for the user. - + return wxMessageDialog( NULL, content, caption, flags ).ShowModal(); } @@ -766,7 +766,7 @@ public: void DoTheDialog() { int result; - + if( m_id == pxEVT_MSGBOX ) result = pxMessageDialog( m_Content, m_Title, m_Flags ); else @@ -799,7 +799,7 @@ namespace Msgbox instdat.WaitForMe.WaitNoCancel(); // Important! disable cancellation since we're using local stack vars. return instdat.result; } - + void OnEvent( pxMessageBoxEvent& evt ) { evt.DoTheDialog(); @@ -844,7 +844,7 @@ namespace Msgbox return wxID_YES == ThreadedMessageBox( text, caption, icon ); } } - + // [TODO] : This should probably be a fancier looking dialog box with the stacktrace // displayed inside a wxTextCtrl. static int CallStack( const wxString& errormsg, const wxString& stacktrace, const wxString& prompt, const wxString& caption, int buttons ) @@ -862,10 +862,10 @@ namespace Msgbox return ThreadedMessageBox( text, caption, buttons, pxEVT_CallStackBox ); } } - + int Assertion( const wxString& text, const wxString& stacktrace ) { - return CallStack( text, stacktrace, + return CallStack( text, stacktrace, L"\nDo you want to stop the program?" L"\nOr press [Cancel] to suppress further assertions.", L"PCSX2 Assertion Failure", @@ -877,5 +877,5 @@ namespace Msgbox { CallStack( src.FormatDisplayMessage(), src.FormatDiagnosticMessage(), wxEmptyString, L"PCSX2 Unhandled Exception", wxOK ); } - + } diff --git a/pcsx2/gui/Dialogs/PickUserModeDialog.cpp b/pcsx2/gui/Dialogs/PickUserModeDialog.cpp index 99c3e39935..f1836a8ad3 100644 --- a/pcsx2/gui/Dialogs/PickUserModeDialog.cpp +++ b/pcsx2/gui/Dialogs/PickUserModeDialog.cpp @@ -22,12 +22,12 @@ #include using namespace wxHelpers; - +using namespace Panels; Dialogs::PickUserModeDialog::PickUserModeDialog( wxWindow* parent, int id ) : wxDialogWithHelpers( parent, id, _("PCSX2 First Time configuration"), false ) -, m_panel_usersel( new Panels::UsermodeSelectionPanel( *this, 620, false ) ) -, m_panel_langsel( new Panels::LanguageSelectionPanel( *this, 620 ) ) +, m_panel_usersel( new UsermodeSelectionPanel( *this, 620, false ) ) +, m_panel_langsel( new LanguageSelectionPanel( *this, 620 ) ) { wxBoxSizer& s_main = *new wxBoxSizer( wxVERTICAL ); @@ -46,7 +46,7 @@ Dialogs::PickUserModeDialog::PickUserModeDialog( wxWindow* parent, int id ) : void Dialogs::PickUserModeDialog::OnOk_Click( wxCommandEvent& evt ) { - if( Panels::g_ApplyState.ApplyAll(true) ) + if( g_ApplyState.ApplyAll() ) { Close(); evt.Skip(); diff --git a/pcsx2/gui/Panels/ConfigurationPanels.h b/pcsx2/gui/Panels/ConfigurationPanels.h index bafea89e23..739e0c3663 100644 --- a/pcsx2/gui/Panels/ConfigurationPanels.h +++ b/pcsx2/gui/Panels/ConfigurationPanels.h @@ -70,7 +70,7 @@ namespace Exception BaseException::InitBaseEx( msg_eng, msg_xlt ); m_Panel = thispanel; } - + Panels::BaseApplicableConfigPanel* GetPanel() { return m_Panel; @@ -88,7 +88,7 @@ namespace Panels PanelApplyList_t PanelList; // Current book page being initialized. Any apply objects created will use - // this page as their "go here on error" page. (used to take the user to the + // this page as their "go here on error" page. (used to take the user to the // page with the option that failed apply validation). int CurOwnerPage; @@ -98,14 +98,14 @@ namespace Panels // Crappy hack to handle the UseAdminMode option, which can't be part of AppConfig // because AppConfig depends on this value to initialize itself. bool UseAdminMode; - + StaticApplyState() : PanelList() , CurOwnerPage( wxID_NONE ) , ParentBook( NULL ) { } - + void SetCurrentPage( int page ) { CurOwnerPage = page; @@ -115,7 +115,7 @@ namespace Panels { CurOwnerPage = wxID_NONE; } - + void StartBook( wxBookCtrlBase* book ); void StartWizard(); bool ApplyAll( bool saveOnSuccess=true ); @@ -132,7 +132,7 @@ namespace Panels // Thread Safety: None. This class is only safe when used from the GUI thread, as it uses // static vars and assumes that only one ApplicableConfig system is available to the // user at any time (ie, a singular modal dialog). - // + // class BaseApplicableConfigPanel : public wxPanelWithHelpers { protected: @@ -145,7 +145,7 @@ namespace Panels g_ApplyState.PanelList.remove( this ); } - BaseApplicableConfigPanel( wxWindow* parent, int idealWidth ) : + BaseApplicableConfigPanel( wxWindow* parent, int idealWidth ) : wxPanelWithHelpers( parent, idealWidth ) , m_OwnerPage( g_ApplyState.CurOwnerPage ) , m_OwnerBook( g_ApplyState.ParentBook ) @@ -155,7 +155,7 @@ namespace Panels int GetOwnerPage() const { return m_OwnerPage; } wxBookCtrlBase* GetOwnerBook() { return m_OwnerBook; } - + void SetFocusToMe() { if( (m_OwnerBook == NULL) || (m_OwnerPage == wxID_NONE) ) return; @@ -212,7 +212,7 @@ namespace Panels wxRadioButton* m_Option_RecIOP; wxRadioButton* m_Option_mVU0; wxRadioButton* m_Option_mVU1; - + wxRadioButton* m_Option_sVU0; wxRadioButton* m_Option_sVU1; @@ -220,7 +220,7 @@ namespace Panels CpuPanel( wxWindow& parent, int idealWidth ); void Apply( AppConfig& conf ); }; - + ////////////////////////////////////////////////////////////////////////////////////////// // class VideoPanel : public BaseApplicableConfigPanel @@ -231,7 +231,7 @@ namespace Panels VideoPanel( wxWindow& parent, int idealWidth ); void Apply( AppConfig& conf ); }; - + ////////////////////////////////////////////////////////////////////////////////////////// // class SpeedHacksPanel : public BaseApplicableConfigPanel @@ -241,7 +241,7 @@ namespace Panels wxSlider* m_slider_vustealer; wxStaticText* m_msg_eecycle; wxStaticText* m_msg_vustealer; - + wxCheckBox* m_check_intc; wxCheckBox* m_check_b1fc0; wxCheckBox* m_check_IOPx2; @@ -286,7 +286,7 @@ namespace Panels void Apply( AppConfig& conf ); void Reset(); wxDirName GetPath() const { return wxDirName( m_pickerCtrl->GetPath() ); } - + DirPickerPanel& SetStaticDesc( const wxString& msg ); protected: @@ -294,7 +294,7 @@ namespace Panels void Explore_Click( wxCommandEvent &event ); void UpdateCheckStatus( bool someNoteworthyBoolean ); }; - + ////////////////////////////////////////////////////////////////////////////////////////// // class SettingsDirPickerPanel : public DirPickerPanel @@ -331,7 +331,7 @@ namespace Panels class BaseSelectorPanel: public BaseApplicableConfigPanel { protected: - + public: virtual ~BaseSelectorPanel(); BaseSelectorPanel( wxWindow& parent, int idealWidth ); @@ -345,7 +345,7 @@ namespace Panels virtual void DoRefresh()=0; virtual bool ValidateEnumerationStatus()=0; }; - + ////////////////////////////////////////////////////////////////////////////////////////// // class BiosSelectorPanel : public BaseSelectorPanel @@ -354,7 +354,7 @@ namespace Panels wxListBox& m_ComboBox; DirPickerPanel& m_FolderPicker; wxArrayString* m_BiosList; - + public: BiosSelectorPanel( wxWindow& parent, int idealWidth ); virtual ~BiosSelectorPanel(); @@ -392,7 +392,7 @@ namespace Panels { } }; - + class EnumThread : public Threading::PersistentThread { public: @@ -400,15 +400,18 @@ namespace Panels protected: PluginSelectorPanel& m_master; - volatile bool m_cancel; + volatile bool m_cancel; public: virtual ~EnumThread(); EnumThread( PluginSelectorPanel& master ); void Cancel(); + void DoNextPlugin( int evtidx ); + + protected: sptr ExecuteTask(); }; - + // This panel contains all of the plugin combo boxes. We stick them // on a panel together so that we can hide/show the whole mess easily. class ComboBoxPanel : public wxPanelWithHelpers @@ -431,10 +434,10 @@ namespace Panels wxGauge& m_gauge; wxStaticText& m_label; int m_progress; - + public: StatusPanel( wxWindow* parent ); - + void SetGaugeLength( int len ); void AdvanceProgress( const wxString& msg ); void Reset(); @@ -465,7 +468,7 @@ namespace Panels virtual void DoRefresh(); virtual bool ValidateEnumerationStatus(); - + int FileCount() const { return m_FileList->Count(); } const wxString& GetFilename( int i ) const { return (*m_FileList)[i]; } friend class EnumThread; diff --git a/pcsx2/gui/Panels/PluginSelectorPanel.cpp b/pcsx2/gui/Panels/PluginSelectorPanel.cpp index 36e0e923ea..c7c31aac0d 100644 --- a/pcsx2/gui/Panels/PluginSelectorPanel.cpp +++ b/pcsx2/gui/Panels/PluginSelectorPanel.cpp @@ -26,7 +26,12 @@ #include // Allows us to force-disable threading for debugging/troubleshooting -static const bool DisableThreading = true; +static const bool DisableThreading = +#ifdef __LINUX__ + true; // linux appears to have threading issues with loadlibrary. +#else + false; +#endif using namespace wxHelpers; using namespace Threading; @@ -287,12 +292,20 @@ void Panels::PluginSelectorPanel::Apply( AppConfig& conf ) void Panels::PluginSelectorPanel::CancelRefresh() { - if (!DisableThreading) safe_delete( m_EnumeratorThread ); + safe_delete( m_EnumeratorThread ); safe_delete( m_FileList ); } void Panels::PluginSelectorPanel::DoRefresh() { + m_ComponentBoxes.Reset(); + if( m_FileList == NULL ) + { + wxCommandEvent evt; + OnEnumComplete( evt ); + return; + } + // Disable all controls until enumeration is complete. // Show status bar for plugin enumeration. @@ -307,19 +320,13 @@ void Panels::PluginSelectorPanel::DoRefresh() m_StatusPanel.Show(); // Use a thread to load plugins. - if (!DisableThreading) safe_delete( m_EnumeratorThread ); + safe_delete( m_EnumeratorThread ); m_EnumeratorThread = new EnumThread( *this ); if( DisableThreading ) - { - m_ComponentBoxes.Reset(); - m_EnumeratorThread->ExecuteTask(); - } + m_EnumeratorThread->DoNextPlugin( 0 ); else - { m_EnumeratorThread->Start(); - m_ComponentBoxes.Reset(); - } } bool Panels::PluginSelectorPanel::ValidateEnumerationStatus() @@ -338,6 +345,12 @@ bool Panels::PluginSelectorPanel::ValidateEnumerationStatus() if( (m_FileList == NULL) || (*pluginlist != *m_FileList) ) validated = false; + if( pluggers == 0 ) + { + safe_delete( m_FileList ); + return validated; + } + delete m_FileList; m_FileList = pluginlist.release(); @@ -363,7 +376,7 @@ void Panels::PluginSelectorPanel::OnConfigure_Clicked( wxCommandEvent& evt ) void Panels::PluginSelectorPanel::OnEnumComplete( wxCommandEvent& evt ) { - if (!DisableThreading) safe_delete( m_EnumeratorThread ); + safe_delete( m_EnumeratorThread ); // fixme: Default plugins should be picked based on the timestamp of the DLL or something? // (for now we just force it to selection zero if nothing's selected) @@ -388,7 +401,20 @@ void Panels::PluginSelectorPanel::OnProgress( wxCommandEvent& evt ) { if( m_FileList == NULL ) return; - size_t evtidx = evt.GetExtraLong(); + const size_t evtidx = evt.GetExtraLong(); + + if( DisableThreading ) + { + const int nextidx = evtidx+1; + if( nextidx == m_FileList->Count() ) + { + wxCommandEvent done( wxEVT_EnumerationFinished ); + GetEventHandler()->AddPendingEvent( done ); + } + else + m_EnumeratorThread->DoNextPlugin( nextidx ); + } + m_StatusPanel.AdvanceProgress( (evtidx < m_FileList->Count()-1) ? (*m_FileList)[evtidx + 1] : wxString(_("Completing tasks...")) ); @@ -449,6 +475,39 @@ void Panels::PluginSelectorPanel::EnumThread::Cancel() PersistentThread::Cancel(); } +void Panels::PluginSelectorPanel::EnumThread::DoNextPlugin( int curidx ) +{ + DbgCon::WriteLn( L"Enumerating Plugin: " + m_master.GetFilename( curidx ) ); + + try + { + EnumeratedPluginInfo& result( Results[curidx] ); + result.TypeMask = 0; + + PluginEnumerator penum( m_master.GetFilename( curidx ) ); + + result.Name = penum.GetName(); + for( int pidx=0; pidxAddPendingEvent( yay ); +} + sptr Panels::PluginSelectorPanel::EnumThread::ExecuteTask() { DevCon::Status( "Plugin Enumeration Thread started..." ); @@ -458,37 +517,8 @@ sptr Panels::PluginSelectorPanel::EnumThread::ExecuteTask() for( int curidx=0; curidx < m_master.FileCount(); ++curidx ) { if( m_cancel ) return 0; - DbgCon::WriteLn( L"Enumerating Plugin: " + m_master.GetFilename( curidx ) ); - - try - { - EnumeratedPluginInfo& result( Results[curidx] ); - result.TypeMask = 0; - - PluginEnumerator penum( m_master.GetFilename( curidx ) ); - - result.Name = penum.GetName(); - for( int pidx=0; pidxAddPendingEvent( yay ); } wxCommandEvent done( wxEVT_EnumerationFinished );