From d4ba121b56c237cf8bf3d25494ae0f02a16f8ac1 Mon Sep 17 00:00:00 2001 From: amir Date: Tue, 31 Mar 2026 00:26:03 +0100 Subject: [PATCH] Implementation of IO Ring in Windows Fixing the two compilation warnings. --- .../clangd/index/arena.c.9D15F3F90FD5376F.idx | Bin 0 -> 15052 bytes .../clangd/index/arena.h.D70DBB2C6778A245.idx | Bin 0 -> 8146 bytes .../clangd/index/base.h.2DF61E974E1BB064.idx | Bin 0 -> 5110 bytes .../index/file_hasher.c.AC1BEF31045A7497.idx | Bin 0 -> 3904 bytes .../index/lf_mpmc.h.FB4A8CD7AC664EBA.idx | Bin 0 -> 6734 bytes .../index/platform.c.2B562A3FE6816950.idx | Bin 0 -> 25908 bytes .../xxh_x86dispatch.h.3E8D4826C191778C.idx | Bin 0 -> 2570 bytes .../index/xxhash.h.11DA710B069D4A59.idx | Bin 0 -> 124706 bytes .gitignore | 1 + README.md | 14 +- base.h | 16 +- compile_commands.json | 7 + file_hasher.c | 64 +- io_ring_test.c | 147 ++++ ioringapi.c | 285 +++++++ platform.c | 694 ++++++++++++++---- xxh_x86dispatch.c | 2 + 17 files changed, 1078 insertions(+), 152 deletions(-) create mode 100644 .cache/clangd/index/arena.c.9D15F3F90FD5376F.idx create mode 100644 .cache/clangd/index/arena.h.D70DBB2C6778A245.idx create mode 100644 .cache/clangd/index/base.h.2DF61E974E1BB064.idx create mode 100644 .cache/clangd/index/file_hasher.c.AC1BEF31045A7497.idx create mode 100644 .cache/clangd/index/lf_mpmc.h.FB4A8CD7AC664EBA.idx create mode 100644 .cache/clangd/index/platform.c.2B562A3FE6816950.idx create mode 100644 .cache/clangd/index/xxh_x86dispatch.h.3E8D4826C191778C.idx create mode 100644 .cache/clangd/index/xxhash.h.11DA710B069D4A59.idx create mode 100644 compile_commands.json create mode 100644 io_ring_test.c create mode 100644 ioringapi.c diff --git a/.cache/clangd/index/arena.c.9D15F3F90FD5376F.idx b/.cache/clangd/index/arena.c.9D15F3F90FD5376F.idx new file mode 100644 index 0000000000000000000000000000000000000000..c010d8446142e3379786c65bd590fb92bf4c8b83 GIT binary patch literal 15052 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 HQI7WivN6hD literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/arena.h.D70DBB2C6778A245.idx b/.cache/clangd/index/arena.h.D70DBB2C6778A245.idx new file mode 100644 index 0000000000000000000000000000000000000000..fc1a62eb3622636b20d615bab03547459e76b4e6 GIT binary patch literal 8146 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 a@qFvc1na|o$UA9C@a5l5S*trHi2nlPs|{cP literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/base.h.2DF61E974E1BB064.idx b/.cache/clangd/index/base.h.2DF61E974E1BB064.idx new file mode 100644 index 0000000000000000000000000000000000000000..cd6107e707535eb8a13671402c2bef755800d0af GIT binary patch literal 5110 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/lf_mpmc.h.FB4A8CD7AC664EBA.idx b/.cache/clangd/index/lf_mpmc.h.FB4A8CD7AC664EBA.idx new file mode 100644 index 0000000000000000000000000000000000000000..26a0ed21c617715979968e7302ae05ebeccf8667 GIT binary patch literal 6734 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 P#j2e7U=mDBAVK&q6FBs} literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/platform.c.2B562A3FE6816950.idx b/.cache/clangd/index/platform.c.2B562A3FE6816950.idx new file mode 100644 index 0000000000000000000000000000000000000000..4a35d146dcaf8411f9578eabe645c640c2f03a0d GIT binary patch literal 25908 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 literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/xxh_x86dispatch.h.3E8D4826C191778C.idx b/.cache/clangd/index/xxh_x86dispatch.h.3E8D4826C191778C.idx new file mode 100644 index 0000000000000000000000000000000000000000..d56cdc9288efb9cd36a79b8a200352eb2908bcfb GIT binary patch literal 2570 zcmbtWe`p(J82*x`vzMLCben6h%{;bR))5wyT#oy(b*(Y&vRT`vp{qI?(&W0_%#V`0 z)Vff_Kh!zd#-NC!xc#BEPA2H~hv3lSMuowUB06TrU?_t*{M}L37M$OGx$koAsznVH z?s>lFd!P4xzxVrSIMmW&wF3}Hgoe_R8nXca*1*I~a(xhnMW! z26rfuWD-Op&vXR4>@?U?SMUhh#;2Rxf?XZWfuQCbRiLF5Rq3thW7tL46}&<`n}L}P zX5|FERn83LRmA%QMLwu$Q&MKQp%`#^rT4!nHFKYqBg;gSF(nDDU@?h*;Z=-aPD+vD zu!Sgp24_{|o+?%W3t6cvF4`OsHN-`idI2N+uXUGpT#HLyDdggeiuD*N8Razqbi^Jn z*C(q=G$AVkF*TlC%;pCY+5{}W_=2n^yHE%JQ6MFjEVIlqjyk!lp}zLFev8(Z*cq+U z3R^VZuyTEn?pumVxm-3E&n6_a)Gx*0y_6#GMuNCd*A#4SS{lr>LsN9!uQ1ugg%IeM zQ)r++&j?Bzwl%oS;4u|RMNJCFquP(K7N z?SAFn+aw^dKy->cARe%S^&4P>LocOH_AGRsC&43F;92Y01gcl9vear-ygxbh$87%P zDH2p-0q^8Rlq9kkc6q=2;`Ejo60E@j*2!`u3jiNYg@tc%{gGqGE_a@-C?_eO!&hT8 zkYXH6jnNE>+0Q5?s?X?J($hgJ11i=FBrdTMoZ<6JD&#q-d490Stm z6mN{hm8_j#V$Mmc$A<4 z8X?^P4-ll$;OO+(gSTeMJLwMC+C9XcM|Bn?e|qi4=eMpFG$)vj$qA-|oqDew`t7@h z=~uO!VLEs(%o(PGQT^(Gzz;-=zuU-LZTqHn9x2kiP89U`buvW zx|`^xaqOu=>P*$8E4LvXB0^3~<8gvP&;z3LIVly>K9yWt*`R$GT?STao*lfg_u`uw HOAYu7W+SLD literal 0 HcmV?d00001 diff --git a/.cache/clangd/index/xxhash.h.11DA710B069D4A59.idx b/.cache/clangd/index/xxhash.h.11DA710B069D4A59.idx new file mode 100644 index 0000000000000000000000000000000000000000..349da000a8e8d2003889f7f855edf5d69bd00e87 GIT binary patch literal 124706 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 q6Ic8G*ABmb1wIHD<8 #include #include #include +#include #include #include #include @@ -11,13 +13,25 @@ #include #if defined(_WIN32) || defined(_WIN64) -#define PLATFORM_WINDOWS 1 +// #define PLATFORM_WINDOWS 1 +// #define WIN32_LEAN_AND_MEAN +// #define NTDDI_VERSION NTDDI_WIN11 +// +// #pragma comment(lib, "kernel32.Lib") + #include #include #include +#include +#include #include #include #include +#include + +#if defined(_MSC_VER) +#pragma comment(lib, "advapi32.lib") +#endif #define strdup _strdup #else diff --git a/compile_commands.json b/compile_commands.json new file mode 100644 index 0000000..34a424d --- /dev/null +++ b/compile_commands.json @@ -0,0 +1,7 @@ +[ + { + "directory": "D:/Code/c/filehasher", + "command": "clang-cl /O2 file_hasher.c xxh_x86dispatch.c", + "file": "file_hasher.c" + } +] \ No newline at end of file diff --git a/file_hasher.c b/file_hasher.c index 8a2b57f..682f7f7 100644 --- a/file_hasher.c +++ b/file_hasher.c @@ -89,12 +89,33 @@ int main(int argc, char **argv) { // ------------------------------- // Scanning and hashing // ------------------------------- + + // test_io_ring(); MPMCQueue dir_queue; mpmc_init(&dir_queue, MiB(1)); MPMCQueue file_queue; mpmc_init(&file_queue, MiB(1)); + // Starting hash threads + // size_t num_hash_threads = num_threads; + // + // WorkerContext workers[num_hash_threads]; + // Thread *hash_threads = + // arena_push(&gp_arena, sizeof(Thread) * num_hash_threads, true); + // + // for (size_t i = 0; i < num_hash_threads; ++i) { + // workers[i].arena = arena_create(¶ms); + // workers[i].file_queue = &file_queue; + // + // if (thread_create(&hash_threads[i], (ThreadFunc)hash_worker, &workers[i]) + // != + // 0) { + // fprintf(stderr, "Failed to create hash thread %zu\n", i); + // exit(1); + // } + // } + // Starting hash threads size_t num_hash_threads = num_threads; @@ -106,13 +127,46 @@ int main(int argc, char **argv) { workers[i].arena = arena_create(¶ms); workers[i].file_queue = &file_queue; - if (thread_create(&hash_threads[i], (ThreadFunc)hash_worker, &workers[i]) != + if (thread_create(&hash_threads[i], (ThreadFunc)hash_worker_io_ring, &workers[i]) + != 0) { fprintf(stderr, "Failed to create hash thread %zu\n", i); exit(1); } } + // Starting hash threads + // size_t num_hash_threads = num_threads; + // + // WorkerContext workers[num_hash_threads]; + // Thread *hash_threads = + // arena_push(&gp_arena, sizeof(Thread) * num_hash_threads, true); + // + // // Check if I/O Ring is available + // bool io_ring_available = false; + // HIORING test_ring = io_ring_init(); + // if (test_ring) { + // io_ring_available = true; + // io_ring_cleanup(test_ring); + // // printf("I/O Ring is available, using high-performance async I/O\n"); + // } else { + // printf("I/O Ring not available, using buffered I/O\n"); + // } + // + // for (size_t i = 0; i < num_hash_threads; ++i) { + // workers[i].arena = arena_create(¶ms); + // workers[i].file_queue = &file_queue; + // + // // Select the appropriate worker function + // ThreadFunc fn = io_ring_available ? (ThreadFunc)hash_worker_io_ring + // : (ThreadFunc)hash_worker; + // + // if (thread_create(&hash_threads[i], fn, &workers[i]) != 0) { + // fprintf(stderr, "Failed to create hash thread %zu\n", i); + // exit(1); + // } + // } + // Starting progress printing thread Thread progress_thread_handle; if (thread_create(&progress_thread_handle, (ThreadFunc)progress_thread, @@ -209,6 +263,14 @@ int main(int argc, char **argv) { // ------------------------------- // Print summary // ------------------------------- + // DEBUG + uint64_t incomplete = atomic_load(&g_io_ring_fallbacks); + if (incomplete > 0) { + printf( + "\nI/O Ring incomplete files: %llu (fallback to buffered I/O used)\n", + (unsigned long long)incomplete); + } + // double total_seconds = timer_elapsed(&total_timer); printf("Completed hashing %zu files\n", total_found); diff --git a/io_ring_test.c b/io_ring_test.c new file mode 100644 index 0000000..39fe81f --- /dev/null +++ b/io_ring_test.c @@ -0,0 +1,147 @@ +#pragma once + +#include +#include +// #include "ioringapi.c" +#include + +// Initialize I/O Ring +HIORING io_ring_init(void) { + + // if (!io_ring_load_functions()) { + // printf("[I/O Ring] Failed to load functions\n"); + // return NULL; + // } + + IORING_CAPABILITIES caps; + ZeroMemory(&caps, sizeof(caps)); + + HRESULT hr = QueryIoRingCapabilities(&caps); + if (FAILED(hr)) { + printf("[I/O Ring] QueryIoRingCapabilities failed: 0x%08lx\n", hr); + return NULL; + } + + // printf("[I/O Ring] MaxVersion=%d, MaxSubmission=%u, MaxCompletion=%u\n", + // (int)caps.MaxVersion, caps.MaxSubmissionQueueSize, + // caps.MaxCompletionQueueSize); + + if (caps.MaxVersion < IORING_VERSION_1) { + printf("[I/O Ring] Version too old\n"); + return NULL; + } + + IORING_CREATE_FLAGS flags = {0}; + HIORING ring = NULL; + + // hr = CreateIoRing(IORING_VERSION_1, flags, 256, 512, &ring); + hr = CreateIoRing(caps.MaxVersion, flags, 256, 512, &ring); + if (FAILED(hr)) { + printf("[I/O Ring] CreateIoRing failed: 0x%08lx\n", hr); + return NULL; + } + + // printf("[I/O Ring] Created successfully\n"); + + // Check if read operation is supported + + // HRESULT io_ring_support = IsIoRingOpSupported(ring, IORING_OP_READ); + // if (io_ring_support == S_FALSE) { + // printf("[I/O Ring] Not supported, %ld /n", io_ring_support); + // } + + // Get ring info + IORING_INFO info; + ZeroMemory(&info, sizeof(info)); + GetIoRingInfo(ring, &info); + // printf("[I/O Ring] Submission: %u, Completion: %u\n", + // info.SubmissionQueueSize, info.CompletionQueueSize); + + return ring; +} + +void io_ring_cleanup(HIORING ring) { + if (ring) { + CloseIoRing(ring); + // printf("[I/O Ring] Closed\n"); + } +} + +// Read file using I/O Ring +int io_ring_read_file(HIORING ring, HANDLE hFile, void *buffer, DWORD size, + UINT64 offset) { + + IORING_HANDLE_REF file_ref = IoRingHandleRefFromHandle(hFile); + IORING_BUFFER_REF buf_ref = IoRingBufferRefFromPointer(buffer); + + HRESULT hr = BuildIoRingReadFile(ring, file_ref, buf_ref, size, offset, + (UINT_PTR)buffer, IOSQE_FLAGS_NONE); + + if (FAILED(hr)) + return -1; + + UINT32 submitted = 0; + hr = SubmitIoRing(ring, 1, INFINITE, &submitted); + if (FAILED(hr) || submitted == 0) + return -1; + + for (;;) { + IORING_CQE cqe; + hr = PopIoRingCompletion(ring, &cqe); + + if (FAILED(hr)) + continue; + + if (cqe.UserData != (UINT_PTR)buffer) + continue; + + if (FAILED(cqe.ResultCode)) + return -1; + + return (int)cqe.Information; + } +} + +// Test function +void test_io_ring(void) { + printf("\n=== Testing I/O Ring ===\n"); + + HIORING ring = io_ring_init(); + if (!ring) { + printf("I/O Ring not available\n"); + return; + } + + // Create test file + HANDLE hFile = CreateFileA("test.txt", GENERIC_READ | GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE) { + char test_data[] = + "Hello, I/O Ring! This is a test of the Windows I/O Ring API."; + DWORD written; + WriteFile(hFile, test_data, sizeof(test_data), &written, NULL); + CloseHandle(hFile); + } + + // Read using I/O Ring + hFile = CreateFileA("test.txt", GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (hFile != INVALID_HANDLE_VALUE) { + char buffer[512] = {0}; + int bytes = io_ring_read_file(ring, hFile, buffer, sizeof(buffer), 0); + if (bytes > 0) { + printf("Read %d bytes: %s\n", bytes, buffer); + } else { + printf("Failed to read file\n"); + } + CloseHandle(hFile); + } else { + printf("Failed to open test file\n"); + } + + // Cleanup + DeleteFileA("test.txt"); + io_ring_cleanup(ring); + + printf("=== Test complete ===\n\n"); +} diff --git a/ioringapi.c b/ioringapi.c new file mode 100644 index 0000000..202905c --- /dev/null +++ b/ioringapi.c @@ -0,0 +1,285 @@ +#pragma once +#include +#include +#include + +// Forward declarations +typedef struct IORING_HANDLE_REF IORING_HANDLE_REF; +typedef struct IORING_BUFFER_REF IORING_BUFFER_REF; +typedef void *HIORING; + +/* --------------------- Types declaration --------------------- */ +typedef enum IORING_CREATE_ADVISORY_FLAGS { + IORING_CREATE_ADVISORY_FLAGS_NONE, + IORING_CREATE_SKIP_BUILDER_PARAM_CHECKS +} IORING_CREATE_ADVISORY_FLAGS; +// Specifies advisory flags for creating an I/O ring with a call to +// CreateIoRing. + +typedef enum IORING_CREATE_REQUIRED_FLAGS { + IORING_CREATE_REQUIRED_FLAGS_NONE +} IORING_CREATE_REQUIRED_FLAGS; +// Specifies required flags for creating an I/O ring with a call to +// CreateIoRing. + +typedef enum IORING_REF_KIND { + IORING_REF_RAW = 0, + IORING_REF_REGISTERED = 1, +} IORING_REF_KIND; +// Specifies the type of an IORING_HANDLE_REF structure. + +typedef enum IORING_SQE_FLAGS { + IOSQE_FLAGS_NONE, + IOSQE_FLAGS_DRAIN_PRECEDING_OPS +} IORING_SQE_FLAGS; +// Specifies kernel behavior options for I/O ring submission queue entries + +// IORING_REGISTERED_BUFFER structure +typedef struct IORING_REGISTERED_BUFFER { + UINT32 Index; + UINT32 Offset; +} IORING_REGISTERED_BUFFER; + +// IORING_HANDLE_REF +struct IORING_HANDLE_REF { + IORING_REF_KIND Kind; + union { + HANDLE Handle; + UINT32 Index; + } HandleUnion; +}; +// Represents a reference to a file handle used in an I/O ring operation + +// IORING_BUFFER_REF +struct IORING_BUFFER_REF { + IORING_REF_KIND Kind; + union { + void *Address; + IORING_REGISTERED_BUFFER IndexAndOffset; + } BufferUnion; +}; + +typedef struct IORING_BUFFER_INFO { + void *Address; + UINT32 Length; +} IORING_BUFFER_INFO; + +// IORING_BUFFER_REF represents a reference to a buffer used in an I/O ring +// operation + +// IORING_VERSION enumeration +typedef enum IORING_VERSION { + IORING_VERSION_INVALID = 0, + IORING_VERSION_1 = 1, + IORING_VERSION_2 = 2, + IORING_VERSION_3 = 3, + IORING_VERSION_4 = 4, +} IORING_VERSION; + +typedef enum IORING_FEATURE_FLAGS { + IORING_FEATURE_FLAGS_NONE = 0, + IORING_FEATURE_UM_EMULATION = 1 +} IORING_FEATURE_FLAGS; + +// IORING_CAPABILITIES structure +typedef struct IORING_CAPABILITIES { + IORING_VERSION MaxVersion; + UINT32 MaxSubmissionQueueSize; + UINT32 MaxCompletionQueueSize; + IORING_FEATURE_FLAGS FeatureFlags; +} IORING_CAPABILITIES; +// Represents the IORING API capabilities. + +// IORING_CQE structure +typedef struct IORING_CQE { + UINT_PTR UserData; + HRESULT ResultCode; + ULONG_PTR Information; +} IORING_CQE; +// Represents a completed I/O ring queue entry. + +// IORING_CREATE_FLAGS structure +typedef struct IORING_CREATE_FLAGS { + IORING_CREATE_REQUIRED_FLAGS Required; + IORING_CREATE_ADVISORY_FLAGS Advisory; +} IORING_CREATE_FLAGS; +// Specifies flags for creating an I/O ring with a call to CreateIoRing. + +// IORING_INFO structure +typedef struct IORING_INFO { + IORING_VERSION IoRingVersion; + IORING_CREATE_FLAGS Flags; + UINT32 SubmissionQueueSize; + UINT32 CompletionQueueSize; +} IORING_INFO; +// Represents the shape and version information for the specified I/O ring + +// IORING_OP_CODE for IsIoRingOpSupported +typedef enum IORING_OP_CODE { + IORING_OP_NOP = 0, + IORING_OP_READ = 1, + IORING_OP_WRITE = 2, + IORING_OP_FLUSH = 3, + IORING_OP_REGISTER_BUFFERS = 4, + IORING_OP_REGISTER_FILES = 5, + IORING_OP_CANCEL = 6, +} IORING_OP_CODE; + +/* --------------------- Dynamic loader --------------------- */ +// Function pointer types +typedef BOOL(WINAPI *IsIoRingOpSupported_t)(HIORING, IORING_OP_CODE); +typedef HRESULT(WINAPI *QueryIoRingCapabilities_t)(IORING_CAPABILITIES *); +typedef HRESULT(WINAPI *GetIoRingInfo_t)(HIORING, IORING_INFO *); +typedef HRESULT(WINAPI *CreateIoRing_t)(IORING_VERSION, IORING_CREATE_FLAGS, + UINT32, UINT32, HIORING *); +typedef HRESULT(WINAPI *CloseIoRing_t)(HIORING); +typedef HRESULT(WINAPI *SubmitIoRing_t)(HIORING, UINT32, UINT32, UINT32 *); +typedef HRESULT(WINAPI *PopIoRingCompletion_t)(HIORING, IORING_CQE *); +typedef HRESULT(WINAPI *SetIoRingCompletionEvent_t)(HIORING, HANDLE); +typedef HRESULT(WINAPI *BuildIoRingCancelRequest_t)(HIORING, IORING_HANDLE_REF, + UINT_PTR, UINT_PTR); +typedef HRESULT(WINAPI *BuildIoRingReadFile_t)(HIORING, IORING_HANDLE_REF, + IORING_BUFFER_REF, UINT32, + UINT64, UINT_PTR, + IORING_SQE_FLAGS); +typedef HRESULT(WINAPI *BuildIoRingRegisterBuffers_t)( + HIORING, UINT32, IORING_BUFFER_INFO const[], UINT_PTR); + +typedef HRESULT(WINAPI *BuildIoRingRegisterFileHandles_t)(HIORING, UINT32, + HANDLE const[], + UINT_PTR); + +// Core: +// Queries the support of the specified operation for the specified I/O ring +static IsIoRingOpSupported_t IsIoRingOpSupported = NULL; + +// Queries the OS for the supported capabilities for IORINGs +static QueryIoRingCapabilities_t QueryIoRingCapabilities = NULL; + +// Gets information about the API version and queue sizes of an I/O ring +static GetIoRingInfo_t GetIoRingInfo = NULL; + +// Creates a new instance of an I/O ring submission/completion queue pair and +// returns a handle for referencing the I/O ring +static CreateIoRing_t CreateIoRing = NULL; + +// Closes an HIORING handle that was previously opened with a call to +// CreateIoRing +static CloseIoRing_t CloseIoRing = NULL; + +// Submission / completion: +// Submits all constructed but not yet submitted entries to the kernel’s queue +// and optionally waits for a set of operations to complete +static SubmitIoRing_t SubmitIoRing = NULL; + +// Pops a single entry from the completion queue, if one is available +static PopIoRingCompletion_t PopIoRingCompletion = NULL; + +// Registers a completion queue event with an IORING +static SetIoRingCompletionEvent_t SetIoRingCompletionEvent = NULL; + +// Operations: +// Performs an asynchronous read from a file using an I/O ring +static BuildIoRingReadFile_t BuildIoRingReadFile = NULL; + +// Attempts to cancel a previously submitted I/O ring operation +static BuildIoRingCancelRequest_t BuildIoRingCancelRequest = NULL; + +// Registers an array of buffers with the system for future I/O ring operations +static BuildIoRingRegisterBuffers_t BuildIoRingRegisterBuffers = NULL; + +// Registers an array of file handles with the system for future I/O ring +// operations +static BuildIoRingRegisterFileHandles_t BuildIoRingRegisterFileHandles = NULL; + +static int io_ring_loaded = 0; + +static int io_ring_load_functions(void) { + if (io_ring_loaded) + return 1; + + HMODULE hKernel = GetModuleHandleW(L"kernel32.dll"); + if (!hKernel) + return 0; + + IsIoRingOpSupported = + (IsIoRingOpSupported_t)GetProcAddress(hKernel, "IsIoRingOpSupported"); + QueryIoRingCapabilities = (QueryIoRingCapabilities_t)GetProcAddress( + hKernel, "QueryIoRingCapabilities"); + GetIoRingInfo = (GetIoRingInfo_t)GetProcAddress(hKernel, "GetIoRingInfo"); + CreateIoRing = (CreateIoRing_t)GetProcAddress(hKernel, "CreateIoRing"); + CloseIoRing = (CloseIoRing_t)GetProcAddress(hKernel, "CloseIoRing"); + SubmitIoRing = (SubmitIoRing_t)GetProcAddress(hKernel, "SubmitIoRing"); + PopIoRingCompletion = + (PopIoRingCompletion_t)GetProcAddress(hKernel, "PopIoRingCompletion"); + SetIoRingCompletionEvent = (SetIoRingCompletionEvent_t)GetProcAddress( + hKernel, "SetIoRingCompletionEvent"); + BuildIoRingReadFile = + (BuildIoRingReadFile_t)GetProcAddress(hKernel, "BuildIoRingReadFile"); + BuildIoRingCancelRequest = (BuildIoRingCancelRequest_t)GetProcAddress( + hKernel, "BuildIoRingCancelRequest"); + BuildIoRingRegisterBuffers = (BuildIoRingRegisterBuffers_t)GetProcAddress( + hKernel, "BuildIoRingRegisterBuffers"); + BuildIoRingRegisterFileHandles = + (BuildIoRingRegisterFileHandles_t)GetProcAddress( + hKernel, "BuildIoRingRegisterFileHandles"); + + io_ring_loaded = + (IsIoRingOpSupported && QueryIoRingCapabilities && CreateIoRing && + CloseIoRing && SubmitIoRing && PopIoRingCompletion && + SetIoRingCompletionEvent && BuildIoRingReadFile && + BuildIoRingCancelRequest && BuildIoRingRegisterBuffers && + BuildIoRingRegisterFileHandles); + + if (io_ring_loaded) + printf("[I/O Ring] Functions loaded\n"); + else + printf("[I/O Ring] Some functions not available\n"); + + return io_ring_loaded; +} + +/* ------------- Standard helper functions definition ------------- */ +// Creates an instance of the IORING_BUFFER_REF structure with the provided +// buffer index and offset +static inline IORING_BUFFER_REF +IoRingBufferRefFromIndexAndOffset(UINT32 index, UINT32 offset) { + IORING_BUFFER_REF ref; + ref.Kind = IORING_REF_REGISTERED; + ref.BufferUnion.IndexAndOffset.Index = index; + ref.BufferUnion.IndexAndOffset.Offset = offset; + return ref; +} + +// Creates an instance of the IORING_BUFFER_REF structure from the provided +// pointer +static IORING_BUFFER_REF IoRingBufferRefFromPointer(void *addr) { + IORING_BUFFER_REF ref; + ref.Kind = IORING_REF_RAW; + ref.BufferUnion.Address = addr; + return ref; +} + +// Creates an instance of the IORING_HANDLE_REF structure from the provided file +// handle +static IORING_HANDLE_REF IoRingHandleRefFromHandle(HANDLE h) { + IORING_HANDLE_REF ref; + ref.Kind = IORING_REF_RAW; + ref.HandleUnion.Handle = h; + return ref; +} + +// Creates an instance of the IORING_HANDLE_REF structure from the provided +// index +static inline IORING_HANDLE_REF IoRingHandleRefFromIndex(UINT32 index) { + IORING_HANDLE_REF ref; + ref.Kind = IORING_REF_REGISTERED; // MUST be registered + ref.HandleUnion.Index = index; + return ref; +} + +// NOTE: If you are using index-based buffers or handles, make sure you have +// successfully called BuildIoRingRegisterBuffers or +// BuildIoRingRegisterFileHandles first so the kernel has a valid table to look +// into, otherwise the kernel will treat the index as an invalid memory +// address/handle. diff --git a/platform.c b/platform.c index 162e985..dd92946 100644 --- a/platform.c +++ b/platform.c @@ -1,7 +1,5 @@ #pragma once // ensure that a given header file is included only once in a // single compilation unit -#define _CRT_SECURE_NO_WARNINGS - #include "arena.h" #include "base.h" #include "lf_mpmc.h" @@ -9,7 +7,7 @@ #include "arena.c" // xxhash include -#define XXH_INLINE_ALL +#define XXH_STATIC_LINKING_ONLY #include "xxh_x86dispatch.h" // ----------------------------- Config ------------------------------------- @@ -584,8 +582,6 @@ void scan_folder(const char *base, ScannerContext *ctx) { } #elif defined(__linux__) -To test -Choice 1 static int platform_get_file_times_fd(int dir_fd, const char *name, time_t *created, time_t *modified) { struct stat st; @@ -604,9 +600,9 @@ static int platform_get_file_owner_fd(int dir_fd, const char *name, char *owner, struct passwd pw; struct passwd *result; char buffer[4096]; // Sufficiently large buffer for passwd data - + // Reentrant version (thread-safe) - if (getpwuid_r(st.st_uid, &pw, buffer, sizeof(buffer), &result) == 0 && + if (getpwuid_r(st.st_uid, &pw, buffer, sizeof(buffer), &result) == 0 && result != NULL && result->pw_name != NULL) { strncpy(owner, result->pw_name, owner_size - 1); owner[owner_size - 1] = '\0'; @@ -618,170 +614,111 @@ static int platform_get_file_owner_fd(int dir_fd, const char *name, char *owner, } return -1; + void scan_folder(const char *base, ScannerContext *ctx) { + PathBuilder pb; + path_builder_init(&pb, base); -void scan_folder(const char *base, ScannerContext *ctx) { - PathBuilder pb; - path_builder_init(&pb, base); + int dir_fd = open(base, O_RDONLY | O_DIRECTORY | O_NOFOLLOW); + if (dir_fd == -1) + return; - int dir_fd = open(base, O_RDONLY | O_DIRECTORY | O_NOFOLLOW); - if (dir_fd == -1) - return; + DIR *dir = fdopendir(dir_fd); + if (!dir) { + close(dir_fd); + return; + } - DIR *dir = fdopendir(dir_fd); - if (!dir) { - close(dir_fd); - return; - } + struct dirent *entry; - struct dirent *entry; + while ((entry = readdir(dir)) != NULL) { + if (entry->d_name[0] == '.' && + (entry->d_name[1] == 0 || + (entry->d_name[1] == '.' && entry->d_name[2] == 0))) + continue; - while ((entry = readdir(dir)) != NULL) { - if (entry->d_name[0] == '.' && - (entry->d_name[1] == 0 || - (entry->d_name[1] == '.' && entry->d_name[2] == 0))) - continue; + size_t name_len = strlen(entry->d_name); + path_builder_set_filename(&pb, entry->d_name, name_len); - size_t name_len = strlen(entry->d_name); - path_builder_set_filename(&pb, entry->d_name, name_len); - - int file_type = DT_UNKNOWN; + int file_type = DT_UNKNOWN; #ifdef _DIRENT_HAVE_D_TYPE - file_type = entry->d_type; + file_type = entry->d_type; #endif - // Fast path using d_type - if (file_type != DT_UNKNOWN) { - if (file_type == DT_LNK) - continue; // Skip symlinks + // Fast path using d_type + if (file_type != DT_UNKNOWN) { + if (file_type == DT_LNK) + continue; // Skip symlinks - if (file_type == DT_DIR) { - char *dir_path = path_builder_dup_arena(&pb, ctx->path_arena, false); - mpmc_push_work(ctx->dir_queue, dir_path); - continue; + if (file_type == DT_DIR) { + char *dir_path = path_builder_dup_arena(&pb, ctx->path_arena, false); + mpmc_push_work(ctx->dir_queue, dir_path); + continue; + } + + if (file_type == DT_REG) { + atomic_fetch_add(&g_files_found, 1); + FileEntry *fe = arena_push(&ctx->meta_arena, sizeof(FileEntry), true); + + // Use fstatat for file info + struct stat st; + if (fstatat(dir_fd, entry->d_name, &st, 0) == 0) { + // Convert times using fd variant + platform_get_file_times_fd(dir_fd, entry->d_name, &fe->created_time, + &fe->modified_time); + platform_get_file_owner_fd(dir_fd, entry->d_name, fe->owner, + sizeof(fe->owner)); + fe->size_bytes = (uint64_t)st.st_size; + + // Normalize path + char temp_path[MAX_PATHLEN]; + memcpy(temp_path, pb.buffer, + (pb.filename_pos - pb.buffer) + name_len + 1); + normalize_path(temp_path); + + fe->path = + arena_push(&ctx->path_arena, strlen(temp_path) + 1, false); + strcpy(fe->path, temp_path); + + mpmc_push(ctx->file_queue, fe); + } + continue; + } } - if (file_type == DT_REG) { - atomic_fetch_add(&g_files_found, 1); - FileEntry *fe = arena_push(&ctx->meta_arena, sizeof(FileEntry), - true); + // Fallback for unknown types + struct stat st; + if (fstatat(dir_fd, entry->d_name, &st, AT_SYMLINK_NOFOLLOW) == 0) { + if (S_ISLNK(st.st_mode)) + continue; - // Use fstatat for file info - struct stat st; - if (fstatat(dir_fd, entry->d_name, &st, 0) == 0) { - // Convert times using fd variant - platform_get_file_times_fd(dir_fd, entry->d_name, - &fe->created_time, - &fe->modified_time); - platform_get_file_owner_fd(dir_fd, entry->d_name, fe->owner, - sizeof(fe->owner)); + if (S_ISDIR(st.st_mode)) { + char *dir_path = path_builder_dup_arena(&pb, ctx->path_arena, false); + mpmc_push_work(ctx->dir_queue, dir_path); + } else if (S_ISREG(st.st_mode)) { + atomic_fetch_add(&g_files_found, 1); + FileEntry *fe = arena_push(&ctx->meta_arena, sizeof(FileEntry), true); + + platform_get_file_times(pb.buffer, &fe->created_time, + &fe->modified_time); + platform_get_file_owner(pb.buffer, fe->owner, sizeof(fe->owner)); fe->size_bytes = (uint64_t)st.st_size; - // Normalize path char temp_path[MAX_PATHLEN]; memcpy(temp_path, pb.buffer, (pb.filename_pos - pb.buffer) + name_len + 1); normalize_path(temp_path); - fe->path = arena_push(&ctx->path_arena, strlen(temp_path) + 1, - false); strcpy(fe->path, temp_path); + fe->path = arena_push(&ctx->path_arena, strlen(temp_path) + 1, false); + strcpy(fe->path, temp_path); mpmc_push(ctx->file_queue, fe); } - continue; } } - // Fallback for unknown types - struct stat st; - if (fstatat(dir_fd, entry->d_name, &st, AT_SYMLINK_NOFOLLOW) == 0) { - if (S_ISLNK(st.st_mode)) - continue; - - if (S_ISDIR(st.st_mode)) { - char *dir_path = path_builder_dup_arena(&pb, ctx->path_arena, false); - mpmc_push_work(ctx->dir_queue, dir_path); - } else if (S_ISREG(st.st_mode)) { - atomic_fetch_add(&g_files_found, 1); - FileEntry *fe = arena_push(&ctx->meta_arena, sizeof(FileEntry), - true); - - platform_get_file_times(pb.buffer, &fe->created_time, - &fe->modified_time); - platform_get_file_owner(pb.buffer, fe->owner, sizeof(fe->owner)); - fe->size_bytes = (uint64_t)st.st_size; - - char temp_path[MAX_PATHLEN]; - memcpy(temp_path, pb.buffer, - (pb.filename_pos - pb.buffer) + name_len + 1); - normalize_path(temp_path); - - fe->path = arena_push(&ctx->path_arena, strlen(temp_path) + 1, - false); strcpy(fe->path, temp_path); - - mpmc_push(ctx->file_queue, fe); - } - } + closedir(dir); // Closes dir_fd automatically } - closedir(dir); // Closes dir_fd automatically -} - -// Choice 2 - -// void scan_folder(const char *base, ScannerContext *ctx) { -// PathBuilder pb; -// path_builder_init(&pb, base); -// -// DIR *dir = opendir(base); -// if (!dir) -// return; -// -// struct dirent *entry; -// struct stat st; -// -// while ((entry = readdir(dir)) != NULL) { -// if (entry->d_name[0] == '.' && -// (entry->d_name[1] == 0 || -// (entry->d_name[1] == '.' && entry->d_name[2] == 0))) -// continue; -// -// size_t name_len = strlen(entry->d_name); -// path_builder_set_filename(&pb, entry->d_name, name_len); -// -// if (lstat(pb.buffer, &st) == 0 && S_ISLNK(st.st_mode)) -// continue; -// -// if (stat(pb.buffer, &st) == 0) { -// if (S_ISDIR(st.st_mode)) { -// char *dir_path = path_builder_dup_arena(&pb, ctx->path_arena, false); -// mpmc_push_work(ctx->dir_queue, dir_path); -// } else { -// atomic_fetch_add(&g_files_found, 1); -// -// FileEntry *fe = arena_push(&ctx->meta_arena, sizeof(FileEntry), true); -// -// // Create a temporary copy for normalization -// char temp_path[MAX_PATHLEN]; -// memcpy(temp_path, pb.buffer, -// (pb.filename_pos - pb.buffer) + name_len + 1); -// normalize_path(temp_path); -// -// fe->path = arena_push(&ctx->path_arena, strlen(temp_path) + 1, false); -// strcpy(fe->path, temp_path); -// -// platform_get_file_times(pb.buffer, &fe->created_time, -// &fe->modified_time); -// platform_get_file_owner(pb.buffer, fe->owner, sizeof(fe->owner)); -// fe->size_bytes = (uint64_t)st.st_size; -// -// mpmc_push(ctx->file_queue, fe); -// } -// } -// } -// -// closedir(dir); -// } - #endif // ------------------------- Scan worker -------------------------------- @@ -864,7 +801,7 @@ static THREAD_RETURN hash_worker(void *arg) { return THREAD_RETURN_VALUE; } -// ----------------------------- Progress display --------------------------- +// ------------------------- Progress display --------------------------- static THREAD_RETURN progress_thread(void *arg) { (void)arg; // Unused parameter @@ -940,3 +877,472 @@ static THREAD_RETURN progress_thread(void *arg) { return THREAD_RETURN_VALUE; } + +// ======================== Hash worker IO Ring ======================== +// -------------------------- Configuration --------------------------- +#define IORING_READ_BLOCK (4096 * 64) +#define NUM_BUFFERS_PER_THREAD 16 +#define SUBMIT_TIMEOUT_MS 30000 +#define USERDATA_REGISTER 1 + +// Global stats +static atomic_uint_fast64_t g_io_ring_fallbacks = 0; + +// -------------------------- Buffer structure --------------------------- +typedef struct IoBuffer { + void *data; + uint64_t offset; + size_t size; + size_t bytes_read; + HRESULT result; + int buffer_id; + int completed; +} IoBuffer; + +// ============================================================================ +// Thread-local I/O Ring context +// ============================================================================ +typedef struct ThreadIoContext { + HIORING ring; + HANDLE completion_event; + IoBuffer buffers[NUM_BUFFERS_PER_THREAD]; + int buffer_pool[NUM_BUFFERS_PER_THREAD]; + int free_count; + int initialized; +} ThreadIoContext; + +// -------------------------- File context --------------------------- +typedef struct FileReadContext { + HANDLE hFile; + uint64_t file_size; + XXH3_state_t hash_state; + char path[MAX_PATH]; + + // Completion tracking + int reads_submitted; + int reads_completed; + int reads_hashed; + uint64_t bytes_hashed; + int failed_reads; + int active_reads; + int buffers_ready; // Count of buffers ready to hash + + // For in-order hashing + uint64_t next_hash_offset; // Next offset that needs to be hashed + uint64_t next_read_offset; // Next offset to submit for reading + +} FileReadContext; + +static _Thread_local ThreadIoContext *g_thread_ctx = NULL; + +// ---------------------- Initialize thread context --------------------------- +static ThreadIoContext *io_ring_init_thread(void) { + if (g_thread_ctx && g_thread_ctx->initialized) { + return g_thread_ctx; + } + + if (!g_thread_ctx) { + g_thread_ctx = (ThreadIoContext *)calloc(1, sizeof(ThreadIoContext)); + if (!g_thread_ctx) + return NULL; + } + + // Create I/O Ring + IORING_CAPABILITIES caps; + QueryIoRingCapabilities(&caps); + + UINT32 queue_size = min(4096, caps.MaxSubmissionQueueSize); + IORING_CREATE_FLAGS flags = {0}; + HRESULT hr = CreateIoRing(caps.MaxVersion, flags, queue_size, queue_size * 2, + &g_thread_ctx->ring); + + // Create completion event + g_thread_ctx->completion_event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (g_thread_ctx->completion_event) { + SetIoRingCompletionEvent(g_thread_ctx->ring, + g_thread_ctx->completion_event); + } + + // Initialize buffer pool + for (int i = 0; i < NUM_BUFFERS_PER_THREAD; i++) { + + // 4096 alignment + void *ptr = _aligned_malloc(IORING_READ_BLOCK, 4096); + if (!ptr) { + return NULL; + } + + g_thread_ctx->buffers[i].data = ptr; + + g_thread_ctx->buffer_pool[i] = i; + g_thread_ctx->buffers[i].buffer_id = i; + } + g_thread_ctx->free_count = NUM_BUFFERS_PER_THREAD; + + IORING_BUFFER_INFO buf_info[NUM_BUFFERS_PER_THREAD]; + + for (int i = 0; i < NUM_BUFFERS_PER_THREAD; i++) { + buf_info[i].Address = g_thread_ctx->buffers[i].data; + buf_info[i].Length = IORING_READ_BLOCK; + } + + HRESULT hb = BuildIoRingRegisterBuffers( + g_thread_ctx->ring, NUM_BUFFERS_PER_THREAD, buf_info, USERDATA_REGISTER); + + if (FAILED(hb)) { + printf("Buffer registration failed: 0x%lx\n", hb); + return NULL; + } + + // Submit registration + SubmitIoRing(g_thread_ctx->ring, 0, 0, NULL); + + g_thread_ctx->initialized = 1; + + return g_thread_ctx; +} + +static void io_ring_cleanup_thread(void) { + if (!g_thread_ctx) + return; + + if (g_thread_ctx->completion_event) + CloseHandle(g_thread_ctx->completion_event); + if (g_thread_ctx->ring) + CloseIoRing(g_thread_ctx->ring); + for (int i = 0; i < NUM_BUFFERS_PER_THREAD; i++) { + _aligned_free(g_thread_ctx->buffers[i].data); + } + free(g_thread_ctx); + g_thread_ctx = NULL; +} + +// ---------------------- Get a free buffer from pool ------------------------ +static IoBuffer *get_free_buffer(ThreadIoContext *ctx) { + + if (ctx->free_count == 0) { + return NULL; + } + + int idx = ctx->buffer_pool[--ctx->free_count]; + IoBuffer *buf = &ctx->buffers[idx]; + buf->completed = 0; + buf->bytes_read = 0; + buf->result = E_PENDING; + + return buf; +} + +static void return_buffer(ThreadIoContext *ctx, IoBuffer *buf) { + if (!buf) + return; + + ctx->buffer_pool[ctx->free_count++] = buf->buffer_id; +} + +// -------------------------- Submit async read --------------------------- +static HRESULT submit_read(ThreadIoContext *ctx, FileReadContext *file_ctx, + IoBuffer *buf, uint64_t offset, size_t size) { + buf->offset = offset; + buf->size = size; + + IORING_HANDLE_REF file_ref = IoRingHandleRefFromHandle(file_ctx->hFile); + IORING_BUFFER_REF buffer_ref = + IoRingBufferRefFromIndexAndOffset(buf->buffer_id, 0); + + HRESULT hr = + BuildIoRingReadFile(ctx->ring, file_ref, buffer_ref, (UINT32)size, offset, + (UINT_PTR)buf, IOSQE_FLAGS_NONE); + + if (SUCCEEDED(hr)) { + file_ctx->active_reads++; + file_ctx->reads_submitted++; + } else { + buf->completed = 1; + return_buffer(ctx, buf); + } + return hr; +} + +// -------------------------- Process completions --------------------------- +static int process_completions(ThreadIoContext *ctx, + FileReadContext *file_ctx) { + IORING_CQE cqe; + int processed = 0; + + while (PopIoRingCompletion(ctx->ring, &cqe) == S_OK) { + + if (cqe.UserData == USERDATA_REGISTER || cqe.UserData == 0) + continue; + + IoBuffer *buf = (IoBuffer *)cqe.UserData; + + if (buf && !buf->completed) { + buf->result = cqe.ResultCode; + buf->bytes_read = (DWORD)cqe.Information; + buf->completed = 1; + + if (SUCCEEDED(cqe.ResultCode) && cqe.Information > 0) { + file_ctx->active_reads--; + file_ctx->reads_completed++; + file_ctx->buffers_ready++; + } else { + file_ctx->failed_reads++; + file_ctx->active_reads--; + file_ctx->reads_completed++; + } + + processed++; + } + } + + return processed; +} + +// -------------------- Hash buffer in sequential order ----------------------- +static int hash_sequential_buffers(ThreadIoContext *ctx, + FileReadContext *file_ctx) { + int hashed = 0; + + // Keep hashing while the next buffer in sequence is ready + while (file_ctx->next_hash_offset < file_ctx->file_size) { + // Find the buffer that contains next_hash_offset + IoBuffer *found_buf = NULL; + + for (int i = 0; i < NUM_BUFFERS_PER_THREAD; i++) { + IoBuffer *buf = &ctx->buffers[i]; + if (buf->completed && buf->offset == file_ctx->next_hash_offset) { + found_buf = buf; + break; + } + } + + if (!found_buf) + break; // Buffer not ready yet + + // Found the correct buffer in order - hash it + if (SUCCEEDED(found_buf->result) && found_buf->bytes_read > 0) { + XXH3_128bits_update(&file_ctx->hash_state, found_buf->data, + found_buf->bytes_read); + atomic_fetch_add(&g_bytes_processed, found_buf->bytes_read); + + // Update bytes_hashed for this file! + file_ctx->bytes_hashed += found_buf->bytes_read; + file_ctx->reads_hashed++; + file_ctx->buffers_ready--; + + // Mark as processed and return buffer to pool + found_buf->completed = 0; + return_buffer(ctx, found_buf); + + // Move to next offset + file_ctx->next_hash_offset += found_buf->size; + hashed++; + } else if (found_buf->bytes_read == 0 && SUCCEEDED(found_buf->result)) { + // Read operation failed with an error code + file_ctx->reads_hashed++; + file_ctx->buffers_ready--; + found_buf->completed = 0; + return_buffer(ctx, found_buf); + file_ctx->next_hash_offset += found_buf->size; + hashed++; + } else { + // Read failed + file_ctx->failed_reads++; + file_ctx->reads_hashed++; + file_ctx->buffers_ready--; + found_buf->completed = 0; + return_buffer(ctx, found_buf); + file_ctx->next_hash_offset += found_buf->size; + hashed++; + } + } + + return hashed; +} + +// ------------- Submit pending reads - fill all free buffers ----------------- +static int submit_pending_reads(ThreadIoContext *ctx, + FileReadContext *file_ctx) { + int submitted = 0; + + while (1) { + // Check if we have more data to read + uint64_t current_offset = file_ctx->next_read_offset; + int has_data = (current_offset < file_ctx->file_size); + + if (!has_data) + break; + + // Get a free buffer + IoBuffer *buf = get_free_buffer(ctx); + if (!buf) + break; + + size_t remaining = file_ctx->file_size - current_offset; + + size_t bytes_to_read; + + if (remaining >= IORING_READ_BLOCK) { + bytes_to_read = IORING_READ_BLOCK; + } else { + // Round UP to sector size (4096) + bytes_to_read = (remaining + 4095) & ~4095; + } + + HRESULT hr = submit_read(ctx, file_ctx, buf, current_offset, bytes_to_read); + + if (SUCCEEDED(hr)) { + submitted++; + + file_ctx->next_read_offset += bytes_to_read; + } else { + return_buffer(ctx, buf); + break; + } + } + + return submitted; +} + +// -------------------------- Wait for completions --------------------------- +static void wait_for_completions(ThreadIoContext *ctx, + FileReadContext *file_ctx) { + int has_active = (file_ctx->active_reads > 0); + + if (has_active && ctx->completion_event) { + WaitForSingleObject(ctx->completion_event, SUBMIT_TIMEOUT_MS); + } +} + +// ---------------------- Main parallel hashing function ---------------------- +static void xxh3_hash_file_parallel(ThreadIoContext *ctx, const char *path, + char *out_hex, unsigned char *temp_buffer) { + + // Validate I/O Ring + if (!ctx || !ctx->ring) { + xxh3_hash_file_stream(path, out_hex, temp_buffer); + return; + } + + HANDLE hFile = CreateFileA( + path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, NULL); + + if (hFile == INVALID_HANDLE_VALUE) { + xxh3_hash_file_stream(path, out_hex, temp_buffer); + return; + } + + LARGE_INTEGER file_size; + if (!GetFileSizeEx(hFile, &file_size)) { + xxh3_hash_file_stream(path, out_hex, temp_buffer); + CloseHandle(hFile); + return; + } + + FileReadContext file_ctx; + memset(&file_ctx, 0, sizeof(file_ctx)); + file_ctx.hFile = hFile; + file_ctx.file_size = file_size.QuadPart; + file_ctx.next_hash_offset = 0; + file_ctx.next_read_offset = 0; + strncpy(file_ctx.path, path, MAX_PATH - 1); + file_ctx.path[MAX_PATH - 1] = 0; + XXH3_128bits_reset(&file_ctx.hash_state); + + // Submit initial reads + submit_pending_reads(ctx, &file_ctx); + + if (file_ctx.reads_submitted > 0) { + UINT32 submitted = 0; + SubmitIoRing(ctx->ring, 0, 0, &submitted); + } + + // Main loop + while (file_ctx.reads_hashed < file_ctx.reads_submitted) { + // Process completions + process_completions(ctx, &file_ctx); + + // Hash buffers in sequential order (critical!) + hash_sequential_buffers(ctx, &file_ctx); + + // Submit more reads if needed + if (file_ctx.active_reads < NUM_BUFFERS_PER_THREAD && + file_ctx.next_read_offset < file_ctx.file_size) { + int new_reads = submit_pending_reads(ctx, &file_ctx); + if (new_reads > 0) { + UINT32 submitted = 0; + SubmitIoRing(ctx->ring, 0, 0, &submitted); + } + } + + // Wait if nothing to hash and active reads exist + if (file_ctx.active_reads > 0 && file_ctx.buffers_ready == 0) { + wait_for_completions(ctx, &file_ctx); + } + } + + // Final verification + if (file_ctx.bytes_hashed == file_ctx.file_size && + file_ctx.failed_reads == 0) { + XXH128_hash_t h = XXH3_128bits_digest(&file_ctx.hash_state); + snprintf(out_hex, HASH_STRLEN, "%016llx%016llx", + (unsigned long long)h.high64, (unsigned long long)h.low64); + } else { + if (file_ctx.bytes_hashed != file_ctx.file_size) { + atomic_fetch_add(&g_io_ring_fallbacks, 1); + } + xxh3_hash_file_stream(path, out_hex, temp_buffer); + } + + CloseHandle(hFile); +} + +// -------------------------- Hash worker I/O Ring --------------------------- +static THREAD_RETURN hash_worker_io_ring(void *arg) { + WorkerContext *ctx = (WorkerContext *)arg; + unsigned char *temp_buffer = (unsigned char *)malloc(IORING_READ_BLOCK); + char hash[HASH_STRLEN]; + + if (!temp_buffer) + return THREAD_RETURN_VALUE; + + // Initialize I/O Ring for this thread + ThreadIoContext *ring_ctx = io_ring_init_thread(); + if (!ring_ctx || !ring_ctx->ring) { + printf("Thread %lu: I/O Ring unavailable, using buffered I/O\n", + GetCurrentThreadId()); + free(temp_buffer); + return hash_worker(arg); + } + + for (;;) { + FileEntry *fe = mpmc_pop(ctx->file_queue); + if (!fe) + break; + + // Pass the I/O Ring context to the hashing function + xxh3_hash_file_parallel(ring_ctx, fe->path, hash, temp_buffer); + + char created[32], modified[32]; + format_time(fe->created_time, created, sizeof(created)); + format_time(fe->modified_time, modified, sizeof(modified)); + + double size_kib = (double)fe->size_bytes / 1024.0; + + char stack_buf[1024]; + int len = + snprintf(stack_buf, sizeof(stack_buf), "%s\t%s\t%.2f\t%s\t%s\t%s\n", + hash, fe->path, size_kib, created, modified, fe->owner); + + char *dst = arena_push(&ctx->arena, len, false); + memcpy(dst, stack_buf, len); + atomic_fetch_add(&g_files_hashed, 1); + } + + io_ring_cleanup_thread(); + free(temp_buffer); + + return THREAD_RETURN_VALUE; +} diff --git a/xxh_x86dispatch.c b/xxh_x86dispatch.c index 0c15820..ddb71e1 100644 --- a/xxh_x86dispatch.c +++ b/xxh_x86dispatch.c @@ -1,3 +1,5 @@ +#define XXH_INLINE_ALL + /* * xxHash - Extremely Fast Hash algorithm * Copyright (C) 2020-2021 Yann Collet