// Multi-threaded file-cache/VAD fault regression test. // Several threads map the same file mapping and fault/read pages concurrently. #include #include #include #include #define THREAD_COUNT 6u #define ITERATIONS 16u #define TEST_SIZE 32768u static uint8_t gPattern[TEST_SIZE]; typedef struct THREAD_CTX { uint32_t Index; uint32_t Mapping; volatile uint32_t Status; volatile uint32_t IterationsDone; } THREAD_CTX; static THREAD_CTX gThreads[THREAD_COUNT]; static void print(const char* s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); } 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[2] = { buf[i], 0 }; print(c); } } static void exit_now(int code) { while (true) SysExitProcess(code); } static void fail(const char* msg) { print("pcache_multithread: "); print(msg); print("\n"); exit_now(1); } static void fail_status(const char* what, uint32_t status) { print("pcache_multithread: "); print(what); print(" failed status="); print_u64(status); print("\n"); exit_now(1); } static uint32_t query_cache(SYS_FILE_CACHE_STATS* out) { memset(out, 0, sizeof(*out)); return SysQueryKernelStats(SYS_KERNEL_STATS_FILE_CACHE, out, sizeof(*out), 0); } static void delete_path(const char* path) { SYS_PATH_REQUEST req = { SysSvFromCstr(path) }; (void)SysDeleteFile(&req); } static uint32_t create_temp(uint32_t* fileOut, const char** pathOut) { static const char* paths[] = { "pcache_multithread.tmp", "/pcache_multithread.tmp", "1:/pcache_multithread.tmp", "0:/pcache_multithread.tmp", }; for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); ++i) { uint32_t h = 0; delete_path(paths[i]); SYS_OPEN_FILE_REQUEST req = SysMakeOpenFileRequest(SysSvFromCstr(paths[i]), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_WRITE | SYS_FILE_ACCESS_QUERY, SYS_FILE_CREATE_NEW, SYS_FILE_FLAG_NONE, &h); uint32_t s = SysOpenFile(&req); if (s == NTLSTATUS_SUCCESS) { *fileOut = h; if (pathOut) *pathOut = paths[i]; return NTLSTATUS_SUCCESS; } } return NTLSTATUS_NOT_FOUND; } static void fill_pattern(void) { for (size_t i = 0; i < TEST_SIZE; ++i) gPattern[i] = (uint8_t)(0x5Au ^ (uint8_t)(i * 13u) ^ (uint8_t)(i >> 3)); } static uint32_t validate_mapping(const uint8_t* base) { for (size_t i = 0; i < TEST_SIZE; ++i) { if (base[i] != gPattern[i]) return 0x10000000u | (uint32_t)(i & 0x0FFFFFFFu); } return NTLSTATUS_SUCCESS; } static void thread_main(uintptr_t arg) { THREAD_CTX* ctx = (THREAD_CTX*)arg; if (!ctx) SysExitThread(1); for (uint32_t iter = 0; iter < ITERATIONS; ++iter) { void* base = 0; uint32_t s = SysMapFile(ctx->Mapping, 0, TEST_SIZE, SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, &base); if (s != NTLSTATUS_SUCCESS || !base) { ctx->Status = s ? s : NTLSTATUS_INVALID_PARAMETER; SysExitThread(1); } volatile uint8_t* touch = (volatile uint8_t*)base; for (size_t off = 0; off < TEST_SIZE; off += 4096) (void)touch[off]; (void)touch[TEST_SIZE - 1]; s = validate_mapping((const uint8_t*)base); uint32_t fs = SysFreeVm(base); if (s != NTLSTATUS_SUCCESS) { ctx->Status = s; SysExitThread(1); } if (fs != NTLSTATUS_SUCCESS) { ctx->Status = fs; SysExitThread(1); } ctx->IterationsDone++; } ctx->Status = NTLSTATUS_SUCCESS; SysExitThread(0); } int main(void) { print("pcache_multithread: start\n"); fill_pattern(); uint32_t file = 0; const char* path = 0; uint32_t s = create_temp(&file, &path); if (s != NTLSTATUS_SUCCESS) fail_status("create temp", s); size_t written = 0; SYS_FILE_IO_REQUEST writeReq = SysMakeFileIoRequest(file, gPattern, sizeof(gPattern), &written); s = SysWriteFile(&writeReq); if (s != NTLSTATUS_SUCCESS) fail_status("write pattern", s); if (written != sizeof(gPattern)) fail("short write"); uint32_t mapping = 0; s = SysCreateFileMapping(file, SYS_VM_READ, SYS_MAP_FLAG_NONE, TEST_SIZE, &mapping); if (s != NTLSTATUS_SUCCESS) fail_status("create mapping", s); SYS_FILE_CACHE_STATS before; SYS_FILE_CACHE_STATS after; if (query_cache(&before) != NTLSTATUS_SUCCESS) fail("initial stats query failed"); uint32_t handles[THREAD_COUNT]; uint32_t tids[THREAD_COUNT]; memset(handles, 0, sizeof(handles)); memset(tids, 0, sizeof(tids)); memset(gThreads, 0, sizeof(gThreads)); for (uint32_t i = 0; i < THREAD_COUNT; ++i) { gThreads[i].Index = i; gThreads[i].Mapping = mapping; gThreads[i].Status = NTLSTATUS_PENDING; SYS_CREATE_THREAD_REQUEST req = { (uintptr_t)thread_main, (uintptr_t)&gThreads[i], 1, 0, &handles[i], &tids[i] }; s = SysCreateThread(&req); if (s != NTLSTATUS_SUCCESS) fail_status("create thread", s); } for (uint32_t i = 0; i < THREAD_COUNT; ++i) { s = SysWait(handles[i], SYS_WAIT_FLAG_NONE); if (s != NTLSTATUS_SUCCESS) fail_status("wait thread", s); SysCloseHandle(handles[i]); } for (uint32_t i = 0; i < THREAD_COUNT; ++i) { if (gThreads[i].Status != NTLSTATUS_SUCCESS) { print("pcache_multithread: worker "); print_u64(i); print(" status="); print_u64(gThreads[i].Status); print(" iterations="); print_u64(gThreads[i].IterationsDone); print("\n"); exit_now(1); } if (gThreads[i].IterationsDone != ITERATIONS) fail("worker iteration count mismatch"); } if (query_cache(&after) != NTLSTATUS_SUCCESS) fail("final stats query failed"); if (after.Loads <= before.Loads && after.Hits <= before.Hits) fail("concurrent mmap did not touch cache pages"); if (after.Hits <= before.Hits) fail("concurrent mmap did not reuse cache pages"); SysCloseHandle(mapping); SysCloseHandle(file); delete_path(path); print("pcache_multithread: loads+="); print_u64(after.Loads - before.Loads); print(" hits+="); print_u64(after.Hits - before.Hits); print(" races+="); print_u64(after.RaceDiscards - before.RaceDiscards); print("\n"); print("pcache_multithread: PASS\n"); exit_now(0); return 0; }