summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/io/windows
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/SDL-3.2.8/src/io/windows')
-rw-r--r--contrib/SDL-3.2.8/src/io/windows/SDL_asyncio_windows_ioring.c550
1 files changed, 550 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/io/windows/SDL_asyncio_windows_ioring.c b/contrib/SDL-3.2.8/src/io/windows/SDL_asyncio_windows_ioring.c
new file mode 100644
index 0000000..2d38efc
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/io/windows/SDL_asyncio_windows_ioring.c
@@ -0,0 +1,550 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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 CreateAsyncIOQueue = SDL_SYS_CreateAsyncIOQueue_ioring;
515 QuitAsyncIO = SDL_SYS_QuitAsyncIO_ioring;
516 AsyncIOFromFile = SDL_SYS_AsyncIOFromFile_ioring;
517 } else { // can't use ioring? Use the "generic" threadpool implementation instead.
518 CreateAsyncIOQueue = SDL_SYS_CreateAsyncIOQueue_Generic;
519 QuitAsyncIO = SDL_SYS_QuitAsyncIO_Generic;
520 AsyncIOFromFile = SDL_SYS_AsyncIOFromFile_Generic;
521 }
522 SDL_SetInitialized(&ioring_init, true);
523 }
524}
525
526bool SDL_SYS_CreateAsyncIOQueue(SDL_AsyncIOQueue *queue)
527{
528 MaybeInitializeWinIoRing();
529 return CreateAsyncIOQueue(queue);
530}
531
532bool SDL_SYS_AsyncIOFromFile(const char *file, const char *mode, SDL_AsyncIO *asyncio)
533{
534 MaybeInitializeWinIoRing();
535 return AsyncIOFromFile(file, mode, asyncio);
536}
537
538void SDL_SYS_QuitAsyncIO(void)
539{
540 if (SDL_ShouldQuit(&ioring_init)) {
541 QuitAsyncIO();
542 CreateAsyncIOQueue = NULL;
543 QuitAsyncIO = NULL;
544 AsyncIOFromFile = NULL;
545 SDL_SetInitialized(&ioring_init, false);
546 }
547}
548
549#endif // defined HAVE_IORINGAPI_H
550