[Lldb-commits] [lldb] r278299 - Centralize all select() calls into one place so that we can take advantage of system specific optimizations to deal with more file descriptors than FD_SETSIZE on some systems.

Greg Clayton via lldb-commits lldb-commits at lists.llvm.org
Wed Aug 10 15:43:49 PDT 2016


Author: gclayton
Date: Wed Aug 10 17:43:48 2016
New Revision: 278299

URL: http://llvm.org/viewvc/llvm-project?rev=278299&view=rev
Log:
Centralize all select() calls into one place so that we can take advantage of system specific optimizations to deal with more file descriptors than FD_SETSIZE on some systems.

<rdar://problem/25325383>
https://reviews.llvm.org/D22950



Added:
    lldb/trunk/include/lldb/Utility/SelectHelper.h
    lldb/trunk/source/Utility/SelectHelper.cpp
Modified:
    lldb/trunk/lldb.xcodeproj/project.pbxproj
    lldb/trunk/source/Host/common/Editline.cpp
    lldb/trunk/source/Host/posix/ConnectionFileDescriptorPosix.cpp
    lldb/trunk/source/Host/posix/PipePosix.cpp
    lldb/trunk/source/Target/Process.cpp
    lldb/trunk/source/Utility/CMakeLists.txt

Added: lldb/trunk/include/lldb/Utility/SelectHelper.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Utility/SelectHelper.h?rev=278299&view=auto
==============================================================================
--- lldb/trunk/include/lldb/Utility/SelectHelper.h (added)
+++ lldb/trunk/include/lldb/Utility/SelectHelper.h Wed Aug 10 17:43:48 2016
@@ -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 <chrono>
+
+// 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<int, FDInfo> m_fd_map;
+    llvm::Optional<std::chrono::steady_clock::time_point> m_end_time;
+};
+
+#endif // liblldb_SelectHelper_h_

Modified: lldb/trunk/lldb.xcodeproj/project.pbxproj
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lldb.xcodeproj/project.pbxproj?rev=278299&r1=278298&r2=278299&view=diff
==============================================================================
--- lldb/trunk/lldb.xcodeproj/project.pbxproj (original)
+++ lldb/trunk/lldb.xcodeproj/project.pbxproj Wed Aug 10 17:43:48 2016
@@ -579,6 +579,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 */; };
@@ -1562,7 +1563,6 @@
 		265ABF6210F42EE900531910 /* DebugSymbols.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DebugSymbols.framework; path = /System/Library/PrivateFrameworks/DebugSymbols.framework; sourceTree = "<absolute>"; };
 		265E9BE1115C2BAA00D0DCCB /* debugserver.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = debugserver.xcodeproj; path = tools/debugserver/debugserver.xcodeproj; sourceTree = "<group>"; };
 		2660D9F611922A1300958FBD /* StringExtractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StringExtractor.cpp; path = source/Utility/StringExtractor.cpp; sourceTree = "<group>"; };
-		2660D9F711922A1300958FBD /* StringExtractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StringExtractor.h; path = source/Utility/StringExtractor.h; sourceTree = "<group>"; };
 		2660D9FE11922A7F00958FBD /* ThreadPlanStepUntil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanStepUntil.cpp; path = source/Target/ThreadPlanStepUntil.cpp; sourceTree = "<group>"; };
 		26651A14133BEC76005B64B7 /* lldb-public.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "lldb-public.h"; path = "include/lldb/lldb-public.h"; sourceTree = "<group>"; };
 		26651A15133BF9CC005B64B7 /* Opcode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Opcode.h; path = include/lldb/Core/Opcode.h; sourceTree = "<group>"; };
@@ -1838,6 +1838,9 @@
 		26A0604711A5BC7A00F75969 /* Baton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Baton.h; path = include/lldb/Core/Baton.h; sourceTree = "<group>"; };
 		26A0604811A5D03C00F75969 /* Baton.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Baton.cpp; path = source/Core/Baton.cpp; sourceTree = "<group>"; };
 		26A0DA4D140F721D006DA411 /* HashedNameToDIE.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HashedNameToDIE.h; sourceTree = "<group>"; };
+		26A3757F1D59462700D6CBDB /* SelectHelper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SelectHelper.cpp; path = source/Utility/SelectHelper.cpp; sourceTree = "<group>"; };
+		26A375831D59486000D6CBDB /* StringExtractor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = StringExtractor.h; path = include/lldb/Utility/StringExtractor.h; sourceTree = "<group>"; };
+		26A375841D59487700D6CBDB /* SelectHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SelectHelper.h; path = include/lldb/Utility/SelectHelper.h; sourceTree = "<group>"; };
 		26A3B4AC1181454800381BC2 /* ObjectContainerBSDArchive.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ObjectContainerBSDArchive.cpp; sourceTree = "<group>"; };
 		26A3B4AD1181454800381BC2 /* ObjectContainerBSDArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectContainerBSDArchive.h; sourceTree = "<group>"; };
 		26A4EEB511682AAC007A372A /* LLDBWrapPython.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = LLDBWrapPython.cpp; sourceTree = BUILT_PRODUCTS_DIR; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
@@ -3917,10 +3920,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 */,
@@ -7232,6 +7237,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 */,

Modified: lldb/trunk/source/Host/common/Editline.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Host/common/Editline.cpp?rev=278299&r1=278298&r2=278299&view=diff
==============================================================================
--- lldb/trunk/source/Host/common/Editline.cpp (original)
+++ lldb/trunk/source/Host/common/Editline.cpp Wed Aug 10 17:43:48 2016
@@ -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 @@ IsInputPending (FILE * file)
     // 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

