Skip to content

Commit 9c5005d

Browse files
committed
[lldb] Added Pipe::WriteWithTimeout()
Fixed few bugs in PipeWindows. Added the test for async read/write.
1 parent c35c4c7 commit 9c5005d

File tree

7 files changed

+169
-54
lines changed

7 files changed

+169
-54
lines changed

lldb/include/lldb/Host/PipeBase.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,10 @@ class PipeBase {
5656
// Delete named pipe.
5757
virtual Status Delete(llvm::StringRef name) = 0;
5858

59-
virtual Status Write(const void *buf, size_t size, size_t &bytes_written) = 0;
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);
6063
virtual Status ReadWithTimeout(void *buf, size_t size,
6164
const std::chrono::microseconds &timeout,
6265
size_t &bytes_read) = 0;

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ class PipePosix : public PipeBase {
6464

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

67-
Status Write(const void *buf, size_t size, size_t &bytes_written) override;
67+
Status WriteWithTimeout(const void *buf, size_t size,
68+
const std::chrono::microseconds &timeout,
69+
size_t &bytes_written) override;
6870
Status ReadWithTimeout(void *buf, size_t size,
6971
const std::chrono::microseconds &timeout,
7072
size_t &bytes_read) override;

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ class PipeWindows : public PipeBase {
3232
Status CreateNew(bool child_process_inherit) override;
3333

3434
// Create a named pipe.
35-
Status CreateNewNamed(bool child_process_inherit);
3635
Status CreateNew(llvm::StringRef name, bool child_process_inherit) override;
3736
Status CreateWithUniqueName(llvm::StringRef prefix,
3837
bool child_process_inherit,
@@ -60,7 +59,9 @@ class PipeWindows : public PipeBase {
6059

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

63-
Status Write(const void *buf, size_t size, size_t &bytes_written) override;
62+
Status WriteWithTimeout(const void *buf, size_t size,
63+
const std::chrono::microseconds &timeout,
64+
size_t &bytes_written) override;
6465
Status ReadWithTimeout(void *buf, size_t size,
6566
const std::chrono::microseconds &timeout,
6667
size_t &bytes_read) override;

lldb/source/Host/common/PipeBase.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ Status PipeBase::OpenAsWriter(llvm::StringRef name,
1818
std::chrono::microseconds::zero());
1919
}
2020

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+
2126
Status PipeBase::Read(void *buf, size_t size, size_t &bytes_read) {
2227
return ReadWithTimeout(buf, size, std::chrono::microseconds::zero(),
2328
bytes_read);

lldb/source/Host/posix/PipePosix.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,15 +335,17 @@ Status PipePosix::ReadWithTimeout(void *buf, size_t size,
335335
return error;
336336
}
337337

338-
Status PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) {
338+
Status PipePosix::WriteWithTimeout(const void *buf, size_t size,
339+
const std::chrono::microseconds &timeout,
340+
size_t &bytes_written) {
339341
std::lock_guard<std::mutex> guard(m_write_mutex);
340342
bytes_written = 0;
341343
if (!CanWriteUnlocked())
342344
return Status(EINVAL, eErrorTypePOSIX);
343345

344346
const int fd = GetWriteFileDescriptorUnlocked();
345347
SelectHelper select_helper;
346-
select_helper.SetTimeout(std::chrono::seconds(0));
348+
select_helper.SetTimeout(timeout);
347349
select_helper.FDSetWrite(fd);
348350

349351
Status error;

lldb/source/Host/windows/PipeWindows.cpp

Lines changed: 83 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -58,30 +58,15 @@ PipeWindows::PipeWindows(pipe_t read, pipe_t write)
5858
}
5959

6060
ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped));
61+
m_read_overlapped.hEvent = ::CreateEventA(nullptr, TRUE, FALSE, nullptr);
62+
6163
ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped));
64+
m_write_overlapped.hEvent = ::CreateEventA(nullptr, TRUE, FALSE, nullptr);
6265
}
6366

