Signed-off-by: Brendan Shanks bshanks@codeweavers.com --- v2: Use overlapped I/O instead of threads, test nonblocking write and pending reads
dlls/kernel32/tests/pipe.c | 134 +++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+)
diff --git a/dlls/kernel32/tests/pipe.c b/dlls/kernel32/tests/pipe.c index f61d441303..95ad4e0e82 100644 --- a/dlls/kernel32/tests/pipe.c +++ b/dlls/kernel32/tests/pipe.c @@ -3887,6 +3887,139 @@ static void test_wait_pipe(void) CloseHandle(ov.hEvent); }
+static void test_nowait(void) +{ + HANDLE piperead, pipewrite, hFile; + OVERLAPPED ol, ol2; + DWORD read, write; + char readbuf[32768]; + static const char teststring[] = "bits"; + + /* CreateNamedPipe with PIPE_NOWAIT, and read from empty pipe */ + piperead = CreateNamedPipeA(PIPENAME, FILE_FLAG_OVERLAPPED | PIPE_ACCESS_DUPLEX, + /* dwPipeMode */ PIPE_TYPE_BYTE | PIPE_NOWAIT, + /* nMaxInstances */ 1, + /* nOutBufSize */ 512, + /* nInBufSize */ 512, + /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT, + /* lpSecurityAttrib */ NULL); + ok(piperead != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n"); + pipewrite = CreateFileA(PIPENAME, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); + ok(pipewrite != INVALID_HANDLE_VALUE, "CreateFileA failed\n"); + memset(&ol, 0, sizeof(ol)); + ol.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + SetLastError(0xdeadbeef); + ok(ReadFile(piperead, readbuf, sizeof(readbuf), &read, &ol) == FALSE, "ReadFile should fail\n"); + todo_wine ok(GetLastError() == ERROR_NO_DATA, "got %d should be ERROR_NO_DATA\n", GetLastError()); + if (GetLastError() == ERROR_IO_PENDING) + CancelIo(piperead); + + /* test a small write/read */ + ok(WriteFile(pipewrite, teststring, sizeof(teststring), &write, NULL), "WriteFile should succeed\n"); + ok(ReadFile(piperead, readbuf, sizeof(readbuf), &read, &ol), "ReadFile should succeed\n"); + ok(read == write, "read/write bytes should match\n"); + ok(CloseHandle(ol.hEvent), "CloseHandle for the event failed\n"); + ok(CloseHandle(pipewrite), "CloseHandle for the write pipe failed\n"); + ok(CloseHandle(piperead), "CloseHandle for the read pipe failed\n"); + + + /* create write side with PIPE_NOWAIT, read side PIPE_WAIT, and test writes */ + pipewrite = CreateNamedPipeA(PIPENAME, FILE_FLAG_OVERLAPPED | PIPE_ACCESS_DUPLEX, + /* dwPipeMode */ PIPE_TYPE_BYTE | PIPE_NOWAIT, + /* nMaxInstances */ 1, + /* nOutBufSize */ 512, + /* nInBufSize */ 512, + /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT, + /* lpSecurityAttrib */ NULL); + ok(pipewrite != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n"); + piperead = CreateFileA(PIPENAME, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); + ok(piperead != INVALID_HANDLE_VALUE, "CreateFileA failed\n"); + memset(&ol, 0, sizeof(ol)); + ol.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + memset(&ol2, 0, sizeof(ol2)); + ol2.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + + /* write one byte larger than the buffer size, should fail */ + SetLastError(0xdeadbeef); + todo_wine ok(WriteFile(pipewrite, readbuf, 513, &write, &ol), "WriteFile should succeed\n"); + /* WriteFile only documents that 'write < sizeof(readbuf)' for this case, but Windows + * doesn't seem to do partial writes ('write == 0' always) + */ + ok(write < sizeof(readbuf), "WriteFile should fail to write the whole buffer\n"); + ok(write == 0, "WriteFile shouldn't do partial writes\n"); + if (GetLastError() == ERROR_IO_PENDING) + CancelIo(piperead); + + /* start overlapped read, then non-blocking write (smaller than buffer, then bigger than buffer) */ + SetLastError(0xdeadbeef); + todo_wine ok(ReadFile(piperead, readbuf, sizeof(readbuf), &read, &ol2) == FALSE, "ReadFile should fail\n"); + todo_wine ok(GetLastError() == ERROR_IO_PENDING, "got %d should be ERROR_IO_PENDING\n", GetLastError()); + ok(WriteFile(pipewrite, teststring, sizeof(teststring), &write, &ol), "WriteFile should succeed\n"); + ok(write == sizeof(teststring), "got %d, write should be %d\n", write, sizeof(teststring)); + ok(GetOverlappedResult(piperead, &ol2, &read, FALSE), "GetOverlappedResult should succeed\n"); + todo_wine ok(read == sizeof(teststring), "got %d, read should be %d\n", write, sizeof(teststring)); + + SetLastError(0xdeadbeef); + todo_wine ok(ReadFile(piperead, readbuf, sizeof(readbuf), &read, &ol2) == FALSE, "ReadFile should fail\n"); + todo_wine ok(GetLastError() == ERROR_IO_PENDING, "got %d should be ERROR_IO_PENDING\n", GetLastError()); + todo_wine ok(WriteFile(pipewrite, readbuf, 513, &write, &ol), "WriteFile should succeed\n"); + todo_wine ok(write == 513, "got %d, write should be %d\n", write, 513); + ok(GetOverlappedResult(piperead, &ol2, &read, FALSE), "GetOverlappedResult should succeed\n"); + todo_wine ok(read == 513, "got %d, read should be %d\n", write, 513); + if (GetOverlappedResult(piperead, &ol2, &read, FALSE) == FALSE) + CancelIo(piperead); + + /* write the exact buffer size, should succeed */ + SetLastError(0xdeadbeef); + todo_wine ok(WriteFile(pipewrite, readbuf, 512, &write, &ol), "WriteFile should succeed\n"); + todo_wine ok(write == 512, "WriteFile should write the whole buffer\n"); + if (GetLastError() == ERROR_IO_PENDING) + CancelIo(piperead); + + ok(CloseHandle(ol.hEvent), "CloseHandle for the event failed\n"); + ok(CloseHandle(ol2.hEvent), "CloseHandle for the event failed\n"); + ok(CloseHandle(pipewrite), "CloseHandle for the write pipe failed\n"); + ok(CloseHandle(piperead), "CloseHandle for the read pipe failed\n"); + + + /* CreateNamedPipe with PIPE_NOWAIT, test ConnectNamedPipe */ + pipewrite = CreateNamedPipeA(PIPENAME, FILE_FLAG_OVERLAPPED | PIPE_ACCESS_DUPLEX, + /* dwPipeMode */ PIPE_TYPE_BYTE | PIPE_NOWAIT, + /* nMaxInstances */ 1, + /* nOutBufSize */ 512, + /* nInBufSize */ 512, + /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT, + /* lpSecurityAttrib */ NULL); + ok(pipewrite != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n"); + memset(&ol, 0, sizeof(ol)); + ol.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + SetLastError(0xdeadbeef); + ok(ConnectNamedPipe(pipewrite, &ol) == FALSE, "ConnectNamedPipe should fail\n"); + todo_wine ok(GetLastError() == ERROR_PIPE_LISTENING, "got %d should be ERROR_PIPE_LISTENING\n", GetLastError()); + if (GetLastError() == ERROR_IO_PENDING) + CancelIo(pipewrite); + + /* connect and disconnect, then test ConnectNamedPipe again */ + hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); + ok(hFile != INVALID_HANDLE_VALUE, "CreateFileA failed\n"); + ok(CloseHandle(hFile), "CloseHandle failed\n"); + SetLastError(0xdeadbeef); + ok(ConnectNamedPipe(pipewrite,&ol) == FALSE, "ConnectNamedPipe should fail\n"); + ok(GetLastError() == ERROR_NO_DATA, "got %d should be ERROR_NO_DATA\n", GetLastError()); + if (GetLastError() == ERROR_IO_PENDING) + CancelIo(pipewrite); + + /* call DisconnectNamedPipe and test ConnectNamedPipe again */ + ok(DisconnectNamedPipe(pipewrite) == TRUE, "DisconnectNamedPipe should succeed\n"); + SetLastError(0xdeadbeef); + ok(ConnectNamedPipe(pipewrite,&ol) == FALSE, "ConnectNamedPipe should fail\n"); + todo_wine ok(GetLastError() == ERROR_PIPE_LISTENING, "got %d should be ERROR_PIPE_LISTENING\n", GetLastError()); + if (GetLastError() == ERROR_IO_PENDING) + CancelIo(pipewrite); + ok(CloseHandle(ol.hEvent), "CloseHandle for the event failed\n"); + ok(CloseHandle(pipewrite), "CloseHandle for the write pipe failed\n"); +} + START_TEST(pipe) { char **argv; @@ -3954,4 +4087,5 @@ START_TEST(pipe) test_namedpipe_session_id(); test_multiple_instances(); test_wait_pipe(); + test_nowait(); }
Fixes Rockstar Games Launcher hanging for some users.
Signed-off-by: Brendan Shanks bshanks@codeweavers.com --- dlls/kernel32/tests/pipe.c | 2 +- server/named_pipe.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/dlls/kernel32/tests/pipe.c b/dlls/kernel32/tests/pipe.c index 95ad4e0e82..9b544afca7 100644 --- a/dlls/kernel32/tests/pipe.c +++ b/dlls/kernel32/tests/pipe.c @@ -3910,7 +3910,7 @@ static void test_nowait(void) ol.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); SetLastError(0xdeadbeef); ok(ReadFile(piperead, readbuf, sizeof(readbuf), &read, &ol) == FALSE, "ReadFile should fail\n"); - todo_wine ok(GetLastError() == ERROR_NO_DATA, "got %d should be ERROR_NO_DATA\n", GetLastError()); + ok(GetLastError() == ERROR_NO_DATA, "got %d should be ERROR_NO_DATA\n", GetLastError()); if (GetLastError() == ERROR_IO_PENDING) CancelIo(piperead);
diff --git a/server/named_pipe.c b/server/named_pipe.c index 8e0380d060..6926712b8c 100644 --- a/server/named_pipe.c +++ b/server/named_pipe.c @@ -851,6 +851,11 @@ static int pipe_end_read( struct fd *fd, struct async *async, file_pos_t pos ) switch (pipe_end->state) { case FILE_PIPE_CONNECTED_STATE: + if ((pipe_end->flags & NAMED_PIPE_NONBLOCKING_MODE) && list_empty( &pipe_end->message_queue )) + { + set_error( STATUS_PIPE_EMPTY ); + return 0; + } break; case FILE_PIPE_DISCONNECTED_STATE: set_error( STATUS_PIPE_DISCONNECTED );
Signed-off-by: Brendan Shanks bshanks@codeweavers.com --- v2: Don't leak pipe_message, and correctly handle a nonblocking write larger than the buffer while a read is pending
dlls/kernel32/tests/pipe.c | 22 +++++++++++----------- server/named_pipe.c | 18 +++++++++++++----- 2 files changed, 24 insertions(+), 16 deletions(-)
diff --git a/dlls/kernel32/tests/pipe.c b/dlls/kernel32/tests/pipe.c index 9b544afca7..370073a8a7 100644 --- a/dlls/kernel32/tests/pipe.c +++ b/dlls/kernel32/tests/pipe.c @@ -3941,7 +3941,7 @@ static void test_nowait(void)
/* write one byte larger than the buffer size, should fail */ SetLastError(0xdeadbeef); - todo_wine ok(WriteFile(pipewrite, readbuf, 513, &write, &ol), "WriteFile should succeed\n"); + ok(WriteFile(pipewrite, readbuf, 513, &write, &ol), "WriteFile should succeed\n"); /* WriteFile only documents that 'write < sizeof(readbuf)' for this case, but Windows * doesn't seem to do partial writes ('write == 0' always) */ @@ -3952,27 +3952,27 @@ static void test_nowait(void)
/* start overlapped read, then non-blocking write (smaller than buffer, then bigger than buffer) */ SetLastError(0xdeadbeef); - todo_wine ok(ReadFile(piperead, readbuf, sizeof(readbuf), &read, &ol2) == FALSE, "ReadFile should fail\n"); - todo_wine ok(GetLastError() == ERROR_IO_PENDING, "got %d should be ERROR_IO_PENDING\n", GetLastError()); + ok(ReadFile(piperead, readbuf, sizeof(readbuf), &read, &ol2) == FALSE, "ReadFile should fail\n"); + ok(GetLastError() == ERROR_IO_PENDING, "got %d should be ERROR_IO_PENDING\n", GetLastError()); ok(WriteFile(pipewrite, teststring, sizeof(teststring), &write, &ol), "WriteFile should succeed\n"); ok(write == sizeof(teststring), "got %d, write should be %d\n", write, sizeof(teststring)); ok(GetOverlappedResult(piperead, &ol2, &read, FALSE), "GetOverlappedResult should succeed\n"); - todo_wine ok(read == sizeof(teststring), "got %d, read should be %d\n", write, sizeof(teststring)); + ok(read == sizeof(teststring), "got %d, read should be %d\n", write, sizeof(teststring));
SetLastError(0xdeadbeef); - todo_wine ok(ReadFile(piperead, readbuf, sizeof(readbuf), &read, &ol2) == FALSE, "ReadFile should fail\n"); - todo_wine ok(GetLastError() == ERROR_IO_PENDING, "got %d should be ERROR_IO_PENDING\n", GetLastError()); - todo_wine ok(WriteFile(pipewrite, readbuf, 513, &write, &ol), "WriteFile should succeed\n"); - todo_wine ok(write == 513, "got %d, write should be %d\n", write, 513); + ok(ReadFile(piperead, readbuf, sizeof(readbuf), &read, &ol2) == FALSE, "ReadFile should fail\n"); + ok(GetLastError() == ERROR_IO_PENDING, "got %d should be ERROR_IO_PENDING\n", GetLastError()); + ok(WriteFile(pipewrite, readbuf, 513, &write, &ol), "WriteFile should succeed\n"); + ok(write == 513, "got %d, write should be %d\n", write, 513); ok(GetOverlappedResult(piperead, &ol2, &read, FALSE), "GetOverlappedResult should succeed\n"); - todo_wine ok(read == 513, "got %d, read should be %d\n", write, 513); + ok(read == 513, "got %d, read should be %d\n", write, 513); if (GetOverlappedResult(piperead, &ol2, &read, FALSE) == FALSE) CancelIo(piperead);
/* write the exact buffer size, should succeed */ SetLastError(0xdeadbeef); - todo_wine ok(WriteFile(pipewrite, readbuf, 512, &write, &ol), "WriteFile should succeed\n"); - todo_wine ok(write == 512, "WriteFile should write the whole buffer\n"); + ok(WriteFile(pipewrite, readbuf, 512, &write, &ol), "WriteFile should succeed\n"); + ok(write == 512, "WriteFile should write the whole buffer\n"); if (GetLastError() == ERROR_IO_PENDING) CancelIo(piperead);
diff --git a/server/named_pipe.c b/server/named_pipe.c index 6926712b8c..f3251439b2 100644 --- a/server/named_pipe.c +++ b/server/named_pipe.c @@ -363,7 +363,7 @@ static struct pipe_message *queue_message( struct pipe_end *pipe_end, struct ios return message; }
-static void wake_message( struct pipe_message *message ) +static void wake_message( struct pipe_message *message, data_size_t result ) { struct async *async = message->async;
@@ -371,7 +371,7 @@ static void wake_message( struct pipe_message *message ) if (!async) return;
message->iosb->status = STATUS_SUCCESS; - message->iosb->result = message->iosb->in_size; + message->iosb->result = result; async_terminate( async, message->iosb->result ? STATUS_ALERTED : STATUS_SUCCESS ); release_object( async ); } @@ -749,7 +749,7 @@ static void message_queue_read( struct pipe_end *pipe_end, struct iosb *iosb ) { iosb->out_data = message->iosb->in_data; message->iosb->in_data = NULL; - wake_message( message ); + wake_message( message, message->iosb->in_size ); free_message( message ); } else @@ -773,7 +773,7 @@ static void message_queue_read( struct pipe_end *pipe_end, struct iosb *iosb ) message->read_pos += writing; if (message->read_pos == message->iosb->in_size) { - wake_message(message); + wake_message(message, message->iosb->in_size); free_message(message); } } while (write_pos < iosb->out_size); @@ -836,7 +836,15 @@ static void reselect_write_queue( struct pipe_end *pipe_end ) { avail += message->iosb->in_size - message->read_pos; if (message->async && (avail <= reader->buffer_size || !message->iosb->in_size)) - wake_message( message ); + { + wake_message( message, message->iosb->in_size ); + } + else if (message->async && (pipe_end->flags & NAMED_PIPE_NONBLOCKING_MODE) && + !async_waiting( &reader->read_q )) + { + wake_message( message, 0 ); + free_message( message ); + } } }
Signed-off-by: Brendan Shanks bshanks@codeweavers.com --- dlls/kernel32/tests/pipe.c | 4 ++-- server/named_pipe.c | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/dlls/kernel32/tests/pipe.c b/dlls/kernel32/tests/pipe.c index 370073a8a7..65888fdca4 100644 --- a/dlls/kernel32/tests/pipe.c +++ b/dlls/kernel32/tests/pipe.c @@ -3995,7 +3995,7 @@ static void test_nowait(void) ol.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); SetLastError(0xdeadbeef); ok(ConnectNamedPipe(pipewrite, &ol) == FALSE, "ConnectNamedPipe should fail\n"); - todo_wine ok(GetLastError() == ERROR_PIPE_LISTENING, "got %d should be ERROR_PIPE_LISTENING\n", GetLastError()); + ok(GetLastError() == ERROR_PIPE_LISTENING, "got %d should be ERROR_PIPE_LISTENING\n", GetLastError()); if (GetLastError() == ERROR_IO_PENDING) CancelIo(pipewrite);
@@ -4013,7 +4013,7 @@ static void test_nowait(void) ok(DisconnectNamedPipe(pipewrite) == TRUE, "DisconnectNamedPipe should succeed\n"); SetLastError(0xdeadbeef); ok(ConnectNamedPipe(pipewrite,&ol) == FALSE, "ConnectNamedPipe should fail\n"); - todo_wine ok(GetLastError() == ERROR_PIPE_LISTENING, "got %d should be ERROR_PIPE_LISTENING\n", GetLastError()); + ok(GetLastError() == ERROR_PIPE_LISTENING, "got %d should be ERROR_PIPE_LISTENING\n", GetLastError()); if (GetLastError() == ERROR_IO_PENDING) CancelIo(pipewrite); ok(CloseHandle(ol.hEvent), "CloseHandle for the event failed\n"); diff --git a/server/named_pipe.c b/server/named_pipe.c index f3251439b2..d40810a8ce 100644 --- a/server/named_pipe.c +++ b/server/named_pipe.c @@ -1108,6 +1108,12 @@ static int pipe_server_ioctl( struct fd *fd, ioctl_code_t code, struct async *as return 0; }
+ if (server->pipe_end.flags & NAMED_PIPE_NONBLOCKING_MODE) + { + set_error( STATUS_PIPE_LISTENING ); + return 0; + } + queue_async( &server->listen_q, async ); async_wake_up( &server->pipe_end.pipe->waiters, STATUS_SUCCESS ); set_error( STATUS_PENDING );