Modified: lldb/trunk/source/Host/posix/ConnectionFileDescriptorPosix.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Host/posix/ConnectionFileDescriptorPosix.cpp?rev=278299&r1=278298&r2=278299&view=diff
==============================================================================
--- lldb/trunk/source/Host/posix/ConnectionFileDescriptorPosix.cpp (original)
+++ lldb/trunk/source/Host/posix/ConnectionFileDescriptorPosix.cpp Wed Aug 10 17:43:48 2016
@@ -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 <errno.h>
@@ -572,7 +573,7 @@ ConnectionFileDescriptor::GetURI()
     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 @@ ConnectionFileDescriptor::GetURI()
 //     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 @@ ConnectionFileDescriptor::BytesAvailable
     if (log)
         log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", static_cast<void *>(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 @@ ConnectionFileDescriptor::BytesAvailable
     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 @@ ConnectionFileDescriptor::BytesAvailable
         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<int>(handle, pipe_fd) + 1;
-#if defined(__APPLE__)
-            llvm::SmallVector<fd_set, 1> 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<void *>(this), nfds, handle, pipe_fd, static_cast<void *>(tv_ptr));
-                else
-                    log->Printf("%p ConnectionFileDescriptor::BytesAvailable()  ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p)...",
-                                static_cast<void *>(this), nfds, handle, static_cast<void *>(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<void *>(this), nfds, handle, pipe_fd, static_cast<void *>(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<void *>(this), nfds, handle, static_cast<void *>(tv_ptr), num_set_fds, error.AsCString());
-            }
+            Error error = select_helper.Select();
 
             if (error_ptr)
                 *error_ptr = error;
@@ -704,6 +643,9 @@ ConnectionFileDescriptor::BytesAvailable
                     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 @@ ConnectionFileDescriptor::BytesAvailable
                         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:

Modified: lldb/trunk/source/Host/posix/PipePosix.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Host/posix/PipePosix.cpp?rev=278299&r1=278298&r2=278299&view=diff
==============================================================================
--- lldb/trunk/source/Host/posix/PipePosix.cpp (original)
+++ lldb/trunk/source/Host/posix/PipePosix.cpp Wed Aug 10 17:43:48 2016
@@ -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 @@ Now()
     return std::chrono::steady_clock::now();
 }
 
-Error
-SelectIO(int handle, bool is_read, const std::function<Error(bool&)> &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<microseconds>(finish_time - Now());
-            if (remaining_dur.count() <= 0)
-            {
-                error.SetErrorString("timeout exceeded");
-                break;
-            }
-            const auto dur_secs = duration_cast<seconds>(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 @@ PipePosix::ReadWithTimeout(void *buf, si
     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<char*>(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<char*>(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 @@ PipePosix::Write(const void *buf, size_t
     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<const char*>(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<const char*>(buf) + bytes_written, size - bytes_written);
+            if (result != -1)
+            {
+                bytes_written += result;
+                if (bytes_written == size)
+                    break;
+            }
+            else
+            {
+                error.SetErrorToErrno();
+            }
+        }
+    }
+    return error;
 }

Modified: lldb/trunk/source/Target/Process.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Target/Process.cpp?rev=278299&r1=278298&r2=278299&view=diff
==============================================================================
--- lldb/trunk/source/Target/Process.cpp (original)
+++ lldb/trunk/source/Target/Process.cpp Wed Aug 10 17:43:48 2016
@@ -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 @@ public:
         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<int>(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 @@ public:
                     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

Modified: lldb/trunk/source/Utility/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Utility/CMakeLists.txt?rev=278299&r1=278298&r2=278299&view=diff
==============================================================================
--- lldb/trunk/source/Utility/CMakeLists.txt (original)
+++ lldb/trunk/source/Utility/CMakeLists.txt Wed Aug 10 17:43:48 2016
@@ -10,6 +10,7 @@ add_lldb_library(lldbUtility
   PseudoTerminal.cpp
   Range.cpp
   RegisterNumber.cpp
+  SelectHelper.cpp
   SharingPtr.cpp
   StringExtractor.cpp
   StringExtractorGDBRemote.cpp

Added: lldb/trunk/source/Utility/SelectHelper.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Utility/SelectHelper.cpp?rev=278299&view=auto
==============================================================================
--- lldb/trunk/source/Utility/SelectHelper.cpp (added)
+++ lldb/trunk/source/Utility/SelectHelper.cpp Wed Aug 10 17:43:48 2016
@@ -0,0 +1,294 @@
+//===-- 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 <errno.h>
+#if defined(_WIN32)
+// Define NOMINMAX to avoid macros that conflict with std::min and std::max
+#define NOMINMAX
+#include <winsock2.h>
+#else
+#include <sys/select.h>
+#endif
+
+// C++ Includes
+#include <algorithm>
+
+// 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<int>(fd, max_read_fd);
+            max_fd = std::max<int>(fd, max_fd);
+        }
+        if (pair.second.write_set)
+        {
+            max_write_fd = std::max<int>(fd, max_write_fd);
+            max_fd = std::max<int>(fd, max_fd);
+        }
+        if (pair.second.error_set)
+        {
+            max_error_fd = std::max<int>(fd, max_error_fd);
+            max_fd = std::max<int>(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<fd_set, 1> read_fdset;
+    llvm::SmallVector<fd_set, 1> write_fdset;
+    llvm::SmallVector<fd_set, 1> 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<microseconds>(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<seconds>(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;
+}




More information about the lldb-commits mailing list