Files
nt-lite/code/user/tests/would_block_smoke/would_block_smoke.c
T

247 lines
7.3 KiB
C

// Native would-block and queue-timeout contract smoke test.
// Verifies that queue try, finite-timeout, and infinite-wait requests return distinct statuses.
#include <stdint.h>
#include <ntlite/syscall.h>
#define TEST_FILL_LIMIT 1024u
#define TEST_MESSAGE_WAKE 0x77616b65u
#define TEST_WAKE_VALUE 0x12345678u
typedef struct TEST_QUEUE_MESSAGE {
uint32_t Type;
uint32_t Flags;
uint64_t Param[6];
} TEST_QUEUE_MESSAGE;
typedef char TEST_QUEUE_MESSAGE_SIZE_CHECK[
(sizeof(TEST_QUEUE_MESSAGE) == SYS_QUEUE_MESSAGE_SIZE) ? 1 : -1
];
typedef struct TEST_SENDER_CONTEXT {
uint32_t Queue;
volatile uint32_t Status;
} TEST_SENDER_CONTEXT;
// Sends one message with the queue timeout request ABI.
static uint32_t send_queue(uint32_t queue, const TEST_QUEUE_MESSAGE* msg, uint64_t timeoutMilliseconds) {
SYS_QUEUE_SEND_REQUEST req = {
sizeof(req),
queue,
SYS_QUEUE_FLAG_NONE,
timeoutMilliseconds,
msg,
};
return SysSendQueue(&req);
}
// Receives one message with the queue timeout request ABI.
static uint32_t recv_queue(uint32_t queue, TEST_QUEUE_MESSAGE* msg, uint64_t timeoutMilliseconds) {
SYS_QUEUE_RECV_REQUEST req = {
sizeof(req),
queue,
SYS_QUEUE_FLAG_NONE,
timeoutMilliseconds,
msg,
};
return SysRecvQueue(&req);
}
// Prints one NUL-terminated string to the kernel console.
static void print(const char* s) {
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
SysWriteConsole(&req);
}
// Prints one unsigned integer to the kernel console.
static void print_u64(uint64_t v) {
char buf[32];
int i = 0;
if (v == 0) {
print("0");
return;
}
while (v && i < (int)sizeof(buf)) {
buf[i++] = (char)('0' + (v % 10));
v /= 10;
}
while (i--) {
char c = buf[i];
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
SysWriteConsole(&req);
}
}
// Terminates the current test process.
static void exit_now(int code) {
for (;;) SysExitProcess(code);
}
// Fails the test with one plain message.
static void fail(const char* msg) {
print("would_block_smoke: ");
print(msg);
print("\n");
exit_now(1);
}
// Fails the test with one status value.
static void fail_status(const char* what, uint32_t status) {
print("would_block_smoke: ");
print(what);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Requires one exact status value.
static void expect_status(const char* what, uint32_t got, uint32_t expected) {
if (got == expected) return;
print("would_block_smoke: ");
print(what);
print(" got=");
print_u64(got);
print(" expected=");
print_u64(expected);
print("\n");
exit_now(1);
}
// Sends one wake message after the receiver has had a chance to block.
static void sender_thread(uintptr_t arg) {
TEST_SENDER_CONTEXT* ctx = (TEST_SENDER_CONTEXT*)arg;
if (!ctx) SysExitThread(1);
SysSleep(20);
TEST_QUEUE_MESSAGE msg = {0};
msg.Type = TEST_MESSAGE_WAKE;
msg.Param[0] = TEST_WAKE_VALUE;
ctx->Status = send_queue(ctx->Queue, &msg, SYS_WAIT_TIMEOUT_INFINITE);
SysExitThread(ctx->Status == NTLSTATUS_SUCCESS ? 0 : 1);
}
// Fills one queue with immediate sends until the queue reports would-block.
static void fill_until_would_block(uint32_t queue) {
TEST_QUEUE_MESSAGE msg = {0};
msg.Type = 1;
uint32_t sends = 0;
for (; sends < TEST_FILL_LIMIT; sends++) {
msg.Param[0] = sends;
uint32_t s = send_queue(queue, &msg, 0);
if (s == NTLSTATUS_WOULD_BLOCK)
break;
if (s != NTLSTATUS_SUCCESS)
fail_status("fill send", s);
}
if (sends == 0 || sends == TEST_FILL_LIMIT)
fail("queue fill did not reach would-block");
}
// Verifies queue try and finite-timeout status distinctions.
static void test_queue_empty_and_full(void) {
uint32_t queue = 0;
uint32_t s = SysCreateQueue(&queue);
if (s != NTLSTATUS_SUCCESS) fail_status("create empty queue", s);
TEST_QUEUE_MESSAGE msg = {0};
expect_status("empty nonblocking receive", recv_queue(queue, &msg, 0), NTLSTATUS_WOULD_BLOCK);
expect_status("empty finite receive", recv_queue(queue, &msg, 20), NTLSTATUS_TIMEOUT);
SysCloseHandle(queue);
queue = 0;
s = SysCreateQueue(&queue);
if (s != NTLSTATUS_SUCCESS) fail_status("create full queue", s);
fill_until_would_block(queue);
expect_status("full nonblocking send", send_queue(queue, &msg, 0), NTLSTATUS_WOULD_BLOCK);
expect_status("full finite send", send_queue(queue, &msg, 20), NTLSTATUS_TIMEOUT);
SysCloseHandle(queue);
}
// Verifies that an infinite receive composes with a sender wakeup.
static void test_queue_infinite_receive_wakeup(void) {
uint32_t queue = 0;
uint32_t s = SysCreateQueue(&queue);
if (s != NTLSTATUS_SUCCESS) fail_status("create wake queue", s);
TEST_SENDER_CONTEXT ctx = { queue, NTLSTATUS_PENDING };
uint32_t threadHandle = 0;
uint32_t threadId = 0;
SYS_CREATE_THREAD_REQUEST req = {
(uintptr_t)sender_thread,
(uintptr_t)&ctx,
1,
0,
&threadHandle,
&threadId
};
s = SysCreateThread(&req);
if (s != NTLSTATUS_SUCCESS) fail_status("create sender", s);
TEST_QUEUE_MESSAGE msg = {0};
s = recv_queue(queue, &msg, SYS_WAIT_TIMEOUT_INFINITE);
if (s != NTLSTATUS_SUCCESS) fail_status("infinite receive", s);
if (msg.Type != TEST_MESSAGE_WAKE || msg.Param[0] != TEST_WAKE_VALUE)
fail("wake message payload mismatch");
s = SysWait(threadHandle, SYS_WAIT_FLAG_NONE);
if (s != NTLSTATUS_SUCCESS) fail_status("wait sender", s);
if (ctx.Status != NTLSTATUS_SUCCESS) fail_status("sender status", ctx.Status);
SysCloseHandle(threadHandle);
SysCloseHandle(queue);
}
// Verifies that request metadata is part of the queue ABI contract.
static void test_queue_request_validation(void) {
uint32_t queue = 0;
uint32_t s = SysCreateQueue(&queue);
if (s != NTLSTATUS_SUCCESS) fail_status("create validation queue", s);
TEST_QUEUE_MESSAGE msg = {0};
expect_status("null send request", SysSendQueue((const SYS_QUEUE_SEND_REQUEST*)0), NTLSTATUS_INVALID_PARAMETER);
expect_status("null recv request", SysRecvQueue((const SYS_QUEUE_RECV_REQUEST*)0), NTLSTATUS_INVALID_PARAMETER);
SYS_QUEUE_SEND_REQUEST sendReq = {
0,
queue,
SYS_QUEUE_FLAG_NONE,
0,
&msg,
};
expect_status("bad send size", SysSendQueue(&sendReq), NTLSTATUS_INVALID_PARAMETER);
sendReq.Size = sizeof(sendReq);
sendReq.Flags = 1;
expect_status("bad send flags", SysSendQueue(&sendReq), NTLSTATUS_INVALID_PARAMETER);
sendReq.Flags = SYS_QUEUE_FLAG_NONE;
sendReq.Message = (const void*)0;
expect_status("bad send message", SysSendQueue(&sendReq), NTLSTATUS_INVALID_PARAMETER);
SYS_QUEUE_RECV_REQUEST recvReq = {
sizeof(recvReq),
queue,
SYS_QUEUE_FLAG_NONE,
0,
(void*)0,
};
expect_status("bad recv message", SysRecvQueue(&recvReq), NTLSTATUS_INVALID_PARAMETER);
SysCloseHandle(queue);
}
// Runs the native would-block smoke test.
int main(void) {
print("would_block_smoke: start\n");
test_queue_request_validation();
test_queue_empty_and_full();
test_queue_infinite_receive_wakeup();
print("would_block_smoke: PASS\n");
exit_now(0);
}