From 655301bfda0e0d29441a90d2fd271d05dec8a68e Mon Sep 17 00:00:00 2001 From: Charlie Fenton Date: Tue, 14 Oct 2008 03:28:34 +0000 Subject: [PATCH] MGR: Multiple fixes to Async GUI RPCs, message polling, task bar tooltip updates svn path=/trunk/boinc/; revision=16196 --- checkin_notes | 21 +++++++++++++++ clientgui/AsyncRPC.cpp | 42 +++++++++++++++++++++++------- clientgui/AsyncRPC.h | 8 +++++- clientgui/BOINCGUIApp.cpp | 26 +++++++++++++++++++ clientgui/BOINCGUIApp.h | 1 + clientgui/BOINCTaskBar.cpp | 20 ++++++-------- clientgui/BOINCTaskBar.h | 6 ++--- clientgui/Events.h | 1 - clientgui/MainDocument.cpp | 53 +++++++++++++++++++------------------- clientgui/MainDocument.h | 2 ++ 10 files changed, 127 insertions(+), 53 deletions(-) diff --git a/checkin_notes b/checkin_notes index 41417f8a1a..3575c3a907 100644 --- a/checkin_notes +++ b/checkin_notes @@ -8311,3 +8311,24 @@ David 12 Oct 2008 html/user/ create_profile.php + +Charlie 13 Oct 2008 + - MGR: Multiple fixes to Async GUI RPCs: + - Reject most events during RPC Wait dialog. This should + eliminate most asserts due to undesired recursion in + CMainDocument::RequestRPC(). + - Always update message list every second, even when in other + view tabs or when manager is minimized. + - Eliminate separate task bar update timer, update with other + periodic RPCs. + - Always update task information in task bar icon tooltip when + user hovers mouse over task bar icon. + - Improve enabling / disabling of task bar icon menu items. + + clientgui/ + AsyncRPC.cpp,.h + BOINCGUIApp.cpp,.h + BOINCTaskBar.cpp,.h + Events.h + MainDocument.cpp,.h + \ No newline at end of file diff --git a/clientgui/AsyncRPC.cpp b/clientgui/AsyncRPC.cpp index dabd89f924..11f8baf8b0 100755 --- a/clientgui/AsyncRPC.cpp +++ b/clientgui/AsyncRPC.cpp @@ -451,15 +451,17 @@ int CMainDocument::RequestRPC(ASYNC_RPC_REQUEST& request, bool hasPriority) { // a dialog allowing the user to cancel. if (request.rpcType == RPC_TYPE_WAIT_FOR_COMPLETION) { // TODO: proper handling if a second user request is received while first is pending ?? + // CBOINCGUIApp::FilterEvent() blocks eventrs during our "Please + // Wait" dialog, which could cause undesirable recursion here if (m_bWaitingForRPC) { wxLogMessage(wxT("Second user RPC request while another was pending")); - //wxASSERT(false); + wxASSERT(false); return -1; } - m_bWaitingForRPC = true; // Don't show dialog if RPC completes before RPC_WAIT_DLG_DELAY wxStopWatch Dlgdelay = wxStopWatch(); m_RPCWaitDlg = new AsyncRPCDlg(); + m_bWaitingForRPC = true; do { // Simulate handling of CRPCFinishedEvent but don't allow any other events (so no user activity) // Allow RPC thread to run while we wait for it @@ -514,6 +516,7 @@ int CMainDocument::RequestRPC(ASYNC_RPC_REQUEST& request, bool hasPriority) { RPC_requests.clear(); current_rpc_request.clear(); m_bNeedRefresh = false; + m_bNeedTaskBarRefresh = false; // We will be reconnected to the same client (if possible) by // CBOINCDialUpManager::OnPoll() and CNetworkConnection::Poll(). @@ -594,15 +597,21 @@ void CMainDocument::HandleCompletedRPC() { *(current_rpc_request.resultPtr) = retval; } - if (current_rpc_request.rpcType == RPC_TYPE_ASYNC_WITH_REFRESH_AFTER) { - if (!retval) { - m_bNeedRefresh = true; - } - } - // Post-processing if (! retval) { - switch (current_rpc_request.which_rpc) { + if (current_rpc_request.rpcType == RPC_TYPE_ASYNC_WITH_REFRESH_AFTER) { + if (!retval) { + m_bNeedRefresh = true; + } + } + + if (current_rpc_request.rpcType == RPC_TYPE_ASYNC_WITH_UPDATE_TASKBAR_ICON_AFTER) { + if (!retval) { + m_bNeedTaskBarRefresh = true; + } + } + + switch (current_rpc_request.which_rpc) { case RPC_GET_STATE: if (current_rpc_request.exchangeBuf && !retval) { CC_STATE* arg1 = (CC_STATE*)current_rpc_request.arg1; @@ -742,6 +751,21 @@ void CMainDocument::HandleCompletedRPC() { } } + if (m_bNeedTaskBarRefresh && !m_bWaitingForRPC) { + m_bNeedTaskBarRefresh = false; + CTaskBarIcon* pTaskbar = wxGetApp().GetTaskBarIcon(); + + if (pTaskbar) { + CTaskbarEvent event(wxEVT_TASKBAR_REFRESH, pTaskbar); + pTaskbar->ProcessEvent(event); + } + } + + // CachedMessageUpdate() does not do any RPCs, so it is safe here + if (current_rpc_request.rpcType == RPC_TYPE_ASYNC_WITH_UPDATE_MESSAGE_LIST_AFTER) { + CachedMessageUpdate(); + } + current_rpc_request.clear(); // Start the next RPC request. diff --git a/clientgui/AsyncRPC.h b/clientgui/AsyncRPC.h index b1631d7667..07abcb19d7 100644 --- a/clientgui/AsyncRPC.h +++ b/clientgui/AsyncRPC.h @@ -90,9 +90,15 @@ enum ASYNC_RPC_TYPE { // Periodic RPC: post request on queue and return immediately // (requested due to a timer interrupt.) RPC_TYPE_ASYNC_NO_REFRESH, - // Periodic RPCas above, but on completion also process a + // Periodic RPC as above, but on completion also process a // wxEVT_FRAME_REFRESHVIEW event to refresh the display. RPC_TYPE_ASYNC_WITH_REFRESH_AFTER, + // Periodic RPC as above, but on completion also update message + // list by calling CMainDocument::CachedMessageUpdate(). + RPC_TYPE_ASYNC_WITH_UPDATE_MESSAGE_LIST_AFTER, + // Periodic RPC as above, but on completion also process a + // wxEVT_TASKBAR_REFRESH event to refresh the taskbar icon. + RPC_TYPE_ASYNC_WITH_UPDATE_TASKBAR_ICON_AFTER, NUM_RPC_TYPES }; diff --git a/clientgui/BOINCGUIApp.cpp b/clientgui/BOINCGUIApp.cpp index 6b47cb674c..bd538207ec 100644 --- a/clientgui/BOINCGUIApp.cpp +++ b/clientgui/BOINCGUIApp.cpp @@ -865,8 +865,34 @@ bool CBOINCGUIApp::IsModalDialogDisplayed() { if (wxDynamicCast(wxWindow::FindWindowById(ID_ANYDIALOG), wxDialog)) { return true; } + + if (m_pDocument) { + if (m_pDocument->WaitingForRPC()) { + return true; + } + } return false; } +// Prevent recursive entry of CMainDocument::RequestRPC() +int CBOINCGUIApp::FilterEvent(wxEvent &event) { + if (!m_pDocument) return -1; + if (!m_pDocument->WaitingForRPC()) return -1; + + // If in RPC Please Wait dialog, reject all events except + // RPC Finished or those for that dialog or its children. + if (event.GetEventType() == wxEVT_RPC_FINISHED) return -1; + + wxDialog* theRPCWaitDialog = m_pDocument->GetRPCWaitDialog(); + wxObject * theObject = event.GetEventObject(); + while (theObject) { + if (! theObject->IsKindOf(CLASSINFO(wxWindow))) break; + if (theObject == theRPCWaitDialog) return -1; + theObject = ((wxWindow*)theObject)->GetParent(); + } + + return false; +} + const char *BOINC_RCSID_487cbf3018 = "$Id$"; diff --git a/clientgui/BOINCGUIApp.h b/clientgui/BOINCGUIApp.h index e0395d0bbe..747bcbc96a 100644 --- a/clientgui/BOINCGUIApp.h +++ b/clientgui/BOINCGUIApp.h @@ -157,6 +157,7 @@ public: int x = wxDefaultCoord, int y = wxDefaultCoord); bool IsModalDialogDisplayed(); + int FilterEvent(wxEvent &event); DECLARE_EVENT_TABLE() }; diff --git a/clientgui/BOINCTaskBar.cpp b/clientgui/BOINCTaskBar.cpp index b605add5fd..7680b11082 100644 --- a/clientgui/BOINCTaskBar.cpp +++ b/clientgui/BOINCTaskBar.cpp @@ -41,12 +41,12 @@ DEFINE_EVENT_TYPE(wxEVT_TASKBAR_RELOADSKIN) - +DEFINE_EVENT_TYPE(wxEVT_TASKBAR_REFRESH) BEGIN_EVENT_TABLE(CTaskBarIcon, wxTaskBarIconEx) EVT_IDLE(CTaskBarIcon::OnIdle) EVT_CLOSE(CTaskBarIcon::OnClose) - EVT_TIMER(ID_TB_TIMER, CTaskBarIcon::OnRefresh) + EVT_TASKBAR_REFRESH(CTaskBarIcon::OnRefresh) EVT_TASKBAR_RELOADSKIN(CTaskBarIcon::OnReloadSkin) EVT_TASKBAR_LEFT_DCLICK(CTaskBarIcon::OnLButtonDClick) EVT_MENU(wxID_OPEN, CTaskBarIcon::OnOpen) @@ -88,19 +88,11 @@ CTaskBarIcon::CTaskBarIcon(wxString title, wxIcon* icon, wxIcon* iconDisconnecte m_dtLastHoverDetected = wxDateTime((time_t)0); m_bMouseButtonPressed = false; - - m_pRefreshTimer = new wxTimer(this, ID_TB_TIMER); - m_pRefreshTimer->Start(1000); // Send event every second } CTaskBarIcon::~CTaskBarIcon() { RemoveIcon(); - - if (m_pRefreshTimer) { - m_pRefreshTimer->Stop(); - delete m_pRefreshTimer; - } } @@ -128,7 +120,7 @@ void CTaskBarIcon::OnClose(wxCloseEvent& event) { } -void CTaskBarIcon::OnRefresh(wxTimerEvent& WXUNUSED(event)) { +void CTaskBarIcon::OnRefresh(CTaskbarEvent& WXUNUSED(event)) { wxLogTrace(wxT("Function Start/End"), wxT("CTaskBarIcon::OnRefresh - Function Begin")); CMainDocument* pDoc = wxGetApp().GetDocument(); @@ -643,7 +635,7 @@ void CTaskBarIcon::AdjustMenuItems(wxMenu* pMenu) { for (loc = 0; loc < pMenu->GetMenuItemCount(); loc++) { pMenuItem = pMenu->FindItemByPosition(loc); - if (is_dialog_detected) { + if (is_dialog_detected && (pMenuItem->GetId() != wxID_OPEN)) { pMenuItem->Enable(false); } else { pMenuItem->Enable(!(pMenuItem->IsSeparator())); @@ -680,11 +672,15 @@ void CTaskBarIcon::AdjustMenuItems(wxMenu* pMenu) { pMenu->Enable(ID_TB_SUSPEND, false); } else { pMenu->Check(ID_TB_SUSPEND, true); + if (!is_dialog_detected) { pMenu->Enable(ID_TB_SUSPEND, true); + } } } else { pMenu->Check(ID_TB_SUSPEND, false); + if (!is_dialog_detected) { pMenu->Enable(ID_TB_SUSPEND, true); + } } } diff --git a/clientgui/BOINCTaskBar.h b/clientgui/BOINCTaskBar.h index 1fdb154c90..a09afa6227 100644 --- a/clientgui/BOINCTaskBar.h +++ b/clientgui/BOINCTaskBar.h @@ -46,7 +46,7 @@ public: void OnIdle(wxIdleEvent& event); void OnClose(wxCloseEvent& event); - void OnRefresh(wxTimerEvent& event); + void OnRefresh(CTaskbarEvent& event); void OnReloadSkin(CTaskbarEvent& event); void OnMouseMove(wxTaskBarIconEvent& event); @@ -80,8 +80,6 @@ public: private: wxDateTime m_dtLastHoverDetected; - wxTimer* m_pRefreshTimer; - bool m_bMouseButtonPressed; void ResetTaskBar(); @@ -116,9 +114,11 @@ public: BEGIN_DECLARE_EVENT_TYPES() DECLARE_EVENT_TYPE( wxEVT_TASKBAR_RELOADSKIN, 10100 ) +DECLARE_EVENT_TYPE( wxEVT_TASKBAR_REFRESH, 10101 ) END_DECLARE_EVENT_TYPES() #define EVT_TASKBAR_RELOADSKIN(fn) DECLARE_EVENT_TABLE_ENTRY(wxEVT_TASKBAR_RELOADSKIN, -1, -1, (wxObjectEventFunction) (wxEventFunction) &fn, NULL), +#define EVT_TASKBAR_REFRESH(fn) DECLARE_EVENT_TABLE_ENTRY(wxEVT_TASKBAR_REFRESH, -1, -1, (wxObjectEventFunction) (wxEventFunction) &fn, NULL), #endif diff --git a/clientgui/Events.h b/clientgui/Events.h index 1fe36b89c1..2a61a81c96 100644 --- a/clientgui/Events.h +++ b/clientgui/Events.h @@ -65,7 +65,6 @@ #define ID_SIMPLE_PREFERENCES 6606 #define ID_SIMPLE_MESSAGESVIEW 6607 #define ID_SIMPLE_SYNCHRONIZE 6608 -#define ID_TB_TIMER 6800 #define ID_TB_SUSPEND 6801 #define ID_LIST_BASE 7000 #define ID_LIST_PROJECTSVIEW 7000 diff --git a/clientgui/MainDocument.cpp b/clientgui/MainDocument.cpp index 4dd0ea294f..77d3e4b679 100644 --- a/clientgui/MainDocument.cpp +++ b/clientgui/MainDocument.cpp @@ -431,6 +431,7 @@ int CMainDocument::OnInit() { m_RPCWaitDlg = NULL; m_bWaitingForRPC = false; m_bNeedRefresh = false; + m_bNeedTaskBarRefresh = false; current_rpc_request.clear(); m_RPCThread = new RPCThread(this); @@ -775,9 +776,6 @@ void CMainDocument::RunPeriodicRPCs() { int currentTabView = wxGetApp().GetCurrentViewPage(); - // TODO: modify SimpleGUI to not do direct RPC calls when hidden / minimized - if (! ((currentTabView & VW_SGUI) || pFrame->IsShown()) ) return; - // Several functions (such as Abort, Reset, Detach) display an // "Are you sure?" dialog before passing a pointer to a result // or project in a demand RPC call. If Periodic RPCs continue @@ -787,7 +785,7 @@ void CMainDocument::RunPeriodicRPCs() { // // Note that this depends on using wxGetApp().SafeMessageBox() // instead of wxMessageBox in all tab views. - if (wxGetApp().IsModalDialogDisplayed() && !(currentTabView & VW_SMSG)) { + if (wxGetApp().IsModalDialogDisplayed() && (currentTabView != (VW_SGUI | VW_SMSG)) ) { return; } @@ -802,13 +800,30 @@ void CMainDocument::RunPeriodicRPCs() { request.which_rpc = RPC_GET_CC_STATUS; request.arg1 = &async_status_buf; request.exchangeBuf = &status; - request.rpcType = RPC_TYPE_ASYNC_NO_REFRESH; + request.rpcType = RPC_TYPE_ASYNC_WITH_UPDATE_TASKBAR_ICON_AFTER; request.completionTime = &m_dtCachedCCStatusTimestamp; request.resultPtr = &m_iGet_status_rpc_result; RequestRPC(request); } + // *********** RPC_GET_MESSAGES ************** + + request.clear(); + request.which_rpc = RPC_GET_MESSAGES; + // m_iMessageSequenceNumber could change between request and execution + // of RPC, so pass in a pointer rather than its value + request.arg1 = &m_iMessageSequenceNumber; + request.arg2 = &messages; +// request.arg2 = &async_messages_buf; +// request.exchangeBuf = &messages; + request.rpcType = (currentTabView & VW_MSGS) ? + RPC_TYPE_ASYNC_WITH_REFRESH_AFTER : RPC_TYPE_ASYNC_WITH_UPDATE_MESSAGE_LIST_AFTER; + request.completionTime = NULL; + request.resultPtr = &m_iGet_messages_rpc_result; + + RequestRPC(request); + ts = dtNow - m_dtCachedStateTimestamp; if (ts.GetSeconds() >= STATERPC_INTERVAL) { @@ -835,7 +850,10 @@ void CMainDocument::RunPeriodicRPCs() { RequestRPC(request); } - + + // TODO: modify SimpleGUI to not do direct RPC calls when hidden / minimized + if (! ((currentTabView & VW_SGUI) || pFrame->IsShown()) ) return; + // *********** RPC_GET_PROJECT_STATUS1 ************** if (currentTabView & VW_PROJ) { @@ -852,7 +870,7 @@ void CMainDocument::RunPeriodicRPCs() { RequestRPC(request); } } - + // *********** RPC_GET_RESULTS ************** if (currentTabView & VW_TASK) { @@ -887,26 +905,6 @@ void CMainDocument::RunPeriodicRPCs() { } } - // *********** RPC_GET_MESSAGES ************** - - if (currentTabView & (VW_MSGS | VW_SGUI)) { - request.clear(); - request.which_rpc = RPC_GET_MESSAGES; - // m_iMessageSequenceNumber could change between request and execution - // of RPC, so pass in a pointer rather than its value - request.arg1 = &m_iMessageSequenceNumber; - request.arg2 = &messages; -// request.arg2 = &async_messages_buf; -// request.exchangeBuf = &messages; - - request.rpcType = (currentTabView & VW_SGUI) ? - RPC_TYPE_ASYNC_NO_REFRESH : RPC_TYPE_ASYNC_WITH_REFRESH_AFTER; - request.completionTime = NULL; - request.resultPtr = &m_iGet_messages_rpc_result; - - RequestRPC(request); - } - // *********** RPC_GET_STATISTICS ************** if (currentTabView & VW_STAT) { @@ -1700,6 +1698,7 @@ int CMainDocument::WorkAbort(std::string& strProjectURL, std::string& strName) { // Call this only when message buffer is stable +// Note: This must not call any rpcs. int CMainDocument::CachedMessageUpdate() { static bool in_this_func = false; diff --git a/clientgui/MainDocument.h b/clientgui/MainDocument.h index 2be3deccf5..9d28b3d53c 100644 --- a/clientgui/MainDocument.h +++ b/clientgui/MainDocument.h @@ -180,10 +180,12 @@ public: void HandleCompletedRPC(); ASYNC_RPC_REQUEST* GetCurrentRPCRequest() { return ¤t_rpc_request; } bool WaitingForRPC() { return m_bWaitingForRPC; } + wxDialog* GetRPCWaitDialog() { return m_RPCWaitDlg; } // void TestAsyncRPC(); // For testing Async RPCs RPCThread* m_RPCThread; wxCriticalSection m_critsect; bool m_bNeedRefresh; + bool m_bNeedTaskBarRefresh; private: int CopyProjectsToStateFile(PROJECTS& p, CC_STATE& state);