Skip to content

Commit c0b5451

Browse files
authored
[lldb] Assorted improvements to the Pipe class (#128719)
The main motivation for this was the inconsistency in handling of partial reads/writes between the windows and posix implementations (windows was returning partial reads, posix was trying to fill the buffer completely). I settle on the windows implementation, as that's the more common behavior, and the "eager" version can be implemented on top of that (in most cases, it isn't necessary, since we're writing just a single byte). Since this also required auditing the callers to make sure they're handling partial reads/writes correctly, I used the opportunity to modernize the function signatures as a forcing function. They now use the `Timeout` class (basically an `optional<duration>`) to support both polls (timeout=0) and blocking (timeout=nullopt) operations in a single function, and use an `Expected` instead of a by-ref result to return the number of bytes read/written. As a drive-by, I also fix a problem with the windows implementation where we were rounding the timeout value down, which meant that calls could time out slightly sooner than expected.
1 parent 0e3ba99 commit c0b5451

File tree

13 files changed

+258
-252
lines changed

13 files changed

+258
-252
lines changed

lldb/include/lldb/Host/PipeBase.h

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@
1010
#ifndef LLDB_HOST_PIPEBASE_H
1111
#define LLDB_HOST_PIPEBASE_H
1212

13-
#include <chrono>
14-
#include <string>
15-
1613
#include "lldb/Utility/Status.h"
14+
#include "lldb/Utility/Timeout.h"
1715
#include "llvm/ADT/SmallVector.h"
1816
#include "llvm/ADT/StringRef.h"
17+
#include "llvm/Support/Error.h"
1918

2019
namespace lldb_private {
2120
class PipeBase {
@@ -32,10 +31,9 @@ class PipeBase {
3231
virtual Status OpenAsReader(llvm::StringRef name,
3332
bool child_process_inherit) = 0;
3433

35-
Status OpenAsWriter(llvm::StringRef name, bool child_process_inherit);
36-
virtual Status
37-
OpenAsWriterWithTimeout(llvm::StringRef name, bool child_process_inherit,
38-
const std::chrono::microseconds &timeout) = 0;
34+
virtual llvm::Error OpenAsWriter(llvm::StringRef name,
35+
bool child_process_inherit,
36+
const Timeout<std::micro> &timeout) = 0;
3937

4038
virtual bool CanRead() const = 0;
4139
virtual bool CanWrite() const = 0;
@@ -56,14 +54,13 @@ class PipeBase {
5654
// Delete named pipe.
5755
virtual Status Delete(llvm::StringRef name) = 0;
5856

59-
virtual Status WriteWithTimeout(const void *buf, size_t size,
60-
const std::chrono::microseconds &timeout,
61-
size_t &bytes_written) = 0;
62-
Status Write(const void *buf, size_t size, size_t &bytes_written);
63-
virtual Status ReadWithTimeout(void *buf, size_t size,
64-
const std::chrono::microseconds &timeout,
65-
size_t &bytes_read) = 0;
66-
Status Read(void *buf, size_t size, size_t &bytes_read);
57+
virtual llvm::Expected<size_t>
58+
Write(const void *buf, size_t size,
59+
const Timeout<std::micro> &timeout = std::nullopt) = 0;
60+
61+
virtual llvm::Expected<size_t>
62+
Read(void *buf, size_t size,
63+
const Timeout<std::micro> &timeout = std::nullopt) = 0;
6764
};
6865
}
6966

lldb/include/lldb/Host/posix/PipePosix.h

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#ifndef LLDB_HOST_POSIX_PIPEPOSIX_H
1010
#define LLDB_HOST_POSIX_PIPEPOSIX_H
11+
1112
#include "lldb/Host/PipeBase.h"
1213
#include <mutex>
1314

@@ -38,9 +39,8 @@ class PipePosix : public PipeBase {
3839
llvm::SmallVectorImpl<char> &name) override;
3940
Status OpenAsReader(llvm::StringRef name,
4041
bool child_process_inherit) override;
41-
Status
42-
OpenAsWriterWithTimeout(llvm::StringRef name, bool child_process_inherit,
43-
const std::chrono::microseconds &timeout) override;
42+
llvm::Error OpenAsWriter(llvm::StringRef name, bool child_process_inherit,
43+
const Timeout<std::micro> &timeout) override;
4444

4545
bool CanRead() const override;
4646
bool CanWrite() const override;
@@ -64,12 +64,13 @@ class PipePosix : public PipeBase {
6464

6565
Status Delete(llvm::StringRef name) override;
6666

67-
Status WriteWithTimeout(const void *buf, size_t size,
68-
const std::chrono::microseconds &timeout,
69-
size_t &bytes_written) override;
70-
Status ReadWithTimeout(void *buf, size_t size,
71-
const std::chrono::microseconds &timeout,
72-
size_t &bytes_read) override;
67+
llvm::Expected<size_t>
68+
Write(const void *buf, size_t size,
69+
const Timeout<std::micro> &timeout = std::nullopt) override;
70+
71+
llvm::Expected<size_t>
72+
Read(void *buf, size_t size,
73+
const Timeout<std::micro> &timeout = std::nullopt) override;
7374

7475
private:
7576
bool CanReadUnlocked() const;

lldb/include/lldb/Host/windows/PipeWindows.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,8 @@ class PipeWindows : public PipeBase {
3838
llvm::SmallVectorImpl<char> &name) override;
3939
Status OpenAsReader(llvm::StringRef name,
4040
bool child_process_inherit) override;
41-
Status
42-
OpenAsWriterWithTimeout(llvm::StringRef name, bool child_process_inherit,
43-
const std::chrono::microseconds &timeout) override;
41+
llvm::Error OpenAsWriter(llvm::StringRef name, bool child_process_inherit,
42+
const Timeout<std::micro> &timeout) override;
4443

4544
bool CanRead() const override;
4645
bool CanWrite() const override;
@@ -59,12 +58,13 @@ class PipeWindows : public PipeBase {
5958

6059
Status Delete(llvm::StringRef name) override;
6160

62-
Status WriteWithTimeout(const void *buf, size_t size,
63-
const std::chrono::microseconds &timeout,
64-
size_t &bytes_written) override;
65-
Status ReadWithTimeout(void *buf, size_t size,
66-
const std::chrono::microseconds &timeout,
67-
size_t &bytes_read) override;
61+
llvm::Expected<size_t>
62+
Write(const void *buf, size_t size,
63+
const Timeout<std::micro> &timeout = std::nullopt) override;
64+
65+
llvm::Expected<size_t>
66+
Read(void *buf, size_t size,
67+
const Timeout<std::micro> &timeout = std::nullopt) override;
6868

6969
// PipeWindows specific methods. These allow access to the underlying OS
7070
// handle.

lldb/source/Host/common/PipeBase.cpp

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,3 @@
1111
using namespace lldb_private;
1212

1313
PipeBase::~PipeBase() = default;
14-
15-
Status PipeBase::OpenAsWriter(llvm::StringRef name,
16-
bool child_process_inherit) {
17-
return OpenAsWriterWithTimeout(name, child_process_inherit,
18-
std::chrono::microseconds::zero());
19-
}
20-
21-
Status PipeBase::Write(const void *buf, size_t size, size_t &bytes_written) {
22-
return WriteWithTimeout(buf, size, std::chrono::microseconds::zero(),
23-
bytes_written);
24-
}
25-
26-
Status PipeBase::Read(void *buf, size_t size, size_t &bytes_read) {
27-
return ReadWithTimeout(buf, size, std::chrono::microseconds::zero(),
28-
bytes_read);
29-
}

lldb/source/Host/common/Socket.cpp

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,14 @@ Status SharedSocket::CompleteSending(lldb::pid_t child_pid) {
9393
"WSADuplicateSocket() failed, error: %d", last_error);
9494
}
9595

96-
size_t num_bytes;
97-
Status error =
98-
m_socket_pipe.WriteWithTimeout(&protocol_info, sizeof(protocol_info),
99-
std::chrono::seconds(10), num_bytes);
100-
if (error.Fail())
101-
return error;
102-
if (num_bytes != sizeof(protocol_info))
96+
llvm::Expected<size_t> num_bytes = m_socket_pipe.Write(
97+
&protocol_info, sizeof(protocol_info), std::chrono::seconds(10));
98+
if (!num_bytes)
99+
return Status::FromError(num_bytes.takeError());
100+
if (*num_bytes != sizeof(protocol_info))
103101
return Status::FromErrorStringWithFormatv(
104-
"WriteWithTimeout(WSAPROTOCOL_INFO) failed: {0} bytes", num_bytes);
102+
"Write(WSAPROTOCOL_INFO) failed: wrote {0}/{1} bytes", *num_bytes,
103+
sizeof(protocol_info));
105104
#endif
106105
return Status();
107106
}
@@ -113,16 +112,14 @@ Status SharedSocket::GetNativeSocket(shared_fd_t fd, NativeSocket &socket) {
113112
WSAPROTOCOL_INFO protocol_info;
114113
{
115114
Pipe socket_pipe(fd, LLDB_INVALID_PIPE);
116-
size_t num_bytes;
117-
Status error =
118-
socket_pipe.ReadWithTimeout(&protocol_info, sizeof(protocol_info),
119-
std::chrono::seconds(10), num_bytes);
120-
if (error.Fail())
121-
return error;
122-
if (num_bytes != sizeof(protocol_info)) {
115+
llvm::Expected<size_t> num_bytes = socket_pipe.Read(
116+
&protocol_info, sizeof(protocol_info), std::chrono::seconds(10));
117+
if (!num_bytes)
118+
return Status::FromError(num_bytes.takeError());
119+
if (*num_bytes != sizeof(protocol_info)) {
123120
return Status::FromErrorStringWithFormatv(
124-
"socket_pipe.ReadWithTimeout(WSAPROTOCOL_INFO) failed: {0} bytes",
125-
num_bytes);
121+
"Read(WSAPROTOCOL_INFO) failed: read {0}/{1} bytes", *num_bytes,
122+
sizeof(protocol_info));
126123
}
127124
}
128125
socket = ::WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,

lldb/source/Host/posix/ConnectionFileDescriptorPosix.cpp

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,7 @@ ConnectionFileDescriptor::Connect(llvm::StringRef path,
178178
}
179179

180180
bool ConnectionFileDescriptor::InterruptRead() {
181-
size_t bytes_written = 0;
182-
Status result = m_pipe.Write("i", 1, bytes_written);
183-
return result.Success();
181+
return !errorToBool(m_pipe.Write("i", 1).takeError());
184182
}
185183

186184
ConnectionStatus ConnectionFileDescriptor::Disconnect(Status *error_ptr) {
@@ -205,13 +203,11 @@ ConnectionStatus ConnectionFileDescriptor::Disconnect(Status *error_ptr) {
205203
std::unique_lock<std::recursive_mutex> locker(m_mutex, std::defer_lock);
206204
if (!locker.try_lock()) {
207205
if (m_pipe.CanWrite()) {
208-
size_t bytes_written = 0;
209-
Status result = m_pipe.Write("q", 1, bytes_written);
210-
LLDB_LOGF(log,
211-
"%p ConnectionFileDescriptor::Disconnect(): Couldn't get "
212-
"the lock, sent 'q' to %d, error = '%s'.",
213-
static_cast<void *>(this), m_pipe.GetWriteFileDescriptor(),
214-
result.AsCString());
206+
llvm::Error err = m_pipe.Write("q", 1).takeError();
207+
LLDB_LOG(log,
208+
"{0}: Couldn't get the lock, sent 'q' to {1}, error = '{2}'.",
209+
this, m_pipe.GetWriteFileDescriptor(), err);
210+
consumeError(std::move(err));
215211
} else if (log) {
216212
LLDB_LOGF(log,
217213
"%p ConnectionFileDescriptor::Disconnect(): Couldn't get the "

lldb/source/Host/posix/MainLoopPosix.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -392,9 +392,5 @@ void MainLoopPosix::Interrupt() {
392392
return;
393393

394394
char c = '.';
395-
size_t bytes_written;
396-
Status error = m_interrupt_pipe.Write(&c, 1, bytes_written);
397-
assert(error.Success());
398-
UNUSED_IF_ASSERT_DISABLED(error);
399-
assert(bytes_written == 1);
395+
cantFail(m_interrupt_pipe.Write(&c, 1));
400396
}

lldb/source/Host/posix/PipePosix.cpp

Lines changed: 46 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
#include "lldb/Utility/SelectHelper.h"
1313
#include "llvm/ADT/SmallString.h"
1414
#include "llvm/Support/Errno.h"
15+
#include "llvm/Support/Error.h"
1516
#include <functional>
17+
#include <system_error>
1618
#include <thread>
1719

1820
#include <cerrno>
@@ -164,26 +166,27 @@ Status PipePosix::OpenAsReader(llvm::StringRef name,
164166
return error;
165167
}
166168

167-
Status
168-
PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name,
169-
bool child_process_inherit,
170-
const std::chrono::microseconds &timeout) {
169+
llvm::Error PipePosix::OpenAsWriter(llvm::StringRef name,
170+
bool child_process_inherit,
171+
const Timeout<std::micro> &timeout) {
171172
std::lock_guard<std::mutex> guard(m_write_mutex);
172173
if (CanReadUnlocked() || CanWriteUnlocked())
173-
return Status::FromErrorString("Pipe is already opened");
174+
return llvm::createStringError("Pipe is already opened");
174175

175176
int flags = O_WRONLY | O_NONBLOCK;
176177
if (!child_process_inherit)
177178
flags |= O_CLOEXEC;
178179

179180
using namespace std::chrono;
180-
const auto finish_time = Now() + timeout;
181+
std::optional<time_point<steady_clock>> finish_time;
182+
if (timeout)
183+
finish_time = Now() + *timeout;
181184

182185
while (!CanWriteUnlocked()) {
183-
if (timeout != microseconds::zero()) {
184-
const auto dur = duration_cast<microseconds>(finish_time - Now()).count();
185-
if (dur <= 0)
186-
return Status::FromErrorString(
186+
if (timeout) {
187+
if (Now() > finish_time)
188+
return llvm::createStringError(
189+
std::make_error_code(std::errc::timed_out),
187190
"timeout exceeded - reader hasn't opened so far");
188191
}
189192

@@ -193,7 +196,8 @@ PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name,
193196
const auto errno_copy = errno;
194197
// We may get ENXIO if a reader side of the pipe hasn't opened yet.
195198
if (errno_copy != ENXIO && errno_copy != EINTR)
196-
return Status(errno_copy, eErrorTypePOSIX);
199+
return llvm::errorCodeToError(
200+
std::error_code(errno_copy, std::generic_category()));
197201

198202
std::this_thread::sleep_for(
199203
milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS));
@@ -202,7 +206,7 @@ PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name,
202206
}
203207
}
204208

205-
return Status();
209+
return llvm::Error::success();
206210
}
207211

208212
int PipePosix::GetReadFileDescriptor() const {
@@ -300,70 +304,51 @@ void PipePosix::CloseWriteFileDescriptorUnlocked() {
300304
}
301305
}
302306

303-
Status PipePosix::ReadWithTimeout(void *buf, size_t size,
304-
const std::chrono::microseconds &timeout,
305-
size_t &bytes_read) {
307+
llvm::Expected<size_t> PipePosix::Read(void *buf, size_t size,
308+
const Timeout<std::micro> &timeout) {
306309
std::lock_guard<std::mutex> guard(m_read_mutex);
307-
bytes_read = 0;
308310
if (!CanReadUnlocked())
309-
return Status(EINVAL, eErrorTypePOSIX);
311+
return llvm::errorCodeToError(
312+
std::make_error_code(std::errc::invalid_argument));
310313

311314
const int fd = GetReadFileDescriptorUnlocked();
312315

313316
SelectHelper select_helper;
314-
select_helper.SetTimeout(timeout);
317+
if (timeout)
318+
select_helper.SetTimeout(*timeout);
315319
select_helper.FDSetRead(fd);
316320

317-
Status error;
318-
while (error.Success()) {
319-
error = select_helper.Select();
320-
if (error.Success()) {
321-
auto result =
322-
::read(fd, static_cast<char *>(buf) + bytes_read, size - bytes_read);
323-
if (result != -1) {
324-
bytes_read += result;
325-
if (bytes_read == size || result == 0)
326-
break;
327-
} else if (errno == EINTR) {
328-
continue;
329-
} else {
330-
error = Status::FromErrno();
331-
break;
332-
}
333-
}
334-
}
335-
return error;
321+
if (llvm::Error error = select_helper.Select().takeError())
322+
return error;
323+
324+
ssize_t result = ::read(fd, buf, size);
325+
if (result == -1)
326+
return llvm::errorCodeToError(
327+
std::error_code(errno, std::generic_category()));
328+
329+
return result;
336330
}
337331

338-
Status PipePosix::WriteWithTimeout(const void *buf, size_t size,
339-
const std::chrono::microseconds &timeout,
340-
size_t &bytes_written) {
332+
llvm::Expected<size_t> PipePosix::Write(const void *buf, size_t size,
333+
const Timeout<std::micro> &timeout) {
341334
std::lock_guard<std::mutex> guard(m_write_mutex);
342-
bytes_written = 0;
343335
if (!CanWriteUnlocked())
344-
return Status(EINVAL, eErrorTypePOSIX);
336+
return llvm::errorCodeToError(
337+
std::make_error_code(std::errc::invalid_argument));
345338

346339
const int fd = GetWriteFileDescriptorUnlocked();
347340
SelectHelper select_helper;
348-
select_helper.SetTimeout(timeout);
341+
if (timeout)
342+
select_helper.SetTimeout(*timeout);
349343
select_helper.FDSetWrite(fd);
350344

351-
Status error;
352-
while (error.Success()) {
353-
error = select_helper.Select();
354-
if (error.Success()) {
355-
auto result = ::write(fd, static_cast<const char *>(buf) + bytes_written,
356-
size - bytes_written);
357-
if (result != -1) {
358-
bytes_written += result;
359-
if (bytes_written == size)
360-
break;
361-
} else if (errno == EINTR) {
362-
continue;
363-
} else {
364-
error = Status::FromErrno();
365-
}
366-
}
367-
}
368-
return error;
345+
if (llvm::Error error = select_helper.Select().takeError())
346+
return error;
347+
348+
ssize_t result = ::write(fd, buf, size);
349+
if (result == -1)
350+
return llvm::errorCodeToError(
351+
std::error_code(errno, std::generic_category()));
352+
353+
return result;
369354
}

0 commit comments

Comments
 (0)