Index: include/lldb/Utility/SelectHelper.h =================================================================== --- include/lldb/Utility/SelectHelper.h +++ include/lldb/Utility/SelectHelper.h @@ -0,0 +1,90 @@ +//===-- SelectHelper.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SelectHelper_h_ +#define liblldb_SelectHelper_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" + +// Project includes +#include "lldb/lldb-forward.h" + +class SelectHelper +{ +public: + + // Defaults to infinite wait for select unless you call SetTimeout() + SelectHelper(); + + // Call SetTimeout() before calling SelectHelper::Select() to set the + // timeout based on the current time + the timeout. This allows multiple + // calls to SelectHelper::Select() without having to worry about the + // absolute timeout as this class manages to set the relative timeout + // correctly. + void SetTimeout(const std::chrono::microseconds &timeout); + + // Call the FDSet*() functions before calling SelectHelper::Select() to + // set the file descriptors that we will watch for when calling + // select. This will cause FD_SET() to be called prior to calling select + // using the "fd" provided. + void FDSetRead(int fd); + void FDSetWrite(int fd); + void FDSetError(int fd); + + // Call the FDIsSet*() functions after calling SelectHelper::Select() + // to check which file descriptors are ready for read/write/error. This + // will contain the result of FD_ISSET after calling select for a given + // file descriptor. + bool FDIsSetRead(int fd) const; + bool FDIsSetWrite(int fd) const; + bool FDIsSetError(int fd) const; + + // Call the system's select() to wait for descriptors using + // timeout provided in a call the SelectHelper::SetTimeout(), + // or infinite wait if no timeout was set. + lldb_private::Error Select(); +protected: + struct FDInfo + { + FDInfo() : + read_set(false), + write_set(false), + error_set(false), + read_is_set(false), + write_is_set(false), + error_is_set(false) + { + } + + void + PrepareForSelect() + { + read_is_set = false; + write_is_set = false; + error_is_set = false; + } + + bool read_set : 1, + write_set : 1, + error_set : 1, + read_is_set : 1, + write_is_set : 1, + error_is_set : 1; + }; + llvm::DenseMap m_fd_map; + llvm::Optional m_end_time; +}; + +#endif // liblldb_SelectHelper_h_ Index: lldb.xcodeproj/project.pbxproj =================================================================== --- lldb.xcodeproj/project.pbxproj +++ lldb.xcodeproj/project.pbxproj @@ -577,6 +577,7 @@ 2697A54D133A6305004E4240 /* PlatformDarwin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2697A54B133A6305004E4240 /* PlatformDarwin.cpp */; }; 2698699B15E6CBD0002415FF /* OperatingSystemPython.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2698699815E6CBD0002415FF /* OperatingSystemPython.cpp */; }; 269DDD4A1B8FD1C300D0DBD8 /* DWARFASTParserClang.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 269DDD481B8FD1C300D0DBD8 /* DWARFASTParserClang.cpp */; }; + 26A375811D59462700D6CBDB /* SelectHelper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A3757F1D59462700D6CBDB /* SelectHelper.cpp */; }; 26A527C114E24F5F00F3A14A /* ProcessMachCore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A527BD14E24F5F00F3A14A /* ProcessMachCore.cpp */; }; 26A527C314E24F5F00F3A14A /* ThreadMachCore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A527BF14E24F5F00F3A14A /* ThreadMachCore.cpp */; }; 26A69C5F137A17A500262477 /* RegisterValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26C6886E137880C400407EDF /* RegisterValue.cpp */; }; @@ -1557,7 +1558,6 @@ 265ABF6210F42EE900531910 /* DebugSymbols.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DebugSymbols.framework; path = /System/Library/PrivateFrameworks/DebugSymbols.framework; sourceTree = ""; }; 265E9BE1115C2BAA00D0DCCB /* debugserver.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = debugserver.xcodeproj; path = tools/debugserver/debugserver.xcodeproj; sourceTree = ""; }; 2660D9F611922A1300958FBD /* StringExtractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StringExtractor.cpp; path = source/Utility/StringExtractor.cpp; sourceTree = ""; }; - 2660D9F711922A1300958FBD /* StringExtractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StringExtractor.h; path = source/Utility/StringExtractor.h; sourceTree = ""; }; 2660D9FE11922A7F00958FBD /* ThreadPlanStepUntil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanStepUntil.cpp; path = source/Target/ThreadPlanStepUntil.cpp; sourceTree = ""; }; 26651A14133BEC76005B64B7 /* lldb-public.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "lldb-public.h"; path = "include/lldb/lldb-public.h"; sourceTree = ""; }; 26651A15133BF9CC005B64B7 /* Opcode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Opcode.h; path = include/lldb/Core/Opcode.h; sourceTree = ""; }; @@ -1833,6 +1833,9 @@ 26A0604711A5BC7A00F75969 /* Baton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Baton.h; path = include/lldb/Core/Baton.h; sourceTree = ""; }; 26A0604811A5D03C00F75969 /* Baton.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Baton.cpp; path = source/Core/Baton.cpp; sourceTree = ""; }; 26A0DA4D140F721D006DA411 /* HashedNameToDIE.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HashedNameToDIE.h; sourceTree = ""; }; + 26A3757F1D59462700D6CBDB /* SelectHelper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SelectHelper.cpp; path = source/Utility/SelectHelper.cpp; sourceTree = ""; }; + 26A375831D59486000D6CBDB /* StringExtractor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = StringExtractor.h; path = include/lldb/Utility/StringExtractor.h; sourceTree = ""; }; + 26A375841D59487700D6CBDB /* SelectHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SelectHelper.h; path = include/lldb/Utility/SelectHelper.h; sourceTree = ""; }; 26A3B4AC1181454800381BC2 /* ObjectContainerBSDArchive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ObjectContainerBSDArchive.cpp; sourceTree = ""; }; 26A3B4AD1181454800381BC2 /* ObjectContainerBSDArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectContainerBSDArchive.h; sourceTree = ""; }; 26A4EEB511682AAC007A372A /* LLDBWrapPython.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = LLDBWrapPython.cpp; sourceTree = BUILT_PRODUCTS_DIR; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; @@ -3912,10 +3915,12 @@ 2682F16B115EDA0D00CCFF99 /* PseudoTerminal.h */, 2682F16A115EDA0D00CCFF99 /* PseudoTerminal.cpp */, 4CAB257C18EC9DB800BAD33E /* SafeMachO.h */, + 26A375841D59487700D6CBDB /* SelectHelper.h */, + 26A3757F1D59462700D6CBDB /* SelectHelper.cpp */, 261B5A5211C3F2AD00AABD0A /* SharingPtr.cpp */, 4C2FAE2E135E3A70001EDE44 /* SharedCluster.h */, 261B5A5311C3F2AD00AABD0A /* SharingPtr.h */, - 2660D9F711922A1300958FBD /* StringExtractor.h */, + 26A375831D59486000D6CBDB /* StringExtractor.h */, 2660D9F611922A1300958FBD /* StringExtractor.cpp */, 2676A094119C93C8008A98EF /* StringExtractorGDBRemote.h */, 2676A093119C93C8008A98EF /* StringExtractorGDBRemote.cpp */, @@ -7224,6 +7229,7 @@ 2698699B15E6CBD0002415FF /* OperatingSystemPython.cpp in Sources */, 947A1D641616476B0017C8D1 /* CommandObjectPlugin.cpp in Sources */, 2666ADC81B3CB675001FAFD3 /* HexagonDYLDRendezvous.cpp in Sources */, + 26A375811D59462700D6CBDB /* SelectHelper.cpp in Sources */, AE44FB471BB4BB090033EB62 /* GoLanguage.cpp in Sources */, 262ED0081631FA3A00879631 /* OptionGroupString.cpp in Sources */, 94F48F251A01C687005C0EC6 /* StringPrinter.cpp in Sources */, Index: source/Host/common/Editline.cpp =================================================================== --- source/Host/common/Editline.cpp +++ source/Host/common/Editline.cpp @@ -20,6 +20,7 @@ #include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Utility/LLDBAssert.h" +#include "lldb/Utility/SelectHelper.h" using namespace lldb_private; using namespace lldb_private::line_editor; @@ -155,11 +156,10 @@ // instead use some kind of yet-to-be-created abstraction that select-like functionality on // non-socket objects. const int fd = fileno (file); - fd_set fds; - FD_ZERO (&fds); - FD_SET (fd, &fds); - timeval timeout = { 0, 0 }; - return select (fd + 1, &fds, NULL, NULL, &timeout); + SelectHelper select_helper; + select_helper.SetTimeout(std::chrono::microseconds(0)); + select_helper.FDSetRead(fd); + return select_helper.Select().Success(); } namespace lldb_private Index: source/Host/posix/ConnectionFileDescriptorPosix.cpp =================================================================== --- source/Host/posix/ConnectionFileDescriptorPosix.cpp +++ source/Host/posix/ConnectionFileDescriptorPosix.cpp @@ -20,6 +20,7 @@ #include "lldb/Host/SocketAddress.h" #include "lldb/Host/Socket.h" #include "lldb/Host/StringConvert.h" +#include "lldb/Utility/SelectHelper.h" // C Includes #include @@ -572,7 +573,7 @@ return m_uri; } -// This ConnectionFileDescriptor::BytesAvailable() uses select(). +// This ConnectionFileDescriptor::BytesAvailable() uses select() via SelectHelper // // PROS: // - select is consistent across most unix platforms @@ -586,11 +587,6 @@ // be used or a new version of ConnectionFileDescriptor::BytesAvailable() // should be written for the system that is running into the limitations. -#if defined(__APPLE__) -#define FD_SET_DATA(fds) fds.data() -#else -#define FD_SET_DATA(fds) &fds -#endif ConnectionStatus ConnectionFileDescriptor::BytesAvailable(uint32_t timeout_usec, Error *error_ptr) @@ -602,21 +598,6 @@ if (log) log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", static_cast(this), timeout_usec); - struct timeval *tv_ptr; - struct timeval tv; - if (timeout_usec == UINT32_MAX) - { - // Infinite wait... - tv_ptr = nullptr; - } - else - { - TimeValue time_value; - time_value.OffsetWithMicroSeconds(timeout_usec); - tv.tv_sec = time_value.seconds(); - tv.tv_usec = time_value.microseconds(); - tv_ptr = &tv; - } // Make a copy of the file descriptors to make sure we don't // have another thread change these values out from under us @@ -624,8 +605,14 @@ const IOObject::WaitableHandle handle = m_read_sp->GetWaitableHandle(); const int pipe_fd = m_pipe.GetReadFileDescriptor(); + if (handle != IOObject::kInvalidHandleValue) { + SelectHelper select_helper; + if (timeout_usec != UINT32_MAX) + select_helper.SetTimeout(std::chrono::microseconds(timeout_usec)); + + select_helper.FDSetRead(handle); #if defined(_MSC_VER) // select() won't accept pipes on Windows. The entire Windows codepath needs to be // converted over to using WaitForMultipleObjects and event HANDLEs, but for now at least @@ -633,62 +620,14 @@ const bool have_pipe_fd = false; #else const bool have_pipe_fd = pipe_fd >= 0; -#if !defined(__APPLE__) - assert(handle < FD_SETSIZE); - if (have_pipe_fd) - assert(pipe_fd < FD_SETSIZE); -#endif #endif + if (have_pipe_fd) + select_helper.FDSetRead(pipe_fd); + while (handle == m_read_sp->GetWaitableHandle()) { - const int nfds = std::max(handle, pipe_fd) + 1; -#if defined(__APPLE__) - llvm::SmallVector read_fds; - read_fds.resize((nfds / FD_SETSIZE) + 1); - for (size_t i = 0; i < read_fds.size(); ++i) - FD_ZERO(&read_fds[i]); -// FD_SET doesn't bounds check, it just happily walks off the end -// but we have taken care of making the extra storage with our -// SmallVector of fd_set objects -#else - fd_set read_fds; - FD_ZERO(&read_fds); -#endif - FD_SET(handle, FD_SET_DATA(read_fds)); - if (have_pipe_fd) - FD_SET(pipe_fd, FD_SET_DATA(read_fds)); - Error error; - - if (log) - { - if (have_pipe_fd) - log->Printf( - "%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p)...", - static_cast(this), nfds, handle, pipe_fd, static_cast(tv_ptr)); - else - log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p)...", - static_cast(this), nfds, handle, static_cast(tv_ptr)); - } - - const int num_set_fds = ::select(nfds, FD_SET_DATA(read_fds), NULL, NULL, tv_ptr); - if (num_set_fds < 0) - error.SetErrorToErrno(); - else - error.Clear(); - - if (log) - { - if (have_pipe_fd) - log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p) " - "=> %d, error = %s", - static_cast(this), nfds, handle, pipe_fd, static_cast(tv_ptr), num_set_fds, - error.AsCString()); - else - log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p) => " - "%d, error = %s", - static_cast(this), nfds, handle, static_cast(tv_ptr), num_set_fds, error.AsCString()); - } + Error error = select_helper.Select(); if (error_ptr) *error_ptr = error; @@ -704,6 +643,9 @@ default: // Other unknown error return eConnectionStatusError; + case ETIMEDOUT: + return eConnectionStatusTimedOut; + case EAGAIN: // The kernel was (perhaps temporarily) unable to // allocate the requested number of file descriptors, // or we have non-blocking IO @@ -713,15 +655,12 @@ break; // Lets keep reading to until we timeout } } - else if (num_set_fds == 0) - { - return eConnectionStatusTimedOut; - } - else if (num_set_fds > 0) + else { - if (FD_ISSET(handle, FD_SET_DATA(read_fds))) + if (select_helper.FDIsSetRead(handle)) return eConnectionStatusSuccess; - if (have_pipe_fd && FD_ISSET(pipe_fd, FD_SET_DATA(read_fds))) + + if (select_helper.FDIsSetRead(pipe_fd)) { // There is an interrupt or exit command in the command pipe // Read the data from that pipe: Index: source/Host/posix/PipePosix.cpp =================================================================== --- source/Host/posix/PipePosix.cpp +++ source/Host/posix/PipePosix.cpp @@ -10,6 +10,7 @@ #include "lldb/Host/posix/PipePosix.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" +#include "lldb/Utility/SelectHelper.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/FileSystem.h" @@ -65,73 +66,6 @@ return std::chrono::steady_clock::now(); } -Error -SelectIO(int handle, bool is_read, const std::function &io_handler, const std::chrono::microseconds &timeout) -{ - Error error; - fd_set fds; - bool done = false; - - using namespace std::chrono; - - const auto finish_time = Now() + timeout; - - while (!done) - { - struct timeval tv = {0, 0}; - if (timeout != microseconds::zero()) - { - const auto remaining_dur = duration_cast(finish_time - Now()); - if (remaining_dur.count() <= 0) - { - error.SetErrorString("timeout exceeded"); - break; - } - const auto dur_secs = duration_cast(remaining_dur); - const auto dur_usecs = remaining_dur % seconds(1); - - tv.tv_sec = dur_secs.count(); - tv.tv_usec = dur_usecs.count(); - } - else - tv.tv_sec = 1; - - FD_ZERO(&fds); - FD_SET(handle, &fds); - - const auto retval = ::select(handle + 1, - (is_read) ? &fds : nullptr, - (is_read) ? nullptr : &fds, - nullptr, &tv); - if (retval == -1) - { - if (errno == EINTR) - continue; - error.SetErrorToErrno(); - break; - } - if (retval == 0) - { - error.SetErrorString("timeout exceeded"); - break; - } - if (!FD_ISSET(handle, &fds)) - { - error.SetErrorString("invalid state"); - break; - } - - error = io_handler(done); - if (error.Fail()) - { - if (error.GetError() == EINTR) - continue; - break; - } - } - return error; -} - } PipePosix::PipePosix() @@ -383,27 +317,33 @@ if (!CanRead()) return Error(EINVAL, eErrorTypePOSIX); - auto handle = GetReadFileDescriptor(); - return SelectIO(handle, - true, - [=, &bytes_read](bool &done) - { - Error error; - auto result = ::read(handle, - reinterpret_cast(buf) + bytes_read, - size - bytes_read); - if (result != -1) - { - bytes_read += result; - if (bytes_read == size || result == 0) - done = true; - } - else - error.SetErrorToErrno(); - - return error; - }, - timeout); + const int fd = GetReadFileDescriptor(); + + SelectHelper select_helper; + select_helper.SetTimeout(timeout); + select_helper.FDSetRead(fd); + + Error error; + while (error.Success()) + { + error = select_helper.Select(); + if (error.Success()) + { + auto result = ::read(fd, reinterpret_cast(buf) + bytes_read, size - bytes_read); + if (result != -1) + { + bytes_read += result; + if (bytes_read == size || result == 0) + break; + } + else + { + error.SetErrorToErrno(); + break; + } + } + } + return error; } Error @@ -413,25 +353,29 @@ if (!CanWrite()) return Error(EINVAL, eErrorTypePOSIX); - auto handle = GetWriteFileDescriptor(); - return SelectIO(handle, - false, - [=, &bytes_written](bool &done) - { - Error error; - auto result = ::write(handle, - reinterpret_cast(buf) + bytes_written, - size - bytes_written); - if (result != -1) - { - bytes_written += result; - if (bytes_written == size) - done = true; - } - else - error.SetErrorToErrno(); - - return error; - }, - std::chrono::microseconds::zero()); + const int fd = GetWriteFileDescriptor(); + SelectHelper select_helper; + select_helper.SetTimeout(std::chrono::seconds(0)); + select_helper.FDSetWrite(fd); + + Error error; + while (error.Success()) + { + error = select_helper.Select(); + if (error.Success()) + { + auto result = ::write(fd, reinterpret_cast(buf) + bytes_written, size - bytes_written); + if (result != -1) + { + bytes_written += result; + if (bytes_written == size) + break; + } + else + { + error.SetErrorToErrno(); + } + } + } + return error; } Index: source/Target/Process.cpp =================================================================== --- source/Target/Process.cpp +++ source/Target/Process.cpp @@ -62,6 +62,7 @@ #include "lldb/Target/ThreadPlanBase.h" #include "lldb/Target/UnixSignals.h" #include "lldb/Utility/NameMatches.h" +#include "lldb/Utility/SelectHelper.h" using namespace lldb; using namespace lldb_private; @@ -4925,25 +4926,20 @@ m_is_running = true; while (!GetIsDone()) { - fd_set read_fdset; - FD_ZERO (&read_fdset); - FD_SET (read_fd, &read_fdset); - FD_SET (pipe_read_fd, &read_fdset); - const int nfds = std::max(read_fd, pipe_read_fd) + 1; - int num_set_fds = select(nfds, &read_fdset, nullptr, nullptr, nullptr); + SelectHelper select_helper; + select_helper.FDSetRead(read_fd); + select_helper.FDSetRead(pipe_read_fd); + Error error = select_helper.Select(); - if (num_set_fds < 0) + if (error.Fail()) { - const int select_errno = errno; - - if (select_errno != EINTR) - SetIsDone(true); + SetIsDone(true); } - else if (num_set_fds > 0) + else { char ch = 0; size_t n; - if (FD_ISSET (read_fd, &read_fdset)) + if (select_helper.FDIsSetRead(read_fd)) { n = 1; if (m_read_file.Read(&ch, n).Success() && n == 1) @@ -4954,7 +4950,7 @@ else SetIsDone(true); } - if (FD_ISSET (pipe_read_fd, &read_fdset)) + if (select_helper.FDIsSetRead(pipe_read_fd)) { size_t bytes_read; // Consume the interrupt byte Index: source/Utility/SelectHelper.cpp =================================================================== --- source/Utility/SelectHelper.cpp +++ source/Utility/SelectHelper.cpp @@ -0,0 +1,291 @@ +//===-- SelectHelper.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if defined(__APPLE__) +// Enable this special support for Apple builds where we can have unlimited +// select bounds. We tried switching to poll() and kqueue and we were panicing +// the kernel, so we have to stick with select for now. +#define _DARWIN_UNLIMITED_SELECT +#endif + +// C Includes +#include +#include +#if defined(_WIN32) +#include +#endif + +// C++ Includes +#include + +// Other libraries and framework includes +#include "llvm/ADT/SmallVector.h" + +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Utility/SelectHelper.h" +#include "lldb/Utility/LLDBAssert.h" + +SelectHelper::SelectHelper() : + m_fd_map(), + m_end_time() // Infinite timeout unless SelectHelper::SetTimeout() gets called +{ +} + +void +SelectHelper::SetTimeout(const std::chrono::microseconds &timeout) +{ + using namespace std::chrono; + m_end_time = steady_clock::time_point(steady_clock::now() + timeout); +} + +void +SelectHelper::FDSetRead(int fd) +{ + m_fd_map[fd].read_set = true; +} + +void +SelectHelper::FDSetWrite(int fd) +{ + m_fd_map[fd].write_set = true; +} + +void +SelectHelper::FDSetError(int fd) +{ + m_fd_map[fd].error_set = true; +} + +bool +SelectHelper::FDIsSetRead(int fd) const +{ + auto pos = m_fd_map.find(fd); + if (pos != m_fd_map.end()) + return pos->second.read_is_set; + else + return false; +} + +bool +SelectHelper::FDIsSetWrite(int fd) const +{ + auto pos = m_fd_map.find(fd); + if (pos != m_fd_map.end()) + return pos->second.write_is_set; + else + return false; +} + +bool +SelectHelper::FDIsSetError(int fd) const +{ + auto pos = m_fd_map.find(fd); + if (pos != m_fd_map.end()) + return pos->second.error_is_set; + else + return false; +} + +lldb_private::Error +SelectHelper::Select() +{ + lldb_private::Error error; + + int max_read_fd = -1; + int max_write_fd = -1; + int max_error_fd = -1; + int max_fd = -1; + for (auto &pair : m_fd_map) + { + pair.second.PrepareForSelect(); + const int fd = pair.first; +#if !defined(__APPLE__) + lldbassert(fd < FD_SETSIZE); + if (fd >= FD_SETSIZE) + { + error.SetErrorStringWithFormat("%i is too large for select()", fd); + return error; + } +#endif + if (pair.second.read_set) + { + max_read_fd = std::max(fd, max_read_fd); + max_fd = std::max(fd, max_fd); + } + if (pair.second.write_set) + { + max_write_fd = std::max(fd, max_write_fd); + max_fd = std::max(fd, max_fd); + } + if (pair.second.error_set) + { + max_error_fd = std::max(fd, max_error_fd); + max_fd = std::max(fd, max_fd); + } + } + + if (max_fd == -1) + { + error.SetErrorString("no valid file descriptors"); + return error; + } + + const int nfds = max_fd + 1; + fd_set *read_fdset_ptr = nullptr; + fd_set *write_fdset_ptr = nullptr; + fd_set *error_fdset_ptr = nullptr; + //---------------------------------------------------------------------- + // Initialize and zero out the fdsets + //---------------------------------------------------------------------- +#if defined(__APPLE__) + llvm::SmallVector read_fdset; + llvm::SmallVector write_fdset; + llvm::SmallVector error_fdset; + + if (max_read_fd >= 0) + { + read_fdset.resize((nfds / FD_SETSIZE) + 1); + read_fdset_ptr = read_fdset.data(); + } + if (max_write_fd >= 0) + { + write_fdset.resize((nfds / FD_SETSIZE) + 1); + write_fdset_ptr = write_fdset.data(); + } + if (max_error_fd >= 0) + { + error_fdset.resize((nfds / FD_SETSIZE) + 1); + error_fdset_ptr = error_fdset.data(); + } + for (auto &fd_set : read_fdset) + FD_ZERO(&fd_set); + for (auto &fd_set : write_fdset) + FD_ZERO(&fd_set); + for (auto &fd_set : error_fdset) + FD_ZERO(&fd_set); +#else + fd_set read_fdset; + fd_set write_fdset; + fd_set error_fdset; + + if (max_read_fd >= 0) + { + FD_ZERO(&read_fdset); + read_fdset_ptr = &read_fdset; + } + if (max_write_fd >= 0) + { + FD_ZERO(&write_fdset); + write_fdset_ptr = &write_fdset; + } + if (max_error_fd >= 0) + { + FD_ZERO(&error_fdset); + error_fdset_ptr = &error_fdset; + } +#endif + //---------------------------------------------------------------------- + // Set the FD bits in the fdsets for read/write/error + //---------------------------------------------------------------------- + for (auto &pair : m_fd_map) + { + const int fd = pair.first; + + if (pair.second.read_set) + FD_SET(fd, read_fdset_ptr); + + if (pair.second.write_set) + FD_SET(fd, write_fdset_ptr); + + if (pair.second.error_set) + FD_SET(fd, error_fdset_ptr); + } + + //---------------------------------------------------------------------- + // Setup our timeout time value if needed + //---------------------------------------------------------------------- + struct timeval *tv_ptr = nullptr; + struct timeval tv = {0, 0}; + + while (1) + { + using namespace std::chrono; + //------------------------------------------------------------------ + // Setup out relative timeout based on the end time if we have one + //------------------------------------------------------------------ + if (m_end_time.hasValue()) + { + tv_ptr = &tv; + const auto remaining_dur = duration_cast(m_end_time.getValue() - steady_clock::now()); + if (remaining_dur.count() > 0) + { + // Wait for a specific amount of time + const auto dur_secs = duration_cast(remaining_dur); + const auto dur_usecs = remaining_dur % seconds(1); + tv.tv_sec = dur_secs.count(); + tv.tv_usec = dur_usecs.count(); + } + else + { + // Just poll once with no timeout + tv.tv_sec = 0; + tv.tv_usec = 0; + } + } + const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr, error_fdset_ptr, tv_ptr); + if (num_set_fds < 0) + { + // We got an error + error.SetErrorToErrno(); + if (error.GetError() == EINTR) + { + error.Clear(); + continue; // Keep calling select if we get EINTR + } + else + return error; + } + else if (num_set_fds == 0) + { + // Timeout + error.SetError(ETIMEDOUT, lldb::eErrorTypePOSIX); + error.SetErrorString("timed out"); + return error; + } + else + { + // One or more descriptors were set, update the FDInfo::select_is_set mask + // so users can ask the SelectHelper class so clients can call one of: + + for (auto &pair : m_fd_map) + { + const int fd = pair.first; + + if (pair.second.read_set) + { + if (FD_ISSET(fd, read_fdset_ptr)) + pair.second.read_is_set = true; + } + if (pair.second.write_set) + { + if (FD_ISSET(fd, write_fdset_ptr)) + pair.second.write_is_set = true; + } + if (pair.second.error_set) + { + if (FD_ISSET(fd, error_fdset_ptr)) + pair.second.error_is_set = true; + } + } + break; + } + } + return error; +}