Files

208 lines
7.9 KiB
C

/* Named and anonymous pipes: overlapped transfers that complete through a port, cancellation, message mode and a blocking read on a thread. */
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
static const WCHAR name[] = L"\\\\.\\pipe\\raven-pipe-contract";
static const WCHAR message_name[] = L"\\\\.\\pipe\\raven-pipe-messages";
static HANDLE blocked_server;
static volatile LONG blocked_result;
static DWORD WINAPI blocked_reader(void *parameter)
{
char buffer[8];
DWORD count = 0;
if (!ReadFile(blocked_server, buffer, sizeof(buffer), &count, NULL) || count != 4 || buffer[0] != 'w' || buffer[3] != 'e')
blocked_result = -1;
else
blocked_result = 1;
return 0;
}
static int same(const char *left, const char *right, int length)
{
int index;
for (index = 0; index < length; ++index)
if (left[index] != right[index])
return 0;
return 1;
}
int raven_entry(void)
{
HANDLE server, client, port, second, read_end, write_end, thread, event;
OVERLAPPED connect_io = {0}, read_io = {0}, write_io = {0}, other_io = {0};
char buffer[64];
DWORD count, flags;
ULONG_PTR key;
OVERLAPPED *finished;
DWORD error;
server = CreateNamedPipeW(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 4096, 4096, 0, NULL);
if (server == INVALID_HANDLE_VALUE)
return 1;
/* A second first instance of a name in use is refused. */
second = CreateNamedPipeW(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE, 1, 4096, 4096, 0, NULL);
if (second != INVALID_HANDLE_VALUE || GetLastError() != ERROR_ACCESS_DENIED)
return 2;
if (!WaitNamedPipeW(name, 100))
return 3;
client = CreateFileW(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (client == INVALID_HANDLE_VALUE)
return 4;
if (GetFileType(client) != FILE_TYPE_PIPE)
return 5;
/* The client came before ConnectNamedPipe: the call reports a connection that exists. */
event = CreateEventW(NULL, TRUE, FALSE, NULL);
connect_io.hEvent = event;
if (ConnectNamedPipe(server, &connect_io) || GetLastError() != ERROR_PIPE_CONNECTED)
return 6;
port = CreateIoCompletionPort(server, NULL, 1, 0);
if (!port || !CreateIoCompletionPort(client, port, 2, 0))
return 7;
/* A read with nothing to read is pending until the client writes. */
read_io.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
if (ReadFile(server, buffer, sizeof(buffer), NULL, &read_io) || GetLastError() != ERROR_IO_PENDING)
return 8;
if (HasOverlappedIoCompleted(&read_io))
return 9;
if (GetOverlappedResult(server, &read_io, &count, FALSE) || GetLastError() != ERROR_IO_INCOMPLETE)
return 10;
write_io.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
if (!WriteFile(client, "hello", 5, NULL, &write_io) && GetLastError() != ERROR_IO_PENDING)
return 11;
/* The write and the read each report to the port. */
{
int seen_read = 0, seen_write = 0, round;
for (round = 0; round < 2; ++round) {
if (!GetQueuedCompletionStatus(port, &count, &key, &finished, 2000))
return 12;
if (key == 1 && finished == &read_io && count == 5)
seen_read = 1;
else if (key == 2 && finished == &write_io && count == 5)
seen_write = 1;
else
return 13;
}
if (!seen_read || !seen_write)
return 14;
}
if (!same(buffer, "hello", 5))
return 15;
if (!GetOverlappedResult(server, &read_io, &count, TRUE) || count != 5)
return 16;
if (WaitForSingleObject(read_io.hEvent, 0) != WAIT_OBJECT_0)
return 17;
/* The other direction, with the read started on the client. */
if (ReadFile(client, buffer, sizeof(buffer), NULL, &other_io) || GetLastError() != ERROR_IO_PENDING)
return 18;
if (!WriteFile(server, "world!", 6, &count, &write_io) && GetLastError() != ERROR_IO_PENDING)
return 19;
{
int round;
for (round = 0; round < 2; ++round) {
if (!GetQueuedCompletionStatus(port, &count, &key, &finished, 2000))
return 20;
}
}
if (!same(buffer, "world!", 6))
return 21;
/* A cancelled read completes as aborted. */
if (ReadFile(server, buffer, sizeof(buffer), NULL, &read_io) || GetLastError() != ERROR_IO_PENDING)
return 22;
if (!CancelIoEx(server, &read_io))
return 23;
if (!GetQueuedCompletionStatus(port, &count, &key, &finished, 2000) && GetLastError() != ERROR_OPERATION_ABORTED)
return 24;
if (finished != &read_io || GetOverlappedResult(server, &read_io, &count, FALSE) || GetLastError() != ERROR_OPERATION_ABORTED)
return 25;
if (CancelIoEx(server, &read_io) || GetLastError() != ERROR_NOT_FOUND)
return 26;
/* Closing the client breaks the pipe for the server's pending read. */
if (ReadFile(server, buffer, sizeof(buffer), NULL, &read_io) || GetLastError() != ERROR_IO_PENDING)
return 27;
CloseHandle(client);
if (GetQueuedCompletionStatus(port, &count, &key, &finished, 2000) || GetLastError() != ERROR_BROKEN_PIPE || finished != &read_io)
return 28;
if (ReadFile(server, buffer, sizeof(buffer), NULL, &read_io) || GetLastError() != ERROR_BROKEN_PIPE)
return 29;
if (!DisconnectNamedPipe(server))
return 30;
if (ReadFile(server, buffer, sizeof(buffer), NULL, &read_io) || GetLastError() != ERROR_PIPE_LISTENING)
return 31;
CloseHandle(server);
CloseHandle(port);
/* A handle without overlapped I/O blocks only the thread that reads, and ConnectNamedPipe waits for a client. */
blocked_server = CreateNamedPipeW(name, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 4096, 4096, 0, NULL);
if (blocked_server == INVALID_HANDLE_VALUE)
return 32;
client = CreateFileW(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (client == INVALID_HANDLE_VALUE)
return 33;
if (ConnectNamedPipe(blocked_server, NULL) || GetLastError() != ERROR_PIPE_CONNECTED)
return 34;
thread = CreateThread(NULL, 0, blocked_reader, NULL, 0, NULL);
if (!thread)
return 35;
Sleep(50);
if (blocked_result != 0)
return 36;
if (!WriteFile(client, "wire", 4, &count, NULL) || count != 4)
return 37;
if (WaitForSingleObject(thread, 3000) != WAIT_OBJECT_0 || blocked_result != 1)
return 38;
CloseHandle(thread);
CloseHandle(client);
CloseHandle(blocked_server);
/* Message mode keeps the boundaries and reports a message that did not fit. */
server = CreateNamedPipeW(message_name, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, 4096, 4096, 0, NULL);
if (server == INVALID_HANDLE_VALUE)
return 39;
client = CreateFileW(message_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (client == INVALID_HANDLE_VALUE)
return 40;
flags = PIPE_READMODE_MESSAGE;
if (!SetNamedPipeHandleState(client, &flags, NULL, NULL))
return 41;
if (!WriteFile(client, "abc", 3, &count, NULL) || !WriteFile(client, "defghij", 7, &count, NULL))
return 42;
if (!ReadFile(server, buffer, 16, &count, NULL) || count != 3 || !same(buffer, "abc", 3))
return 43;
if (ReadFile(server, buffer, 4, &count, NULL) || GetLastError() != ERROR_MORE_DATA || count != 4 || !same(buffer, "defg", 4))
return 44;
if (!ReadFile(server, buffer, 16, &count, NULL) || count != 3 || !same(buffer, "hij", 3))
return 45;
CloseHandle(client);
CloseHandle(server);
/* An anonymous pipe carries bytes from the write end to the read end. */
if (!CreatePipe(&read_end, &write_end, NULL, 0))
return 46;
if (GetFileType(read_end) != FILE_TYPE_PIPE)
return 47;
if (!WriteFile(write_end, "anon", 4, &count, NULL) || count != 4)
return 48;
if (!PeekNamedPipe(read_end, NULL, 0, NULL, &count, NULL) || count != 4)
return 49;
if (!ReadFile(read_end, buffer, 16, &count, NULL) || count != 4 || !same(buffer, "anon", 4))
return 50;
CloseHandle(write_end);
if (ReadFile(read_end, buffer, 16, &count, NULL) || GetLastError() != ERROR_BROKEN_PIPE)
return 51;
CloseHandle(read_end);
/* A pipe nobody serves cannot be opened. */
error = 0;
if (CreateFileW(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL) != INVALID_HANDLE_VALUE)
return 52;
error = GetLastError();
if (error != ERROR_FILE_NOT_FOUND || WaitNamedPipeW(name, 10))
return 53;
return 0;
}