Skip to content

Commit ddb9869

Browse files
authored
[lldb] Fixed PipeWindows bugs; added Pipe::WriteWithTimeout() (#101383)
Added also the test for async read/write.
1 parent badf34a commit ddb9869

File tree

7 files changed

+202
-51
lines changed

7 files changed

+202
-51
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: 82 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,18 @@ 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, /*nMaxInstances=*/1,
102+
/*nOutBufferSize=*/1024,
103+
/*nInBufferSize=*/1024,
104+
/*nDefaultTimeOut=*/0, &sa);
114105
if (INVALID_HANDLE_VALUE == m_read)
115106
return Status(::GetLastError(), eErrorTypeWin32);
116107
m_read_fd = _open_osfhandle((intptr_t)m_read, _O_RDONLY);
@@ -155,7 +146,7 @@ Status PipeWindows::CreateWithUniqueName(llvm::StringRef prefix,
155146
Status PipeWindows::OpenAsReader(llvm::StringRef name,
156147
bool child_process_inherit) {
157148
if (CanRead())
158-
return Status(ERROR_ALREADY_EXISTS, eErrorTypeWin32);
149+
return Status(); // Note the name is ignored.
159150

160151
return OpenNamedPipe(name, child_process_inherit, true);
161152
}
@@ -165,7 +156,7 @@ PipeWindows::OpenAsWriterWithTimeout(llvm::StringRef name,
165156
bool child_process_inherit,
166157
const std::chrono::microseconds &timeout) {
167158
if (CanWrite())
168-
return Status(ERROR_ALREADY_EXISTS, eErrorTypeWin32);
159+
return Status(); // Note the name is ignored.
169160

170161
return OpenNamedPipe(name, child_process_inherit, false);
171162
}
@@ -177,8 +168,8 @@ Status PipeWindows::OpenNamedPipe(llvm::StringRef name,
177168

178169
assert(is_read ? !CanRead() : !CanWrite());
179170

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

183174
std::string pipe_path = g_pipe_name_prefix.str();
184175
pipe_path.append(name.str());
@@ -202,6 +193,7 @@ Status PipeWindows::OpenNamedPipe(llvm::StringRef name,
202193
m_write_fd = _open_osfhandle((intptr_t)m_write, _O_WRONLY);
203194

204195
ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped));
196+
m_write_overlapped.hEvent = ::CreateEventA(nullptr, TRUE, FALSE, nullptr);
205197
}
206198

207199
return Status();
@@ -228,6 +220,8 @@ int PipeWindows::ReleaseWriteFileDescriptor() {
228220
return PipeWindows::kInvalidDescriptor;
229221
int result = m_write_fd;
230222
m_write_fd = PipeWindows::kInvalidDescriptor;
223+
if (m_write_overlapped.hEvent)
224+
::CloseHandle(m_write_overlapped.hEvent);
231225
m_write = INVALID_HANDLE_VALUE;
232226
ZeroMemory(&m_write_overlapped, sizeof(m_write_overlapped));
233227
return result;
@@ -250,6 +244,9 @@ void PipeWindows::CloseWriteFileDescriptor() {
250244
if (!CanWrite())
251245
return;
252246

247+
if (m_write_overlapped.hEvent)
248+
::CloseHandle(m_write_overlapped.hEvent);
249+
253250
_close(m_write_fd);
254251
m_write = INVALID_HANDLE_VALUE;
255252
m_write_fd = PipeWindows::kInvalidDescriptor;
@@ -280,15 +277,21 @@ Status PipeWindows::ReadWithTimeout(void *buf, size_t size,
280277
return Status(ERROR_INVALID_HANDLE, eErrorTypeWin32);
281278

282279
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);
280+
DWORD sys_bytes_read = 0;
281+
BOOL result =
282+
::ReadFile(m_read, buf, size, &sys_bytes_read, &m_read_overlapped);
283+
if (result) {
284+
bytes_read = sys_bytes_read;
285+
return Status();
286+
}
287+
288+
DWORD failure_error = ::GetLastError();
289+
if (failure_error != ERROR_IO_PENDING)
290+
return Status(failure_error, eErrorTypeWin32);
288291

289292
DWORD timeout = (duration == std::chrono::microseconds::zero())
290293
? INFINITE
291-
: duration.count() * 1000;
294+
: duration.count() / 1000;
292295
DWORD wait_result = ::WaitForSingleObject(m_read_overlapped.hEvent, timeout);
293296
if (wait_result != WAIT_OBJECT_0) {
294297
// The operation probably failed. However, if it timed out, we need to
@@ -298,10 +301,10 @@ Status PipeWindows::ReadWithTimeout(void *buf, size_t size,
298301
// happens, the original operation should be considered to have been
299302
// successful.
300303
bool failed = true;
301-
DWORD failure_error = ::GetLastError();
304+
failure_error = ::GetLastError();
302305
if (wait_result == WAIT_TIMEOUT) {
303-
BOOL cancel_result = CancelIoEx(m_read, &m_read_overlapped);
304-
if (!cancel_result && GetLastError() == ERROR_NOT_FOUND)
306+
BOOL cancel_result = ::CancelIoEx(m_read, &m_read_overlapped);
307+
if (!cancel_result && ::GetLastError() == ERROR_NOT_FOUND)
305308
failed = false;
306309
}
307310
if (failed)
@@ -310,27 +313,61 @@ Status PipeWindows::ReadWithTimeout(void *buf, size_t size,
310313

311314
// Now we call GetOverlappedResult setting bWait to false, since we've
312315
// already waited as long as we're willing to.
313-
if (!GetOverlappedResult(m_read, &m_read_overlapped, &sys_bytes_read, FALSE))
316+
if (!::GetOverlappedResult(m_read, &m_read_overlapped, &sys_bytes_read,
317+
FALSE))
314318
return Status(::GetLastError(), eErrorTypeWin32);
315319

316320
bytes_read = sys_bytes_read;
317321
return Status();
318322
}
319323

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

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);
330+
bytes_written = 0;
331+
DWORD sys_bytes_write = 0;
332+
BOOL result =
333+
::WriteFile(m_write, buf, size, &sys_bytes_write, &m_write_overlapped);
334+
if (result) {
335+
bytes_written = sys_bytes_write;
336+
return Status();
337+
}
338+
339+
DWORD failure_error = ::GetLastError();
340+
if (failure_error != ERROR_IO_PENDING)
341+
return Status(failure_error, eErrorTypeWin32);
330342

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

0 commit comments

Comments
 (0)