Index: lldb/trunk/include/lldb/Core/Event.h =================================================================== --- lldb/trunk/include/lldb/Core/Event.h +++ lldb/trunk/include/lldb/Core/Event.h @@ -121,9 +121,8 @@ const ConstString &GetFlavor() const override { return GetFlavorString(); } - bool WaitForEventReceived( - const std::chrono::microseconds &abstime = std::chrono::microseconds(0)) { - return m_predicate.WaitForValueEqualTo(true, abstime); + bool WaitForEventReceived(const Timeout &timeout = llvm::None) { + return m_predicate.WaitForValueEqualTo(true, timeout); } private: Index: lldb/trunk/include/lldb/Host/Predicate.h =================================================================== --- lldb/trunk/include/lldb/Host/Predicate.h +++ lldb/trunk/include/lldb/Host/Predicate.h @@ -20,6 +20,7 @@ // Other libraries and framework includes // Project includes +#include "lldb/Utility/Timeout.h" #include "lldb/lldb-defines.h" //#define DB_PTHREAD_LOG_EVENTS @@ -118,6 +119,40 @@ } //------------------------------------------------------------------ + /// Wait for Cond(m_value) to be true. + /// + /// Waits in a thread safe way for Cond(m_value) to be true. If Cond(m_value) + /// is already true, this function will return without waiting. + /// + /// It is possible for the value to be changed between the time the value is + /// set and the time the waiting thread wakes up. If the value no longer + /// satisfies the condition when the waiting thread wakes up, it will go back + /// into a wait state. It may be necessary for the calling code to use + /// additional thread synchronization methods to detect transitory states. + /// + /// @param[in] Cond + /// The condition we want \a m_value satisfy. + /// + /// @param[in] timeout + /// How long to wait for the condition to hold. + /// + /// @return + /// @li m_value if Cond(m_value) is true. + /// @li None otherwise (timeout occurred). + //------------------------------------------------------------------ + template + llvm::Optional WaitFor(C Cond, const Timeout &timeout) { + std::unique_lock lock(m_mutex); + auto RealCond = [&] { return Cond(m_value); }; + if (!timeout) { + m_condition.wait(lock, RealCond); + return m_value; + } + if (m_condition.wait_for(lock, *timeout, RealCond)) + return m_value; + return llvm::None; + } + //------------------------------------------------------------------ /// Wait for \a m_value to be equal to \a value. /// /// Waits in a thread safe way for \a m_value to be equal to \a @@ -134,38 +169,17 @@ /// @param[in] value /// The value we want \a m_value to be equal to. /// - /// @param[in] abstime - /// If non-nullptr, the absolute time at which we should stop - /// waiting, else wait an infinite amount of time. + /// @param[in] timeout + /// How long to wait for the condition to hold. /// /// @return /// @li \b true if the \a m_value is equal to \a value /// @li \b false otherwise (timeout occurred) //------------------------------------------------------------------ - bool WaitForValueEqualTo(T value, const std::chrono::microseconds &timeout = - std::chrono::microseconds(0)) { - // pthread_cond_timedwait() or pthread_cond_wait() will atomically unlock - // the mutex and wait for the condition to be set. When either function - // returns, they will re-lock the mutex. We use an auto lock/unlock class - // (std::lock_guard) to allow us to return at any point in this function - // and not have to worry about unlocking the mutex. - std::unique_lock lock(m_mutex); - -#ifdef DB_PTHREAD_LOG_EVENTS - printf("%s (value = 0x%8.8x, timeout = %llu), m_value = 0x%8.8x\n", - __FUNCTION__, value, timeout.count(), m_value); -#endif - while (m_value != value) { - if (timeout == std::chrono::microseconds(0)) { - m_condition.wait(lock); - } else { - std::cv_status result = m_condition.wait_for(lock, timeout); - if (result == std::cv_status::timeout) - break; - } - } - - return m_value == value; + bool WaitForValueEqualTo(T value, + const Timeout &timeout = llvm::None) { + return WaitFor([&value](T current) { return value == current; }, timeout) != + llvm::None; } //------------------------------------------------------------------ @@ -185,45 +199,17 @@ /// @param[in] value /// The value we want \a m_value to not be equal to. /// - /// @param[out] new_value - /// The new value if \b true is returned. - /// - /// @param[in] abstime - /// If non-nullptr, the absolute time at which we should stop - /// waiting, else wait an infinite amount of time. + /// @param[in] timeout + /// How long to wait for the condition to hold. /// /// @return - /// @li \b true if the \a m_value is equal to \a value - /// @li \b false otherwise + /// @li m_value if m_value != value + /// @li None otherwise (timeout occurred). //------------------------------------------------------------------ - bool WaitForValueNotEqualTo( - T value, T &new_value, - const std::chrono::microseconds &timeout = std::chrono::microseconds(0)) { - // pthread_cond_timedwait() or pthread_cond_wait() will atomically unlock - // the mutex and wait for the condition to be set. When either function - // returns, they will re-lock the mutex. We use an auto lock/unlock class - // (std::lock_guard) to allow us to return at any point in this function - // and not have to worry about unlocking the mutex. - std::unique_lock lock(m_mutex); -#ifdef DB_PTHREAD_LOG_EVENTS - printf("%s (value = 0x%8.8x, timeout = %llu), m_value = 0x%8.8x\n", - __FUNCTION__, value, timeout.count(), m_value); -#endif - while (m_value == value) { - if (timeout == std::chrono::microseconds(0)) { - m_condition.wait(lock); - } else { - std::cv_status result = m_condition.wait_for(lock, timeout); - if (result == std::cv_status::timeout) - break; - } - } - - if (m_value != value) { - new_value = m_value; - return true; - } - return false; + llvm::Optional + WaitForValueNotEqualTo(T value, + const Timeout &timeout = llvm::None) { + return WaitFor([&value](T current) { return value != current; }, timeout); } protected: Index: lldb/trunk/include/lldb/Host/posix/ConnectionFileDescriptorPosix.h =================================================================== --- lldb/trunk/include/lldb/Host/posix/ConnectionFileDescriptorPosix.h +++ lldb/trunk/include/lldb/Host/posix/ConnectionFileDescriptorPosix.h @@ -72,7 +72,7 @@ lldb::IOObjectSP GetReadObject() override { return m_read_sp; } - uint16_t GetListeningPort(uint32_t timeout_sec); + uint16_t GetListeningPort(const Timeout &timeout); bool GetChildProcessesInherit() const; void SetChildProcessesInherit(bool child_processes_inherit); Index: lldb/trunk/include/lldb/Target/Process.h =================================================================== --- lldb/trunk/include/lldb/Target/Process.h +++ lldb/trunk/include/lldb/Target/Process.h @@ -2443,11 +2443,11 @@ /// The main purpose of this is to implement an interlock waiting for /// HandlePrivateEvent to push an IOHandler. /// - /// @param[in] timeout_msec + /// @param[in] timeout /// The maximum time length to wait for the process to transition to the - /// eStateRunning state, specified in milliseconds. + /// eStateRunning state. //-------------------------------------------------------------------------------------- - void SyncIOHandler(uint32_t iohandler_id, uint64_t timeout_msec); + void SyncIOHandler(uint32_t iohandler_id, const Timeout &timeout); lldb::StateType GetStateChangedEvents( lldb::EventSP &event_sp, const Timeout &timeout, Index: lldb/trunk/source/Commands/CommandObjectProcess.cpp =================================================================== --- lldb/trunk/source/Commands/CommandObjectProcess.cpp +++ lldb/trunk/source/Commands/CommandObjectProcess.cpp @@ -237,7 +237,7 @@ // stack to the main command handler and show an (lldb) prompt before // HandlePrivateEvent (from PrivateStateThread) has a chance to call // PushProcessIOHandler(). - process_sp->SyncIOHandler(0, 2000); + process_sp->SyncIOHandler(0, std::chrono::seconds(2)); llvm::StringRef data = stream.GetString(); if (!data.empty()) @@ -691,7 +691,7 @@ // stack to the main command handler and show an (lldb) prompt before // HandlePrivateEvent (from PrivateStateThread) has a chance to call // PushProcessIOHandler(). - process->SyncIOHandler(iohandler_id, 2000); + process->SyncIOHandler(iohandler_id, std::chrono::seconds(2)); result.AppendMessageWithFormat("Process %" PRIu64 " resuming\n", process->GetID()); Index: lldb/trunk/source/Commands/CommandObjectThread.cpp =================================================================== --- lldb/trunk/source/Commands/CommandObjectThread.cpp +++ lldb/trunk/source/Commands/CommandObjectThread.cpp @@ -786,7 +786,7 @@ // stack to the main command handler and show an (lldb) prompt before // HandlePrivateEvent (from PrivateStateThread) has a chance to call // PushProcessIOHandler(). - process->SyncIOHandler(iohandler_id, 2000); + process->SyncIOHandler(iohandler_id, std::chrono::seconds(2)); if (synchronous_execution) { // If any state changed events had anything to say, add that to the Index: lldb/trunk/source/Host/common/Host.cpp =================================================================== --- lldb/trunk/source/Host/common/Host.cpp +++ lldb/trunk/source/Host/common/Host.cpp @@ -536,8 +536,11 @@ error.SetErrorString("failed to get process ID"); if (error.Success()) { - if (!shell_info_sp->process_reaped.WaitForValueEqualTo( - true, std::chrono::seconds(timeout_sec))) { + // TODO: Remove this and make the function take Timeout<> argument. + Timeout timeout(llvm::None); + if (timeout_sec != 0) + timeout = std::chrono::seconds(timeout_sec); + if (!shell_info_sp->process_reaped.WaitForValueEqualTo(true, timeout)) { error.SetErrorString("timed out waiting for shell command to complete"); // Kill the process since it didn't complete within the timeout specified Index: lldb/trunk/source/Host/posix/ConnectionFileDescriptorPosix.cpp =================================================================== --- lldb/trunk/source/Host/posix/ConnectionFileDescriptorPosix.cpp +++ lldb/trunk/source/Host/posix/ConnectionFileDescriptorPosix.cpp @@ -746,14 +746,10 @@ return eConnectionStatusSuccess; } -uint16_t ConnectionFileDescriptor::GetListeningPort(uint32_t timeout_sec) { - uint16_t bound_port = 0; - if (timeout_sec == UINT32_MAX) - m_port_predicate.WaitForValueNotEqualTo(0, bound_port); - else - m_port_predicate.WaitForValueNotEqualTo(0, bound_port, - std::chrono::seconds(timeout_sec)); - return bound_port; +uint16_t +ConnectionFileDescriptor::GetListeningPort(const Timeout &timeout) { + auto Result = m_port_predicate.WaitForValueNotEqualTo(0, timeout); + return Result ? *Result : 0; } bool ConnectionFileDescriptor::GetChildProcessesInherit() const { Index: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp =================================================================== --- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp +++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -1133,7 +1133,7 @@ ConnectionFileDescriptor *connection = (ConnectionFileDescriptor *)GetConnection(); // Wait for 10 seconds to resolve the bound port - uint16_t port_ = connection->GetListeningPort(10); + uint16_t port_ = connection->GetListeningPort(std::chrono::seconds(10)); if (port_ > 0) { char port_cstr[32]; snprintf(port_cstr, sizeof(port_cstr), "127.0.0.1:%i", port_); Index: lldb/trunk/source/Target/Process.cpp =================================================================== --- lldb/trunk/source/Target/Process.cpp +++ lldb/trunk/source/Target/Process.cpp @@ -947,21 +947,25 @@ return state; } -void Process::SyncIOHandler(uint32_t iohandler_id, uint64_t timeout_msec) { +void Process::SyncIOHandler(uint32_t iohandler_id, + const Timeout &timeout) { // don't sync (potentially context switch) in case where there is no process // IO if (!m_process_input_reader) return; - uint32_t new_iohandler_id = 0; - m_iohandler_sync.WaitForValueNotEqualTo( - iohandler_id, new_iohandler_id, std::chrono::milliseconds(timeout_msec)); + auto Result = m_iohandler_sync.WaitForValueNotEqualTo(iohandler_id, timeout); Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); - if (log) - log->Printf("Process::%s waited for m_iohandler_sync to change from %u, " - "new value is %u", - __FUNCTION__, iohandler_id, new_iohandler_id); + if (Result) { + LLDB_LOG( + log, + "waited from m_iohandler_sync to change from {0}. New value is {1}.", + iohandler_id, *Result); + } else { + LLDB_LOG(log, "timed out waiting for m_iohandler_sync to change from {0}.", + iohandler_id); + } } StateType Process::WaitForProcessToStop(const Timeout &timeout, Index: lldb/trunk/unittests/Host/CMakeLists.txt =================================================================== --- lldb/trunk/unittests/Host/CMakeLists.txt +++ lldb/trunk/unittests/Host/CMakeLists.txt @@ -3,6 +3,7 @@ HostInfoTest.cpp HostTest.cpp MainLoopTest.cpp + PredicateTest.cpp SocketAddressTest.cpp SocketTest.cpp SymbolsTest.cpp Index: lldb/trunk/unittests/Host/PredicateTest.cpp =================================================================== --- lldb/trunk/unittests/Host/PredicateTest.cpp +++ lldb/trunk/unittests/Host/PredicateTest.cpp @@ -0,0 +1,34 @@ +//===-- PredicateTest.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Predicate.h" +#include "gtest/gtest.h" +#include + +using namespace lldb_private; + +TEST(Predicate, WaitForValueEqualTo) { + Predicate P(0); + EXPECT_TRUE(P.WaitForValueEqualTo(0)); + EXPECT_FALSE(P.WaitForValueEqualTo(1, std::chrono::milliseconds(10))); + + std::thread Setter([&P] { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + P.SetValue(1, eBroadcastAlways); + }); + EXPECT_TRUE(P.WaitForValueEqualTo(1)); + Setter.join(); +} + +TEST(Predicate, WaitForValueNotEqualTo) { + Predicate P(0); + EXPECT_EQ(0, P.WaitForValueNotEqualTo(1)); + EXPECT_EQ(llvm::None, + P.WaitForValueNotEqualTo(0, std::chrono::milliseconds(10))); +}