Atlas - SDL_asyncio_windows_ioring.c

Home / ext / SDL / src / io / windows Lines: 1 | Size: 20948 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21 22// The Windows backend uses IoRing for asynchronous i/o, and falls back to 23// the "generic" threadpool implementation if it isn't available or 24// fails for some other reason. IoRing was introduced in Windows 11. 25 26#include "SDL_internal.h" 27#include "../SDL_sysasyncio.h" 28 29#ifdef HAVE_IORINGAPI_H 30 31#include "../../core/windows/SDL_windows.h" 32#include <ioringapi.h> 33 34// Don't know what the lowest usable version is, but this seems safe. 35#define SDL_REQUIRED_IORING_VERSION IORING_VERSION_3 36 37static SDL_InitState ioring_init; 38 39// We could add a whole bootstrap thing like the audio/video/etc subsystems use, but let's keep this simple for now. 40static bool (*CreateAsyncIOQueue)(SDL_AsyncIOQueue *queue); 41static void (*QuitAsyncIO)(void); 42static bool (*AsyncIOFromFile)(const char *file, const char *mode, SDL_AsyncIO *asyncio); 43 44// we never link directly to ioring. 45static const char *ioring_library = "KernelBase.dll"; 46static void *ioring_handle = NULL; 47 48#define SDL_IORING_FUNCS \ 49 SDL_IORING_FUNC(HRESULT, QueryIoRingCapabilities, (IORING_CAPABILITIES *capabilities)) \ 50 SDL_IORING_FUNC(BOOL, IsIoRingOpSupported, (HIORING ioRing, IORING_OP_CODE op)) \ 51 SDL_IORING_FUNC(HRESULT, CreateIoRing, (IORING_VERSION ioringVersion, IORING_CREATE_FLAGS flags, UINT32 submissionQueueSize, UINT32 completionQueueSize, HIORING* h)) \ 52 SDL_IORING_FUNC(HRESULT, GetIoRingInfo, (HIORING ioRing, IORING_INFO* info)) \ 53 SDL_IORING_FUNC(HRESULT, SubmitIoRing, (HIORING ioRing, UINT32 waitOperations, UINT32 milliseconds, UINT32 * submittedEntries)) \ 54 SDL_IORING_FUNC(HRESULT, CloseIoRing, (HIORING ioRing)) \ 55 SDL_IORING_FUNC(HRESULT, PopIoRingCompletion, (HIORING ioRing, IORING_CQE* cqe)) \ 56 SDL_IORING_FUNC(HRESULT, SetIoRingCompletionEvent, (HIORING ioRing, HANDLE hEvent)) \ 57 SDL_IORING_FUNC(HRESULT, BuildIoRingCancelRequest, (HIORING ioRing, IORING_HANDLE_REF file, UINT_PTR opToCancel, UINT_PTR userData)) \ 58 SDL_IORING_FUNC(HRESULT, BuildIoRingReadFile, (HIORING ioRing, IORING_HANDLE_REF fileRef, IORING_BUFFER_REF dataRef, UINT32 numberOfBytesToRead, UINT64 fileOffset, UINT_PTR userData, IORING_SQE_FLAGS sqeFlags)) \ 59 SDL_IORING_FUNC(HRESULT, BuildIoRingWriteFile, (HIORING ioRing, IORING_HANDLE_REF fileRef, IORING_BUFFER_REF bufferRef, UINT32 numberOfBytesToWrite, UINT64 fileOffset, FILE_WRITE_FLAGS writeFlags, UINT_PTR userData, IORING_SQE_FLAGS sqeFlags)) \ 60 SDL_IORING_FUNC(HRESULT, BuildIoRingFlushFile, (HIORING ioRing, IORING_HANDLE_REF fileRef, FILE_FLUSH_MODE flushMode, UINT_PTR userData, IORING_SQE_FLAGS sqeFlags)) \ 61 62#define SDL_IORING_FUNC(ret, fn, args) typedef ret (WINAPI *SDL_fntype_##fn) args; 63SDL_IORING_FUNCS 64#undef SDL_IORING_FUNC 65 66typedef struct SDL_WinIoRingFunctions 67{ 68 #define SDL_IORING_FUNC(ret, fn, args) SDL_fntype_##fn fn; 69 SDL_IORING_FUNCS 70 #undef SDL_IORING_FUNC 71} SDL_WinIoRingFunctions; 72 73static SDL_WinIoRingFunctions ioring; 74 75 76typedef struct WinIoRingAsyncIOQueueData 77{ 78 SDL_Mutex *sqe_lock; 79 SDL_Mutex *cqe_lock; 80 HANDLE event; 81 HIORING ring; 82 SDL_AtomicInt num_waiting; 83} WinIoRingAsyncIOQueueData; 84 85 86static void UnloadWinIoRingLibrary(void) 87{ 88 if (ioring_library) { 89 SDL_UnloadObject(ioring_handle); 90 ioring_library = NULL; 91 } 92 SDL_zero(ioring); 93} 94 95static bool LoadWinIoRingSyms(void) 96{ 97 #define SDL_IORING_FUNC(ret, fn, args) { \ 98 ioring.fn = (SDL_fntype_##fn) SDL_LoadFunction(ioring_handle, #fn); \ 99 if (!ioring.fn) { \ 100 return false; \ 101 } \ 102 } 103 SDL_IORING_FUNCS 104 #undef SDL_IORING_FUNC 105 return true; 106} 107 108static bool LoadWinIoRing(void) 109{ 110 bool result = true; 111 112 if (!ioring_handle) { 113 ioring_handle = SDL_LoadObject(ioring_library); 114 if (!ioring_handle) { 115 result = false; 116 // Don't call SDL_SetError(): SDL_LoadObject already did. 117 } else { 118 result = LoadWinIoRingSyms(); 119 if (result) { 120 IORING_CAPABILITIES caps; 121 HRESULT hr = ioring.QueryIoRingCapabilities(&caps); 122 if (FAILED(hr)) { 123 result = false; 124 } else if (caps.MaxVersion < SDL_REQUIRED_IORING_VERSION) { 125 result = false; 126 } 127 } 128 129 if (!result) { 130 UnloadWinIoRingLibrary(); 131 } 132 } 133 } 134 return result; 135} 136 137static Sint64 ioring_asyncio_size(void *userdata) 138{ 139 HANDLE handle = (HANDLE) userdata; 140 LARGE_INTEGER size; 141 if (!GetFileSizeEx(handle, &size)) { 142 WIN_SetError("GetFileSizeEx"); 143 return -1; 144 } 145 return (Sint64) size.QuadPart; 146} 147 148// you must hold sqe_lock when calling this! 149static bool ioring_asyncioqueue_queue_task(void *userdata, SDL_AsyncIOTask *task) 150{ 151 WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *) userdata; 152 const HRESULT hr = ioring.SubmitIoRing(queuedata->ring, 0, 0, NULL); 153 return (FAILED(hr) ? WIN_SetErrorFromHRESULT("SubmitIoRing", hr) : true); 154} 155 156static void ioring_asyncioqueue_cancel_task(void *userdata, SDL_AsyncIOTask *task) 157{ 158 if (!task->asyncio || !task->asyncio->userdata) { 159 return; // Windows IoRing needs the file handle in question, so we'll just have to let it complete if unknown. 160 } 161 162 SDL_AsyncIOTask *cancel_task = (SDL_AsyncIOTask *) SDL_calloc(1, sizeof (*cancel_task)); 163 if (!cancel_task) { 164 return; // oh well, the task can just finish on its own. 165 } 166 167 WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *) userdata; 168 HANDLE handle = (HANDLE) task->asyncio->userdata; 169 IORING_HANDLE_REF href = IoRingHandleRefFromHandle(handle); 170 171 // have to hold a lock because otherwise two threads could get_sqe and submit while one request isn't fully set up. 172 SDL_LockMutex(queuedata->sqe_lock); 173 const HRESULT hr = ioring.BuildIoRingCancelRequest(queuedata->ring, href, (UINT_PTR) task, (UINT_PTR) cancel_task); 174 if (FAILED(hr)) { 175 SDL_UnlockMutex(queuedata->sqe_lock); 176 SDL_free(cancel_task); // oh well, the task can just finish on its own. 177 return; 178 } 179 180 cancel_task->app_userdata = task; 181 ioring_asyncioqueue_queue_task(userdata, task); 182 SDL_UnlockMutex(queuedata->sqe_lock); 183} 184 185static SDL_AsyncIOTask *ProcessCQE(WinIoRingAsyncIOQueueData *queuedata, IORING_CQE *cqe) 186{ 187 if (!cqe) { 188 return NULL; 189 } 190 191 SDL_AsyncIOTask *task = (SDL_AsyncIOTask *) cqe->UserData; 192 if (task) { // can be NULL if this was just a wakeup message, a NOP, etc. 193 if (!task->queue) { // We leave `queue` blank to signify this was a task cancellation. 194 SDL_AsyncIOTask *cancel_task = task; 195 task = (SDL_AsyncIOTask *) cancel_task->app_userdata; 196 SDL_free(cancel_task); 197 if (SUCCEEDED(cqe->ResultCode)) { // cancel was successful? 198 task->result = SDL_ASYNCIO_CANCELED; 199 } else { 200 task = NULL; // it already finished or was too far along to cancel, so we'll pick up the actual results later. 201 } 202 } else if (FAILED(cqe->ResultCode)) { 203 task->result = SDL_ASYNCIO_FAILURE; 204 // !!! FIXME: fill in task->error. 205 } else { 206 if ((task->type == SDL_ASYNCIO_TASK_WRITE) && (((Uint64) cqe->Information) < task->requested_size)) { 207 task->result = SDL_ASYNCIO_FAILURE; // it's always a failure on short writes. 208 } 209 210 // don't explicitly mark it as COMPLETE; that's the default value and a linked task might have failed in an earlier operation and this would overwrite it. 211 212 if ((task->type == SDL_ASYNCIO_TASK_READ) || (task->type == SDL_ASYNCIO_TASK_WRITE)) { 213 task->result_size = (Uint64) cqe->Information; 214 } 215 } 216 217 // we currently send all close operations through as flushes, requested or not, so the actually closing is (in theory) fast. We do that here. 218 // if a later IoRing interface version offers an asynchronous close operation, revisit this to only flush if requested, like we do in the Linux io_uring code. 219 if (task->type == SDL_ASYNCIO_TASK_CLOSE) { 220 SDL_assert(task->asyncio != NULL); 221 SDL_assert(task->asyncio->userdata != NULL); 222 HANDLE handle = (HANDLE) task->asyncio->userdata; 223 if (!CloseHandle(handle)) { 224 task->result = SDL_ASYNCIO_FAILURE; // shrug. 225 } 226 } 227 } 228 229 return task; 230} 231 232static SDL_AsyncIOTask *ioring_asyncioqueue_get_results(void *userdata) 233{ 234 WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *) userdata; 235 236 // unlike liburing's io_uring_peek_cqe(), it's possible PopIoRingCompletion() is thread safe, but for now we wrap it in a mutex just in case. 237 SDL_LockMutex(queuedata->cqe_lock); 238 IORING_CQE cqe; 239 const HRESULT hr = ioring.PopIoRingCompletion(queuedata->ring, &cqe); 240 SDL_UnlockMutex(queuedata->cqe_lock); 241 242 if ((hr == S_FALSE) || FAILED(hr)) { 243 return NULL; // nothing available at the moment. 244 } 245 246 return ProcessCQE(queuedata, &cqe); 247} 248 249static SDL_AsyncIOTask *ioring_asyncioqueue_wait_results(void *userdata, Sint32 timeoutMS) 250{ 251 WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *) userdata; 252 253 // the event only signals when the IoRing moves from empty to non-empty, so you have to try a (non-blocking) get_results first or risk eternal hangs. 254 SDL_AsyncIOTask *task = ioring_asyncioqueue_get_results(userdata); 255 if (!task) { 256 SDL_AddAtomicInt(&queuedata->num_waiting, 1); 257 WaitForSingleObject(queuedata->event, (timeoutMS < 0) ? INFINITE : (DWORD) timeoutMS); 258 SDL_AddAtomicInt(&queuedata->num_waiting, -1); 259 260 // (we don't care if the wait failed for any reason, as the upcoming get_results will report valid information. We just wanted the wait operation to block.) 261 task = ioring_asyncioqueue_get_results(userdata); 262 } 263 264 return task; 265} 266 267static void ioring_asyncioqueue_signal(void *userdata) 268{ 269 WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *) userdata; 270 const int num_waiting = SDL_GetAtomicInt(&queuedata->num_waiting); 271 for (int i = 0; i < num_waiting; i++) { 272 SetEvent(queuedata->event); 273 } 274} 275 276static void ioring_asyncioqueue_destroy(void *userdata) 277{ 278 WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *) userdata; 279 ioring.CloseIoRing(queuedata->ring); 280 CloseHandle(queuedata->event); 281 SDL_DestroyMutex(queuedata->sqe_lock); 282 SDL_DestroyMutex(queuedata->cqe_lock); 283 SDL_free(queuedata); 284} 285 286static bool SDL_SYS_CreateAsyncIOQueue_ioring(SDL_AsyncIOQueue *queue) 287{ 288 WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *) SDL_calloc(1, sizeof (*queuedata)); 289 if (!queuedata) { 290 return false; 291 } 292 293 HRESULT hr; 294 IORING_CREATE_FLAGS flags; 295 296 SDL_SetAtomicInt(&queuedata->num_waiting, 0); 297 298 queuedata->sqe_lock = SDL_CreateMutex(); 299 if (!queuedata->sqe_lock) { 300 goto failed; 301 } 302 303 queuedata->cqe_lock = SDL_CreateMutex(); 304 if (!queuedata->cqe_lock) { 305 goto failed; 306 } 307 308 queuedata->event = CreateEventW(NULL, FALSE, FALSE, NULL); 309 if (!queuedata->event) { 310 WIN_SetError("CreateEventW"); 311 goto failed; 312 } 313 314 // !!! FIXME: no idea how large the queue should be. Is 128 overkill or too small? 315 flags.Required = IORING_CREATE_REQUIRED_FLAGS_NONE; 316 flags.Advisory = IORING_CREATE_ADVISORY_FLAGS_NONE; 317 hr = ioring.CreateIoRing(SDL_REQUIRED_IORING_VERSION, flags, 128, 128, &queuedata->ring); 318 if (FAILED(hr)) { 319 WIN_SetErrorFromHRESULT("CreateIoRing", hr); 320 goto failed; 321 } 322 323 hr = ioring.SetIoRingCompletionEvent(queuedata->ring, queuedata->event); 324 if (FAILED(hr)) { 325 WIN_SetErrorFromHRESULT("SetIoRingCompletionEvent", hr); 326 goto failed; 327 } 328 329 static const IORING_OP_CODE needed_ops[] = { 330 IORING_OP_NOP, 331 IORING_OP_FLUSH, 332 IORING_OP_READ, 333 IORING_OP_WRITE, 334 IORING_OP_CANCEL 335 }; 336 337 for (int i = 0; i < SDL_arraysize(needed_ops); i++) { 338 if (!ioring.IsIoRingOpSupported(queuedata->ring, needed_ops[i])) { 339 SDL_SetError("Created IoRing doesn't support op %u", (unsigned int) needed_ops[i]); 340 goto failed; 341 } 342 } 343 344 static const SDL_AsyncIOQueueInterface SDL_AsyncIOQueue_ioring = { 345 ioring_asyncioqueue_queue_task, 346 ioring_asyncioqueue_cancel_task, 347 ioring_asyncioqueue_get_results, 348 ioring_asyncioqueue_wait_results, 349 ioring_asyncioqueue_signal, 350 ioring_asyncioqueue_destroy 351 }; 352 353 SDL_copyp(&queue->iface, &SDL_AsyncIOQueue_ioring); 354 queue->userdata = queuedata; 355 return true; 356 357failed: 358 if (queuedata->ring) { 359 ioring.CloseIoRing(queuedata->ring); 360 } 361 if (queuedata->event) { 362 CloseHandle(queuedata->event); 363 } 364 if (queuedata->sqe_lock) { 365 SDL_DestroyMutex(queuedata->sqe_lock); 366 } 367 if (queuedata->cqe_lock) { 368 SDL_DestroyMutex(queuedata->cqe_lock); 369 } 370 SDL_free(queuedata); 371 return false; 372} 373 374static bool ioring_asyncio_read(void *userdata, SDL_AsyncIOTask *task) 375{ 376 // !!! FIXME: UINT32 smaller than requested_size's Uint64. If we overflow it, we could try submitting multiple SQEs 377 // !!! FIXME: and make a note in the task that there are several in sequence. 378 if (task->requested_size > 0xFFFFFFFF) { 379 return SDL_SetError("ioring: i/o task is too large"); 380 } 381 382 HANDLE handle = (HANDLE) userdata; 383 WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *) task->queue->userdata; 384 IORING_HANDLE_REF href = IoRingHandleRefFromHandle(handle); 385 IORING_BUFFER_REF bref = IoRingBufferRefFromPointer(task->buffer); 386 387 // have to hold a lock because otherwise two threads could get_sqe and submit while one request isn't fully set up. 388 SDL_LockMutex(queuedata->sqe_lock); 389 bool retval; 390 const HRESULT hr = ioring.BuildIoRingReadFile(queuedata->ring, href, bref, (UINT32) task->requested_size, task->offset, (UINT_PTR) task, IOSQE_FLAGS_NONE); 391 if (FAILED(hr)) { 392 retval = WIN_SetErrorFromHRESULT("BuildIoRingReadFile", hr); 393 } else { 394 retval = task->queue->iface.queue_task(task->queue->userdata, task); 395 } 396 SDL_UnlockMutex(queuedata->sqe_lock); 397 return retval; 398} 399 400static bool ioring_asyncio_write(void *userdata, SDL_AsyncIOTask *task) 401{ 402 // !!! FIXME: UINT32 smaller than requested_size's Uint64. If we overflow it, we could try submitting multiple SQEs 403 // !!! FIXME: and make a note in the task that there are several in sequence. 404 if (task->requested_size > 0xFFFFFFFF) { 405 return SDL_SetError("ioring: i/o task is too large"); 406 } 407 408 HANDLE handle = (HANDLE) userdata; 409 WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *) task->queue->userdata; 410 IORING_HANDLE_REF href = IoRingHandleRefFromHandle(handle); 411 IORING_BUFFER_REF bref = IoRingBufferRefFromPointer(task->buffer); 412 413 // have to hold a lock because otherwise two threads could get_sqe and submit while one request isn't fully set up. 414 SDL_LockMutex(queuedata->sqe_lock); 415 bool retval; 416 const HRESULT hr = ioring.BuildIoRingWriteFile(queuedata->ring, href, bref, (UINT32) task->requested_size, task->offset, 0 /*FILE_WRITE_FLAGS_NONE*/, (UINT_PTR) task, IOSQE_FLAGS_NONE); 417 if (FAILED(hr)) { 418 retval = WIN_SetErrorFromHRESULT("BuildIoRingWriteFile", hr); 419 } else { 420 retval = task->queue->iface.queue_task(task->queue->userdata, task); 421 } 422 SDL_UnlockMutex(queuedata->sqe_lock); 423 return retval; 424} 425 426static bool ioring_asyncio_close(void *userdata, SDL_AsyncIOTask *task) 427{ 428 // current IoRing operations don't offer asynchronous closing, but let's assume most of the potential work is flushing to disk, so just do it for everything, explicit flush or not. We'll close when it finishes. 429 HANDLE handle = (HANDLE) userdata; 430 WinIoRingAsyncIOQueueData *queuedata = (WinIoRingAsyncIOQueueData *)task->queue->userdata; 431 IORING_HANDLE_REF href = IoRingHandleRefFromHandle(handle); 432 433 // have to hold a lock because otherwise two threads could get_sqe and submit while one request isn't fully set up. 434 SDL_LockMutex(queuedata->sqe_lock); 435 bool retval; 436 const HRESULT hr = ioring.BuildIoRingFlushFile(queuedata->ring, href, FILE_FLUSH_DEFAULT, (UINT_PTR) task, IOSQE_FLAGS_NONE); 437 if (FAILED(hr)) { 438 retval = WIN_SetErrorFromHRESULT("BuildIoRingFlushFile", hr); 439 } else { 440 retval = task->queue->iface.queue_task(task->queue->userdata, task); 441 } 442 SDL_UnlockMutex(queuedata->sqe_lock); 443 return retval; 444} 445 446static void ioring_asyncio_destroy(void *userdata) 447{ 448 // this is only a Win32 file HANDLE, should have been closed elsewhere. 449} 450 451static bool Win32OpenModeFromString(const char *mode, DWORD *access_mode, DWORD *create_mode) 452{ 453 // this is exactly the set of strings that SDL_AsyncIOFromFile promises will work. 454 static const struct { const char *str; DWORD amode; WORD cmode; } mappings[] = { 455 { "rb", GENERIC_READ, OPEN_EXISTING }, 456 { "wb", GENERIC_WRITE, CREATE_ALWAYS }, 457 { "r+b", GENERIC_READ | GENERIC_WRITE, OPEN_EXISTING }, 458 { "w+b", GENERIC_READ | GENERIC_WRITE, CREATE_ALWAYS } 459 }; 460 461 for (int i = 0; i < SDL_arraysize(mappings); i++) { 462 if (SDL_strcmp(mappings[i].str, mode) == 0) { 463 *access_mode = mappings[i].amode; 464 *create_mode = mappings[i].cmode; 465 return true; 466 } 467 } 468 469 SDL_assert(!"Shouldn't have reached this code"); 470 return SDL_SetError("Invalid file open mode"); 471} 472 473static bool SDL_SYS_AsyncIOFromFile_ioring(const char *file, const char *mode, SDL_AsyncIO *asyncio) 474{ 475 DWORD access_mode, create_mode; 476 if (!Win32OpenModeFromString(mode, &access_mode, &create_mode)) { 477 return false; 478 } 479 480 LPWSTR wstr = WIN_UTF8ToStringW(file); 481 if (!wstr) { 482 return false; 483 } 484 485 HANDLE handle = CreateFileW(wstr, access_mode, FILE_SHARE_READ, NULL, create_mode, FILE_ATTRIBUTE_NORMAL, NULL); 486 SDL_free(wstr); 487 if (!handle) { 488 return WIN_SetError("CreateFileW"); 489 } 490 491 static const SDL_AsyncIOInterface SDL_AsyncIOFile_ioring = { 492 ioring_asyncio_size, 493 ioring_asyncio_read, 494 ioring_asyncio_write, 495 ioring_asyncio_close, 496 ioring_asyncio_destroy 497 }; 498 499 SDL_copyp(&asyncio->iface, &SDL_AsyncIOFile_ioring); 500 501 asyncio->userdata = (void *) handle; 502 return true; 503} 504 505static void SDL_SYS_QuitAsyncIO_ioring(void) 506{ 507 UnloadWinIoRingLibrary(); 508} 509 510static void MaybeInitializeWinIoRing(void) 511{ 512 if (SDL_ShouldInit(&ioring_init)) { 513 if (LoadWinIoRing()) { 514 SDL_DebugLogBackend("asyncio", "ioring"); 515 CreateAsyncIOQueue = SDL_SYS_CreateAsyncIOQueue_ioring; 516 QuitAsyncIO = SDL_SYS_QuitAsyncIO_ioring; 517 AsyncIOFromFile = SDL_SYS_AsyncIOFromFile_ioring; 518 } else { // can't use ioring? Use the "generic" threadpool implementation instead. 519 SDL_DebugLogBackend("asyncio", "generic"); 520 CreateAsyncIOQueue = SDL_SYS_CreateAsyncIOQueue_Generic; 521 QuitAsyncIO = SDL_SYS_QuitAsyncIO_Generic; 522 AsyncIOFromFile = SDL_SYS_AsyncIOFromFile_Generic; 523 } 524 SDL_SetInitialized(&ioring_init, true); 525 } 526} 527 528bool SDL_SYS_CreateAsyncIOQueue(SDL_AsyncIOQueue *queue) 529{ 530 MaybeInitializeWinIoRing(); 531 return CreateAsyncIOQueue(queue); 532} 533 534bool SDL_SYS_AsyncIOFromFile(const char *file, const char *mode, SDL_AsyncIO *asyncio) 535{ 536 MaybeInitializeWinIoRing(); 537 return AsyncIOFromFile(file, mode, asyncio); 538} 539 540void SDL_SYS_QuitAsyncIO(void) 541{ 542 if (SDL_ShouldQuit(&ioring_init)) { 543 QuitAsyncIO(); 544 CreateAsyncIOQueue = NULL; 545 QuitAsyncIO = NULL; 546 AsyncIOFromFile = NULL; 547 SDL_SetInitialized(&ioring_init, false); 548 } 549} 550 551#endif // defined HAVE_IORINGAPI_H 552 553
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.