-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[lldb] Assorted improvements to the Pipe class #128719
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,7 +12,9 @@ | |
| #include "lldb/Utility/SelectHelper.h" | ||
| #include "llvm/ADT/SmallString.h" | ||
| #include "llvm/Support/Errno.h" | ||
| #include "llvm/Support/Error.h" | ||
| #include <functional> | ||
| #include <system_error> | ||
| #include <thread> | ||
|
|
||
| #include <cerrno> | ||
|
|
@@ -164,26 +166,27 @@ Status PipePosix::OpenAsReader(llvm::StringRef name, | |
| return error; | ||
| } | ||
|
|
||
| Status | ||
| PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name, | ||
| bool child_process_inherit, | ||
| const std::chrono::microseconds &timeout) { | ||
| llvm::Error PipePosix::OpenAsWriter(llvm::StringRef name, | ||
| bool child_process_inherit, | ||
| const Timeout<std::micro> &timeout) { | ||
| std::lock_guard<std::mutex> guard(m_write_mutex); | ||
| if (CanReadUnlocked() || CanWriteUnlocked()) | ||
| return Status::FromErrorString("Pipe is already opened"); | ||
| return llvm::createStringError("Pipe is already opened"); | ||
|
|
||
| int flags = O_WRONLY | O_NONBLOCK; | ||
| if (!child_process_inherit) | ||
| flags |= O_CLOEXEC; | ||
|
|
||
| using namespace std::chrono; | ||
| const auto finish_time = Now() + timeout; | ||
| std::optional<time_point<steady_clock>> finish_time; | ||
| if (timeout) | ||
| finish_time = Now() + *timeout; | ||
|
|
||
| while (!CanWriteUnlocked()) { | ||
| if (timeout != microseconds::zero()) { | ||
| const auto dur = duration_cast<microseconds>(finish_time - Now()).count(); | ||
| if (dur <= 0) | ||
| return Status::FromErrorString( | ||
| if (timeout) { | ||
| if (Now() > finish_time) | ||
| return llvm::createStringError( | ||
| std::make_error_code(std::errc::timed_out), | ||
| "timeout exceeded - reader hasn't opened so far"); | ||
| } | ||
|
|
||
|
|
@@ -193,7 +196,8 @@ PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name, | |
| const auto errno_copy = errno; | ||
| // We may get ENXIO if a reader side of the pipe hasn't opened yet. | ||
| if (errno_copy != ENXIO && errno_copy != EINTR) | ||
| return Status(errno_copy, eErrorTypePOSIX); | ||
| return llvm::errorCodeToError( | ||
| std::error_code(errno_copy, std::generic_category())); | ||
|
|
||
| std::this_thread::sleep_for( | ||
| milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS)); | ||
|
|
@@ -202,7 +206,7 @@ PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name, | |
| } | ||
| } | ||
|
|
||
| return Status(); | ||
| return llvm::Error::success(); | ||
| } | ||
|
|
||
| int PipePosix::GetReadFileDescriptor() const { | ||
|
|
@@ -300,70 +304,51 @@ void PipePosix::CloseWriteFileDescriptorUnlocked() { | |
| } | ||
| } | ||
|
|
||
| Status PipePosix::ReadWithTimeout(void *buf, size_t size, | ||
| const std::chrono::microseconds &timeout, | ||
| size_t &bytes_read) { | ||
| llvm::Expected<size_t> PipePosix::Read(void *buf, size_t size, | ||
| const Timeout<std::micro> &timeout) { | ||
| std::lock_guard<std::mutex> guard(m_read_mutex); | ||
| bytes_read = 0; | ||
| if (!CanReadUnlocked()) | ||
| return Status(EINVAL, eErrorTypePOSIX); | ||
| return llvm::errorCodeToError( | ||
| std::make_error_code(std::errc::invalid_argument)); | ||
|
|
||
| const int fd = GetReadFileDescriptorUnlocked(); | ||
|
|
||
| SelectHelper select_helper; | ||
| select_helper.SetTimeout(timeout); | ||
| if (timeout) | ||
| select_helper.SetTimeout(*timeout); | ||
| select_helper.FDSetRead(fd); | ||
|
|
||
| Status error; | ||
| while (error.Success()) { | ||
| error = select_helper.Select(); | ||
| if (error.Success()) { | ||
| auto result = | ||
| ::read(fd, static_cast<char *>(buf) + bytes_read, size - bytes_read); | ||
| if (result != -1) { | ||
| bytes_read += result; | ||
| if (bytes_read == size || result == 0) | ||
| break; | ||
| } else if (errno == EINTR) { | ||
| continue; | ||
| } else { | ||
| error = Status::FromErrno(); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| return error; | ||
| if (llvm::Error error = select_helper.Select().takeError()) | ||
| return error; | ||
|
|
||
| ssize_t result = ::read(fd, buf, size); | ||
| if (result == -1) | ||
| return llvm::errorCodeToError( | ||
| std::error_code(errno, std::generic_category())); | ||
|
Comment on lines
+325
to
+327
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we check for EINTR? (or RetryAfterSignal)?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't believe that necessary because EINTR is returned when a blocking syscall is interrupted, and the select call above basically guarantees that this will not block: This is based on the linux documentation, but I'd expect other systems to behave the same way. That said, the check doesn't hurt much, so I'd fine with adding it if you think it helps. |
||
|
|
||
| return result; | ||
| } | ||
|
|
||
| Status PipePosix::WriteWithTimeout(const void *buf, size_t size, | ||
| const std::chrono::microseconds &timeout, | ||
| size_t &bytes_written) { | ||
| llvm::Expected<size_t> PipePosix::Write(const void *buf, size_t size, | ||
| const Timeout<std::micro> &timeout) { | ||
| std::lock_guard<std::mutex> guard(m_write_mutex); | ||
| bytes_written = 0; | ||
| if (!CanWriteUnlocked()) | ||
| return Status(EINVAL, eErrorTypePOSIX); | ||
| return llvm::errorCodeToError( | ||
| std::make_error_code(std::errc::invalid_argument)); | ||
|
|
||
| const int fd = GetWriteFileDescriptorUnlocked(); | ||
| SelectHelper select_helper; | ||
| select_helper.SetTimeout(timeout); | ||
| if (timeout) | ||
| select_helper.SetTimeout(*timeout); | ||
| select_helper.FDSetWrite(fd); | ||
|
|
||
| Status error; | ||
| while (error.Success()) { | ||
| error = select_helper.Select(); | ||
| if (error.Success()) { | ||
| auto result = ::write(fd, static_cast<const char *>(buf) + bytes_written, | ||
| size - bytes_written); | ||
| if (result != -1) { | ||
| bytes_written += result; | ||
| if (bytes_written == size) | ||
| break; | ||
| } else if (errno == EINTR) { | ||
| continue; | ||
| } else { | ||
| error = Status::FromErrno(); | ||
| } | ||
| } | ||
| } | ||
| return error; | ||
| if (llvm::Error error = select_helper.Select().takeError()) | ||
| return error; | ||
|
|
||
| ssize_t result = ::write(fd, buf, size); | ||
| if (result == -1) | ||
| return llvm::errorCodeToError( | ||
| std::error_code(errno, std::generic_category())); | ||
|
Comment on lines
+349
to
+351
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we check for |
||
|
|
||
| return result; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.