// Native thread-description smoke test. #include #include #include #include #include #include #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 // Writes one NUL-terminated string to the console. static void print(const char* text) { SYS_WRITE_CONSOLE_REQUEST request = { sizeof(SYS_WRITE_CONSOLE_REQUEST), SysSvFromCstr(text) }; (void)SysWriteConsole(&request); } // Terminates the test immediately. static void exit_now(int32_t code) { for (;;) SysExitProcess(code); } // Fails the test with one diagnostic. 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; }