166 lines
5.9 KiB
C
166 lines
5.9 KiB
C
// Native thread-description smoke test.
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <ntlite/syscall.h>
|
|
#include <ntlite/status.h>
|
|
|
|
#define TEST_NAME_MAIN "thread-name-main"
|
|
#define TEST_NAME_WORKER "thread-name-worker"
|
|
#define TEST_MAX_THREADS 16u
|
|
#define TEST_EVENT_AUTO_RESET 0u
|
|
|
|
static void print(const char* text) {
|
|
SYS_WRITE_CONSOLE_REQUEST request = { sizeof(SYS_WRITE_CONSOLE_REQUEST), SysSvFromCstr(text) };
|
|
(void)SysWriteConsole(&request);
|
|
}
|
|
|
|
static void exit_now(int32_t code) {
|
|
for (;;) SysExitProcess(code);
|
|
}
|
|
|
|
static void fail(const char* text) {
|
|
print("thread_name_smoke: ");
|
|
print(text);
|
|
print("\n");
|
|
exit_now(1);
|
|
}
|
|
|
|
// Sets a description on the supplied native thread handle.
|
|
static KSTATUS set_name(SYS_HANDLE thread, const char* name) {
|
|
SYS_SET_THREAD_NAME_REQUEST request;
|
|
memset(&request, 0, sizeof(request));
|
|
request.Size = sizeof(request);
|
|
request.ThreadHandle = thread;
|
|
request.Name = SysSvFromCstr(name);
|
|
return SysSetThreadName(&request);
|
|
}
|
|
|
|
// A suspended worker only needs to prove its named handle remains runnable.
|
|
static void worker(uintptr_t argument) {
|
|
(void)argument;
|
|
SysExitThread(0);
|
|
}
|
|
|
|
// A detached worker proves running threads do not need a returned handle or TID.
|
|
static void detached_worker(uintptr_t argument) {
|
|
(void)SysSignalEvent((SYS_HANDLE)argument);
|
|
SysExitThread(0);
|
|
}
|
|
|
|
// Runs the thread-description ABI smoke test.
|
|
int main(void) {
|
|
print("thread_name_smoke: start\n");
|
|
|
|
if (set_name(SYS_HANDLE_INVALID, TEST_NAME_MAIN) != NTLSTATUS_SUCCESS)
|
|
fail("set current name failed");
|
|
|
|
// Native thread descriptions accept well-formed UTF-8 and reject malformed bytes.
|
|
static const char unicodeName[] = "thread-\xC3\xA9";
|
|
if (set_name(SYS_HANDLE_INVALID, unicodeName) != NTLSTATUS_SUCCESS)
|
|
fail("valid UTF-8 thread name rejected");
|
|
|
|
static const char invalidName[] = { 'b', (char)0xC0, (char)0xAF, 0 };
|
|
if (set_name(SYS_HANDLE_INVALID, invalidName) != NTLSTATUS_INVALID_PARAMETER)
|
|
fail("overlong UTF-8 thread name accepted");
|
|
|
|
SYS_SET_THREAD_NAME_REQUEST nulRequest;
|
|
memset(&nulRequest, 0, sizeof(nulRequest));
|
|
nulRequest.Size = sizeof(nulRequest);
|
|
nulRequest.ThreadHandle = SYS_HANDLE_INVALID;
|
|
static const char nulName[] = { 'a', 0, 'b' };
|
|
nulRequest.Name = SysSvFromParts(nulName, sizeof(nulName));
|
|
if (SysSetThreadName(&nulRequest) != NTLSTATUS_INVALID_PARAMETER)
|
|
fail("embedded NUL thread name accepted");
|
|
|
|
if (set_name(SYS_HANDLE_INVALID, TEST_NAME_MAIN) != NTLSTATUS_SUCCESS)
|
|
fail("restore current thread name failed");
|
|
|
|
char name[SYS_THREAD_NAME_MAX];
|
|
size_t length = 0;
|
|
KSTATUS status = SysQueryThreadName(SYS_HANDLE_INVALID, name, sizeof(name), &length);
|
|
if (status != NTLSTATUS_SUCCESS || length != sizeof(TEST_NAME_MAIN) - 1u ||
|
|
strcmp(name, TEST_NAME_MAIN) != 0)
|
|
fail("query current name mismatch");
|
|
|
|
size_t required = 0;
|
|
status = SysQueryThreadName(SYS_HANDLE_INVALID, (char*)0, 0, &required);
|
|
if (status != NTLSTATUS_BUFFER_TOO_SMALL || required != sizeof(TEST_NAME_MAIN) - 1u)
|
|
fail("length-only query contract mismatch");
|
|
|
|
SYS_HANDLE thread = SYS_HANDLE_INVALID;
|
|
uint32_t tid = 0;
|
|
SYS_CREATE_THREAD_REQUEST create;
|
|
memset(&create, 0, sizeof(create));
|
|
create.Size = sizeof(create);
|
|
create.Entry = (uintptr_t)worker;
|
|
create.Priority = 1u;
|
|
create.Flags = SYS_CREATE_THREAD_FLAG_SUSPENDED;
|
|
create.ThreadHandleOut = &thread;
|
|
create.ThreadIdOut = &tid;
|
|
status = SysCreateThread(&create);
|
|
if (status != NTLSTATUS_SUCCESS || thread == SYS_HANDLE_INVALID || tid == 0)
|
|
fail("create suspended worker failed");
|
|
|
|
status = set_name(thread, TEST_NAME_WORKER);
|
|
if (status != NTLSTATUS_SUCCESS)
|
|
fail("set worker name failed");
|
|
|
|
memset(name, 0, sizeof(name));
|
|
length = 0;
|
|
status = SysQueryThreadName(thread, name, sizeof(name), &length);
|
|
if (status != NTLSTATUS_SUCCESS || length != sizeof(TEST_NAME_WORKER) - 1u ||
|
|
strcmp(name, TEST_NAME_WORKER) != 0)
|
|
fail("query worker name mismatch");
|
|
|
|
SYS_THREAD_INFO threads[TEST_MAX_THREADS];
|
|
size_t threadCount = 0;
|
|
status = SysGetThreadList(SysGetCurrentPid(), threads, TEST_MAX_THREADS, &threadCount);
|
|
if (status != NTLSTATUS_SUCCESS)
|
|
fail("thread enumeration failed");
|
|
|
|
bool foundWorker = false;
|
|
size_t visible = threadCount < TEST_MAX_THREADS ? threadCount : TEST_MAX_THREADS;
|
|
for (size_t i = 0; i < visible; ++i) {
|
|
if (threads[i].ThreadId == tid) {
|
|
if (strcmp(threads[i].Name, TEST_NAME_WORKER) != 0)
|
|
fail("thread-list name mismatch");
|
|
foundWorker = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!foundWorker)
|
|
fail("worker missing from thread list");
|
|
|
|
uint32_t previousSuspendCount = 0;
|
|
status = SysResumeThreadAndWait(thread, &previousSuspendCount);
|
|
if (status != NTLSTATUS_SUCCESS || previousSuspendCount != 1u)
|
|
fail("resume-and-wait worker failed");
|
|
(void)SysCloseHandle(thread);
|
|
|
|
SYS_HANDLE detachedReady = SYS_HANDLE_INVALID;
|
|
status = SysCreateEvent(TEST_EVENT_AUTO_RESET, 0, &detachedReady);
|
|
if (status != NTLSTATUS_SUCCESS)
|
|
fail("create detached-ready event failed");
|
|
|
|
memset(&create, 0, sizeof(create));
|
|
create.Size = sizeof(create);
|
|
create.Entry = (uintptr_t)detached_worker;
|
|
create.Argument = (uintptr_t)detachedReady;
|
|
create.Priority = 1u;
|
|
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
|
|
status = SysCreateThread(&create);
|
|
if (status != NTLSTATUS_SUCCESS)
|
|
fail("create detached worker failed");
|
|
status = SysWait(detachedReady, SYS_WAIT_FLAG_NONE);
|
|
if (status != NTLSTATUS_SUCCESS)
|
|
fail("detached worker did not run");
|
|
(void)SysCloseHandle(detachedReady);
|
|
|
|
print("thread_name_smoke: PASS\n");
|
|
exit_now(0);
|
|
return 0;
|
|
}
|