Index: tools/lldb-mi/MICmnLLDBDebugger.h =================================================================== --- tools/lldb-mi/MICmnLLDBDebugger.h +++ tools/lldb-mi/MICmnLLDBDebugger.h @@ -10,8 +10,9 @@ #pragma once // Third party headers -#include +#include #include +#include #include "lldb/API/SBDebugger.h" #include "lldb/API/SBListener.h" #include "lldb/API/SBEvent.h" @@ -48,6 +49,7 @@ CMIDriverBase &GetDriver(void) const; lldb::SBDebugger &GetTheDebugger(void); lldb::SBListener &GetTheListener(void); + void WaitForHandleEvent(void); // MI Commands can use these functions to listen for events they require bool RegisterForEvent(const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass, const MIuint vEventMask); @@ -106,4 +108,6 @@ const CMIUtilString m_constStrThisThreadId; MapBroadcastClassNameToEventMask_t m_mapBroadcastClassNameToEventMask; MapIdToEventMask_t m_mapIdToEventMask; + std::mutex m_mutexEventQueue; + std::condition_variable m_conditionEventQueueEmpty; }; Index: tools/lldb-mi/MICmnLLDBDebugger.cpp =================================================================== --- tools/lldb-mi/MICmnLLDBDebugger.cpp +++ tools/lldb-mi/MICmnLLDBDebugger.cpp @@ -222,6 +222,28 @@ } //++ ------------------------------------------------------------------------------------ +// Details: Wait until all events have been handled. +// This function works in pair with CMICmnLLDBDebugger::MonitorSBListenerEvents +// that handles events from queue. When all events were handled and queue is +// empty the MonitorSBListenerEvents notifies this function that it's ready to +// go on. To synchronize them the m_mutexEventQueue and +// m_conditionEventQueueEmpty are used. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void +CMICmnLLDBDebugger::WaitForHandleEvent(void) +{ + std::unique_lock lock(m_mutexEventQueue); + + lldb::SBEvent event; + if (ThreadIsActive() && m_lldbListener.PeekAtNextEvent(event)) + m_conditionEventQueueEmpty.wait(lock); +} + +//++ ------------------------------------------------------------------------------------ // Details: Initialize the LLDB Debugger object. // Type: Method. // Args: None. @@ -642,39 +664,48 @@ { vrbIsAlive = true; + // Lock the mutex of event queue + // Note that it should be locked while we are in CMICmnLLDBDebugger::MonitorSBListenerEvents to + // avoid a race condition with CMICmnLLDBDebugger::WaitForHandleEvent + std::unique_lock lock(m_mutexEventQueue); + lldb::SBEvent event; const bool bGotEvent = m_lldbListener.GetNextEvent(event); - if (!bGotEvent || !event.IsValid()) + if (!bGotEvent) { + // Notify that we are finished and unlock the mutex of event queue before sleeping + m_conditionEventQueueEmpty.notify_one(); + lock.unlock(); + + // Wait a bit to reduce CPU load const std::chrono::milliseconds time(1); std::this_thread::sleep_for(time); return MIstatus::success; } - if (!event.GetBroadcaster().IsValid()) - return MIstatus::success; + assert(event.IsValid()); + assert(event.GetBroadcaster().IsValid()); // Debugging m_pLog->WriteLog(CMIUtilString::Format("##### An event occurred: %s", event.GetBroadcasterClass())); bool bHandledEvent = false; - bool bOk = false; { // Lock Mutex before handling events so that we don't disturb a running cmd CMIUtilThreadLock lock(CMICmnLLDBDebugSessionInfo::Instance().GetSessionMutex()); bOk = CMICmnLLDBDebuggerHandleEvents::Instance().HandleEvent(event, bHandledEvent); } + if (!bHandledEvent) { const CMIUtilString msg(CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_WRN_UNKNOWN_EVENT), event.GetBroadcasterClass())); m_pLog->WriteLog(msg); } + if (!bOk) - { m_pLog->WriteLog(CMICmnLLDBDebuggerHandleEvents::Instance().GetErrorDescription()); - } - return bOk; + return MIstatus::success; } //++ ------------------------------------------------------------------------------------ Index: tools/lldb-mi/MIDriver.cpp =================================================================== --- tools/lldb-mi/MIDriver.cpp +++ tools/lldb-mi/MIDriver.cpp @@ -536,12 +536,8 @@ CMIUtilString lineText(pCmd); if (!lineText.empty ()) { - if (lineText == "quit") - { - // We want to be exiting when receiving a quit command - m_bExitApp = true; - break; - } + // Check that the handler thread is alive (otherwise we stuck here) + assert(CMICmnLLDBDebugger::Instance().ThreadIsActive()); { // Lock Mutex before processing commands so that we don't disturb an event @@ -549,9 +545,13 @@ CMIUtilThreadLock lock(CMICmnLLDBDebugSessionInfo::Instance().GetSessionMutex()); bOk = InterpretCommand(lineText); } + // Draw prompt if desired if (bOk && m_rStdin.GetEnablePrompt()) bOk = m_rStdOut.WriteMIResponse(m_rStdin.GetPrompt()); + + // Wait while the handler thread handles incoming events + CMICmnLLDBDebugger::Instance().WaitForHandleEvent(); } } }