6467
PipeWindows::~PipeWindows() { Close(); }
6568

6669
Status PipeWindows::CreateNew(bool child_process_inherit) {
67-
// Create an anonymous pipe with the specified inheritance.
68-
SECURITY_ATTRIBUTES sa{sizeof(SECURITY_ATTRIBUTES), 0,
69-
child_process_inherit ? TRUE : FALSE};
70-
BOOL result = ::CreatePipe(&m_read, &m_write, &sa, 1024);
71-
if (result == FALSE)
72-
return Status(::GetLastError(), eErrorTypeWin32);
73-
74-
m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY);
75-
ZeroMemory(&m_read_overlapped, sizeof(m_read_overlapped));
76-
m_read_overlapped.hEvent = ::CreateEventA(nullptr, TRUE, FALSE, nullptr);
77-
78-
m_write_fd = _open_osfhandle((intptr_t)m_write, _O_WRONLY);
79-
ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped));
80-
81-
return Status();
82-
}
83-
84-
Status PipeWindows::CreateNewNamed(bool child_process_inherit) {
8570
// Even for anonymous pipes, we open a named pipe. This is because you
8671
// cannot get overlapped i/o on Windows without using a named pipe. So we
8772
// synthesize a unique name.
@@ -105,12 +90,19 @@ Status PipeWindows::CreateNew(llvm::StringRef name,
10590
std::string pipe_path = g_pipe_name_prefix.str();
10691
pipe_path.append(name.str());
10792

93+
SECURITY_ATTRIBUTES sa{sizeof(SECURITY_ATTRIBUTES), 0,
94+
child_process_inherit ? TRUE : FALSE};
95+
10896
// Always open for overlapped i/o. We implement blocking manually in Read
10997
// and Write.
11098
DWORD read_mode = FILE_FLAG_OVERLAPPED;
111-
m_read = ::CreateNamedPipeA(
112-
pipe_path.c_str(), PIPE_ACCESS_INBOUND | read_mode,
113-
PIPE_TYPE_BYTE | PIPE_WAIT, 1, 1024, 1024, 120 * 1000, NULL);
99+
m_read =
100+
::CreateNamedPipeA(pipe_path.c_str(), PIPE_ACCESS_INBOUND | read_mode,
101+
PIPE_TYPE_BYTE | PIPE_WAIT, 1,
102+
1024, // Out buffer size
103+
1024, // In buffer size
104+
0, // Default timeout in ms, 0 means 50ms
105+
&sa);
114106
if (INVALID_HANDLE_VALUE == m_read)
115107
return Status(::GetLastError(), eErrorTypeWin32);
116108
m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY);
@@ -155,7 +147,7 @@ Status PipeWindows::CreateWithUniqueName(llvm::StringRef prefix,
155147
Status PipeWindows::OpenAsReader(llvm::StringRef name,
156148
bool child_process_inherit) {
157149
if (CanRead())
158-
return Status(ERROR_ALREADY_EXISTS, eErrorTypeWin32);
150+
return Status(); // Note the name is ignored.
159151

160152
return OpenNamedPipe(name, child_process_inherit, true);
161153
}
@@ -165,7 +157,7 @@ PipeWindows::OpenAsWriterWithTimeout(llvm::StringRef name,
165157
bool child_process_inherit,
166158
const std::chrono::microseconds &timeout) {
167159
if (CanWrite())
168-
return Status(ERROR_ALREADY_EXISTS, eErrorTypeWin32);
160+
return Status(); // Note the name is ignored.
169161

170162
return OpenNamedPipe(name, child_process_inherit, false);
171163
}
@@ -177,8 +169,8 @@ Status PipeWindows::OpenNamedPipe(llvm::StringRef name,
177169

178170
assert(is_read ? !CanRead() : !CanWrite());
179171

180-
SECURITY_ATTRIBUTES attributes = {};
181-
attributes.bInheritHandle = child_process_inherit;
172+
SECURITY_ATTRIBUTES attributes{sizeof(SECURITY_ATTRIBUTES), 0,
173+
child_process_inherit ? TRUE : FALSE};
182174

183175
std::string pipe_path = g_pipe_name_prefix.str();
184176
pipe_path.append(name.str());
@@ -202,6 +194,7 @@ Status PipeWindows::OpenNamedPipe(llvm::StringRef name,
202194
m_write_fd = _open_osfhandle((intptr_t)m_write, _O_WRONLY);
203195

204196
ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped));
197+
m_write_overlapped.hEvent = ::CreateEventA(nullptr, TRUE, FALSE, nullptr);
205198
}
206199

207200
return Status();
@@ -228,6 +221,8 @@ int PipeWindows::ReleaseWriteFileDescriptor() {
228221
return PipeWindows::kInvalidDescriptor;
229222
int result = m_write_fd;
230223
m_write_fd = PipeWindows::kInvalidDescriptor;
224+
if (m_write_overlapped.hEvent)
225+
::CloseHandle(m_write_overlapped.hEvent);
231226
m_write = INVALID_HANDLE_VALUE;
232227
ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped));
233228
return result;
@@ -250,6 +245,9 @@ void PipeWindows::CloseWriteFileDescriptor() {
250245
if (!CanWrite())
251246
return;
252247

248+
if (m_write_overlapped.hEvent)
249+
::CloseHandle(m_write_overlapped.hEvent);
250+
253251
_close(m_write_fd);
254252
m_write = INVALID_HANDLE_VALUE;
255253
m_write_fd = PipeWindows::kInvalidDescriptor;
@@ -280,15 +278,21 @@ Status PipeWindows::ReadWithTimeout(void *buf, size_t size,
280278
return Status(ERROR_INVALID_HANDLE, eErrorTypeWin32);
281279

282280
bytes_read = 0;
283-
DWORD sys_bytes_read = size;
284-
BOOL result = ::ReadFile(m_read, buf, sys_bytes_read, &sys_bytes_read,
285-
&m_read_overlapped);
286-
if (!result && GetLastError() != ERROR_IO_PENDING)
287-
return Status(::GetLastError(), eErrorTypeWin32);
281+
DWORD sys_bytes_read = 0;
282+
BOOL result =
283+
::ReadFile(m_read, buf, size, &sys_bytes_read, &m_read_overlapped);
284+
if (result) {
285+
bytes_read = sys_bytes_read;
286+
return Status();
287+
}
288+
289+
DWORD failure_error = ::GetLastError();
290+
if (failure_error != ERROR_IO_PENDING)
291+
return Status(failure_error, eErrorTypeWin32);
288292

289293
DWORD timeout = (duration == std::chrono::microseconds::zero())
290294
? INFINITE
291-
: duration.count() * 1000;
295+
: duration.count() / 1000;
292296
DWORD wait_result = ::WaitForSingleObject(m_read_overlapped.hEvent, timeout);
293297
if (wait_result != WAIT_OBJECT_0) {
294298
// The operation probably failed. However, if it timed out, we need to
@@ -298,10 +302,10 @@ Status PipeWindows::ReadWithTimeout(void *buf, size_t size,
298302
// happens, the original operation should be considered to have been
299303
// successful.
300304
bool failed = true;
301-
DWORD failure_error = ::GetLastError();
305+
failure_error = ::GetLastError();
302306
if (wait_result == WAIT_TIMEOUT) {
303-
BOOL cancel_result = CancelIoEx(m_read, &m_read_overlapped);
304-
if (!cancel_result && GetLastError() == ERROR_NOT_FOUND)
307+
BOOL cancel_result = ::CancelIoEx(m_read, &m_read_overlapped);
308+
if (!cancel_result && ::GetLastError() == ERROR_NOT_FOUND)
305309
failed = false;
306310
}
307311
if (failed)
@@ -310,27 +314,61 @@ Status PipeWindows::ReadWithTimeout(void *buf, size_t size,
310314

311315
// Now we call GetOverlappedResult setting bWait to false, since we've
312316
// already waited as long as we're willing to.
313-
if (!GetOverlappedResult(m_read, &m_read_overlapped, &sys_bytes_read, FALSE))
317+
if (!::GetOverlappedResult(m_read, &m_read_overlapped, &sys_bytes_read,
318+
FALSE))
314319
return Status(::GetLastError(), eErrorTypeWin32);
315320

316321
bytes_read = sys_bytes_read;
317322
return Status();
318323
}
319324

320-
Status PipeWindows::Write(const void *buf, size_t num_bytes,
321-
size_t &bytes_written) {
325+
Status PipeWindows::WriteWithTimeout(const void *buf, size_t size,
326+
const std::chrono::microseconds &duration,
327+
size_t &bytes_written) {
322328
if (!CanWrite())
323329
return Status(ERROR_INVALID_HANDLE, eErrorTypeWin32);
324330

325-
DWORD sys_bytes_written = 0;
326-
BOOL write_result = ::WriteFile(m_write, buf, num_bytes, &sys_bytes_written,
327-
&m_write_overlapped);
328-
if (!write_result && GetLastError() != ERROR_IO_PENDING)
329-
return Status(::GetLastError(), eErrorTypeWin32);
331+
bytes_written = 0;
332+
DWORD sys_bytes_write = 0;
333+
BOOL result =
334+
::WriteFile(m_write, buf, size, &sys_bytes_write, &m_write_overlapped);
335+
if (result) {
336+
bytes_written = sys_bytes_write;
337+
return Status();
338+
}
339+
340+
DWORD failure_error = ::GetLastError();
341+
if (failure_error != ERROR_IO_PENDING)
342+
return Status(failure_error, eErrorTypeWin32);
330343

331-
BOOL result = GetOverlappedResult(m_write, &m_write_overlapped,
332-
&sys_bytes_written, TRUE);
333-
if (!result)
344+
DWORD timeout = (duration == std::chrono::microseconds::zero())
345+
? INFINITE
346+
: duration.count() / 1000;
347+
DWORD wait_result = ::WaitForSingleObject(m_write_overlapped.hEvent, timeout);
348+
if (wait_result != WAIT_OBJECT_0) {
349+
// The operation probably failed. However, if it timed out, we need to
350+
// cancel the I/O. Between the time we returned from WaitForSingleObject
351+
// and the time we call CancelIoEx, the operation may complete. If that
352+
// hapens, CancelIoEx will fail and return ERROR_NOT_FOUND. If that
353+
// happens, the original operation should be considered to have been
354+
// successful.
355+
bool failed = true;
356+
failure_error = ::GetLastError();
357+
if (wait_result == WAIT_TIMEOUT) {
358+
BOOL cancel_result = ::CancelIoEx(m_write, &m_write_overlapped);
359+
if (!cancel_result && ::GetLastError() == ERROR_NOT_FOUND)
360+
failed = false;
361+
}
362+
if (failed)
363+
return Status(failure_error, eErrorTypeWin32);
364+
}
365+
366+
// Now we call GetOverlappedResult setting bWait to false, since we've
367+
// already waited as long as we're willing to.
368+
if (!::GetOverlappedResult(m_write, &m_write_overlapped, &sys_bytes_write,
369+
FALSE))
334370
return Status(::GetLastError(), eErrorTypeWin32);
371+
372+
bytes_written = sys_bytes_write;
335373
return Status();
336374
}

lldb/unittests/Host/PipeTest.cpp

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ TEST_F(PipeTest, CreateWithUniqueName) {
2727
name)
2828
.ToError(),
2929
llvm::Succeeded());
30+
pipe.Close();
31+
ASSERT_THAT_ERROR(pipe.Delete(name).ToError(), llvm::Succeeded());
3032
}
3133

32-
// Test broken
33-
#ifndef _WIN32
3434
TEST_F(PipeTest, OpenAsReader) {
3535
Pipe pipe;
3636
llvm::SmallString<0> name;
@@ -44,8 +44,72 @@ TEST_F(PipeTest, OpenAsReader) {
4444
size_t name_len = name.size();
4545
name += "foobar";
4646
llvm::StringRef name_ref(name.data(), name_len);
47+
// Note OpenAsReader() do nothing on Windows, the pipe is already opened for
48+
// read and write.
4749
ASSERT_THAT_ERROR(
4850
pipe.OpenAsReader(name_ref, /*child_process_inherit=*/false).ToError(),
4951
llvm::Succeeded());
52+
53+
ASSERT_TRUE(pipe.CanRead());
54+
pipe.Close();
55+
ASSERT_THAT_ERROR(pipe.Delete(name_ref).ToError(), llvm::Succeeded());
56+
}
57+
58+
TEST_F(PipeTest, WriteWithTimeout) {
59+
Pipe pipe;
60+
ASSERT_THAT_ERROR(pipe.CreateNew(false).ToError(), llvm::Succeeded());
61+
// Note write_chunk_size must be less than the pipe buffer.
62+
// The pipe buffer is 1024 for PipeWindows and 4096 for PipePosix.
63+
const size_t buf_size = 8192;
64+
const size_t write_chunk_size = 256;
65+
const size_t read_chunk_size = 300;
66+
std::unique_ptr<int32_t[]> write_buf_ptr(
67+
new int32_t[buf_size / sizeof(int32_t)]);
68+
int32_t *write_buf = write_buf_ptr.get();
69+
std::unique_ptr<int32_t[]> read_buf_ptr(
70+
new int32_t[(buf_size + 100) / sizeof(int32_t)]);
71+
int32_t *read_buf = read_buf_ptr.get();
72+
for (int i = 0; i < buf_size / sizeof(int32_t); ++i) {
73+
write_buf[i] = i;
74+
read_buf[i] = -i;
75+
}
76+
77+
char *write_ptr = (char *)write_buf;
78+
size_t write_bytes = 0;
79+
char *read_ptr = (char *)read_buf;
80+
size_t read_bytes = 0;
81+
size_t num_bytes = 0;
82+
Status error;
83+
while (write_bytes < buf_size) {
84+
error = pipe.WriteWithTimeout(write_ptr + write_bytes, write_chunk_size,
85+
std::chrono::milliseconds(10), num_bytes);
86+
if (error.Fail()) {
87+
ASSERT_TRUE(read_bytes < buf_size);
88+
error = pipe.ReadWithTimeout(read_ptr + read_bytes, read_chunk_size,
89+
std::chrono::milliseconds(10), num_bytes);
90+
if (error.Fail())
91+
FAIL();
92+
else
93+
read_bytes += num_bytes;
94+
} else
95+
write_bytes += num_bytes;
96+
}
97+
// Read the rest data.
98+
while (read_bytes < buf_size) {
99+
error = pipe.ReadWithTimeout(read_ptr + read_bytes, buf_size - read_bytes,
100+
std::chrono::milliseconds(10), num_bytes);
101+
if (error.Fail())
102+
FAIL();
103+
else
104+
read_bytes += num_bytes;
105+
}
106+
107+
// Be sure the pipe is empty.
108+
error = pipe.ReadWithTimeout(read_ptr + read_bytes, 100,
109+
std::chrono::milliseconds(10), num_bytes);
110+
ASSERT_TRUE(error.Fail());
111+
112+
// Compare the data.
113+
ASSERT_EQ(write_bytes, read_bytes);
114+
ASSERT_EQ(memcmp(write_buf, read_buf, buf_size), 0);
50115
}
51-
#endif

0 commit comments

Comments
 (0)