1 /*
2     DSDL
3     Copyright (C) 2025 Inochi2D Project <luna@foxgirls.gay>
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 
23     Simple DirectMedia Layer
24     Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
25 
26     This software is provided 'as-is', without any express or implied
27     warranty.  In no event will the authors be held liable for any damages
28     arising from the use of this software.
29 
30     Permission is granted to anyone to use this software for any purpose,
31     including commercial applications, and to alter it and redistribute it
32     freely, subject to the following restrictions:
33 
34     1. The origin of this software must not be misrepresented; you must not
35         claim that you wrote the original software. If you use this software
36         in a product, an acknowledgment in the product documentation would be
37         appreciated but is not required.
38     2. Altered source versions must be plainly marked as such, and must not be
39         misrepresented as being the original software.
40     3. This notice may not be removed or altered from any source distribution.
41 */
42 
43 /**
44     SDL3 Asynchronous I/O
45 
46     See_Also:
47         $(LINK2 https://wiki.libsdl.org/SDL3/CategoryAsyncIO, SDL3 AsyncIO Documentation)
48     
49     Copyright: © 2025 Inochi2D Project, © 1997-2025 Sam Lantinga
50     License: Subject to the terms of the Zlib License, as written in the LICENSE file.
51     Authors: 
52         Luna Nielsen
53 */
54 module sdl.asyncio;
55 import sdl.stdc;
56 
57 extern(C) nothrow @nogc:
58 
59 /**
60     The asynchronous I/O operation structure.
61 
62     This operates as an opaque handle. One can then request read or write
63     operations on it.
64 
65     See_Also:
66         $(D SDL_AsyncIOFromFile)
67 
68     History:
69         Available since SDL 3.2.0.
70 */
71 struct SDL_AsyncIO;
72 
73 /**
74     Types of asynchronous I/O tasks.
75 
76     History:
77         Available since SDL 3.2.0.
78 */
79 enum SDL_AsyncIOTaskType {
80     
81     /**
82         A read operation.
83     */
84     READ,
85     
86     /**
87         A write operation.
88     */
89     WRITE,
90     
91     /**
92         A close operation.
93     */
94     CLOSE
95 }
96 
97 /**
98     Possible outcomes of an asynchronous I/O task.
99 
100     History:
101         Available since SDL 3.2.0.
102 */
103 enum SDL_AsyncIOResult {
104 
105     /**
106         Request was completed without error.
107     */
108     COMPLETE,
109 
110     /**
111         Request failed for some reason; check SDL_GetError()!
112     */
113     FAILURE,
114 
115     /**
116         Request was canceled before completing.
117     */
118     CANCELED
119 }
120 
121 /**
122     Information about a completed asynchronous I/O request.
123     
124     History:
125         Available since SDL 3.2.0.
126 */
127 struct SDL_AsyncIOOutcome {
128     
129     /**
130         What generated this task.
131         
132         This pointer will be invalid if it was closed!
133     */
134     SDL_AsyncIO* asyncio;
135     
136     /**
137         What sort of task was this? Read, write, etc?
138     */
139     SDL_AsyncIOTaskType type;
140     
141     /**
142         The result of the work (success, failure, cancellation).
143     */
144     SDL_AsyncIOResult result;
145     
146     /**
147         Buffer where data was read/written.
148     */
149     void* buffer;
150     
151     /**
152         Offset in the SDL_AsyncIO where data was read/written.
153     */
154     Uint64 offset;
155     
156     /**
157         Number of bytes the task was to read/write.
158     */
159     Uint64 bytes_requested;
160     
161     /**
162         Actual number of bytes that were read/written.
163     */
164     Uint64 bytes_transferred;
165     
166     /**
167         Pointer provided by the app when starting the task
168     */
169     void* userdata;
170 }
171 
172 /**
173     A queue of completed asynchronous I/O tasks.
174 
175     When starting an asynchronous operation, you specify a queue for the new
176     task. A queue can be asked later if any tasks in it have completed,
177     allowing an app to manage multiple pending tasks in one place, in whatever
178     order they complete.
179 
180     History:
181         Available since SDL 3.2.0.
182     
183     See_Also:
184         $(D SDL_CreateAsyncIOQueue)
185         $(D SDL_ReadAsyncIO)
186         $(D SDL_WriteAsyncIO)
187         $(D SDL_GetAsyncIOResult)
188         $(D SDL_WaitAsyncIOResult)
189 */
190 struct SDL_AsyncIOQueue;
191 
192 
193 /**
194     Use this function to create a new SDL_AsyncIO object for reading from and/or writing to a named file.
195 
196     The `mode` string understands the following values:
197     - `"r"`: Open a file for reading only. It must exist.
198     - `"w"`: Open a file for writing only. It will create missing files or
199       truncate existing ones.
200     - `"r+"`: Open a file for update both reading and writing. The file must
201       exist.
202     - `"w+"`: Create an empty file for both reading and writing. If a file with
203       the same name already exists its content is erased and the file is
204       treated as a new empty file.
205     
206     There is no "b" mode, as there is only "binary" style I/O, and no "a" mode
207     for appending, since you specify the position when starting a task.
208     This function supports Unicode filenames, but they must be encoded in UTF-8
209     format, regardless of the underlying operating system.
210     
211     This call is _not_ asynchronous; it will open the file before returning,
212     under the assumption that doing so is generally a fast operation. Future
213     reads and writes to the opened file will be async, however.
214 
215     Params: 
216         file =  a UTF-8 string representing the filename to open.
217         mode =  an ASCII string representing the mode to be used for opening
218                 the file.
219 
220     Returns:
221         a pointer to the SDL_AsyncIO structure that is created or NULL on failure;  
222         call $(D SDL_GetError) for more information.
223 
224     History:
225         Available since SDL 3.2.0.
226     
227     See_Also:
228         $(D SDL_CloseAsyncIO)
229         $(D SDL_ReadAsyncIO)
230         $(D SDL_WriteAsyncIO)
231 */
232 extern SDL_AsyncIO* SDL_AsyncIOFromFile(const(char)* file, const(char)* mode);
233 
234 
235 /**
236     Use this function to get the size of the data stream in an $(D SDL_AsyncIO).
237 
238     This call is *not* asynchronous; it assumes that obtaining this info is a
239     non-blocking operation in most reasonable cases.
240 
241     Params:
242         asyncio = The SDL_AsyncIO to get the size of the data stream from.
243 
244     Returns: 
245         the size of the data stream in the $(D SDL_IOStream) on success or a
246         negative error code on failure; call $(D SDL_GetError) for more
247         information.
248     
249     Threadsafety:
250         It is safe to call this function from any thread.
251 
252     History:
253         Available since SDL 3.2.0.
254 */
255 extern Sint64 SDL_GetAsyncIOSize(SDL_AsyncIO* asyncio);
256 
257 /**
258     Start an async read.
259 
260     This function reads up to `size` bytes from `offset` position in the data
261     source to the area pointed at by `ptr`. This function may read less bytes
262     than requested.
263 
264     This function returns as quickly as possible; it does not wait for the read
265     to complete. On a successful return, this work will continue in the
266     background. If the work begins, even failure is asynchronous: a failing
267     return value from this function only means the work couldn't start at all.
268 
269     `ptr` must remain available until the work is done, and may be accessed by
270     the system at any time until then. Do not allocate it on the stack, as this
271     might take longer than the life of the calling function to complete!
272 
273     An SDL_AsyncIOQueue must be specified. The newly-created task will be added
274     to it when it completes its work.
275 
276     Param:
277         asyncio =   a pointer to an SDL_AsyncIO structure.
278         ptr =       a pointer to a buffer to read data into.
279         offset =    the position to start reading in the data source.
280         size =      the number of bytes to read from the data source.
281         queue =     a queue to add the new SDL_AsyncIO to.
282         userdata =  an app-defined pointer that will be provided with the task
283                     results.
284 
285     Returns:
286         $(D true) on success or $(D false) on failure; call $(D SDL_GetError) for more
287         information.
288 
289     See_Also:
290         $(D SDL_WriteAsyncIO)
291         $(D SDL_CreateAsyncIOQueue)
292     
293     Threadsafety:
294         It is safe to call this function from any thread.
295 
296     History:
297         Available since SDL 3.2.0.
298 */
299 extern bool SDL_ReadAsyncIO(SDL_AsyncIO* asyncio, void* ptr, Uint64 offset, Uint64 size, SDL_AsyncIOQueue* queue, void* userdata);
300 
301 /**
302     Start an async write.
303 
304     This function writes `size` bytes from `offset` position in the data source
305     to the area pointed at by `ptr`.
306 
307     This function returns as quickly as possible; it does not wait for the
308     write to complete. On a successful return, this work will continue in the
309     background. If the work begins, even failure is asynchronous: a failing
310     return value from this function only means the work couldn't start at all.
311 
312     `ptr` must remain available until the work is done, and may be accessed by
313     the system at any time until then. Do not allocate it on the stack, as this
314     might take longer than the life of the calling function to complete!
315 
316     An $(D SDL_AsyncIOQueue) must be specified. The newly-created task will be 
317     added to it when it completes its work.
318 
319     Params:
320         asyncio =   a pointer to an SDL_AsyncIO structure.
321         ptr =       a pointer to a buffer to write data from.
322         offset =    the position to start writing to the data source.
323         size =      the number of bytes to write to the data source.
324         queue =     a queue to add the new SDL_AsyncIO to.
325         userdata =  an app-defined pointer that will be provided with the task
326                     results.
327     
328     Returns:
329         $(D true) on success or $(D false) on failure; call $(D SDL_GetError) for more
330         information.
331 
332     See_Also:
333         $(D SDL_ReadAsyncIO)
334         $(D SDL_CreateAsyncIOQueue)
335     
336     Threadsafety:
337         It is safe to call this function from any thread.
338 
339     History:
340         Available since SDL 3.2.0.
341 */
342 extern bool SDL_WriteAsyncIO(SDL_AsyncIO* asyncio, void* ptr, Uint64 offset, Uint64 size, SDL_AsyncIOQueue* queue, void* userdata);
343 
344 /**
345     Close and free any allocated resources for an async I/O object.
346 
347     Closing a file is *also* an asynchronous task! If a write failure were to
348     happen during the closing process, for example, the task results will
349     report it as usual.
350 
351     Closing a file that has been written to does not guarantee the data has
352     made it to physical media; it may remain in the operating system's file
353     cache, for later writing to disk. This means that a successfully-closed
354     file can be lost if the system crashes or loses power in this small window.
355     To prevent this, call this function with the `flush` parameter set to true.
356     This will make the operation take longer, and perhaps increase system load
357     in general, but a successful result guarantees that the data has made it to
358     physical storage. Don't use this for temporary files, caches, and
359     unimportant data, and definitely use it for crucial irreplaceable files,
360     like game saves.
361 
362     This function guarantees that the close will happen after any other pending
363     tasks to `asyncio`, so it's safe to open a file, start several operations,
364     close the file immediately, then check for all results later. This function
365     will not block until the tasks have completed.
366 
367     Once this function returns true, `asyncio` is no longer valid, regardless
368     of any future outcomes. Any completed tasks might still contain this
369     pointer in their SDL_AsyncIOOutcome data, in case the app was using this
370     value to track information, but it should not be used again.
371 
372     If this function returns false, the close wasn't started at all, and it's
373     safe to attempt to close again later.
374 
375     An $(D SDL_AsyncIOQueue) must be specified. The newly-created task will be 
376     added to it when it completes its work.
377 
378     Params:
379         asyncio =   a pointer to an SDL_AsyncIO structure to close.
380         flush =     true if data should sync to disk before the task completes.
381         queue =     a queue to add the new SDL_AsyncIO to.
382         userdata =  an app-defined pointer that will be provided with the task
383                     results.
384 
385     Returns:
386         $(D true) on success or $(D false) on failure; call $(D SDL_GetError)
387         for more information.
388     
389     Threadsafety:
390         It is safe to call this function from any thread.
391 
392     History:
393         Available since SDL 3.2.0.
394 */
395 extern bool SDL_CloseAsyncIO(SDL_AsyncIO* asyncio, bool flush, SDL_AsyncIOQueue* queue, void* userdata);
396 
397 /**
398     Create a task queue for tracking multiple I/O operations.
399 
400     Async I/O operations are assigned to a queue when started. The queue can be
401     checked for completed tasks thereafter.
402     
403     Returns:
404         A new task queue object or $(D null) if there was an error; call
405         $(D SDL_GetError) for more information.
406     
407     See_Also:
408         $(D SDL_DestroyAsyncIOQueue)
409         $(D SDL_GetAsyncIOResult)
410         $(D SDL_WaitAsyncIOResult)
411     
412     Threadsafety:
413         It is safe to call this function from any thread.
414 
415     History:
416         Available since SDL 3.2.0.
417 */
418 extern SDL_AsyncIOQueue* SDL_CreateAsyncIOQueue();
419 
420 /**
421     Destroy a previously-created async I/O task queue.
422 
423     If there are still tasks pending for this queue, this call will block until
424     those tasks are finished. All those tasks will be deallocated. Their
425     results will be lost to the app.
426 
427     Any pending reads from $(D SDL_LoadFileAsync) that are still in this queue
428     will have their buffers deallocated by this function, to prevent a memory
429     leak.
430 
431     Once this function is called, the queue is no longer valid and should not
432     be used, including by other threads that might access it while destruction
433     is blocking on pending tasks.
434 
435     Do not destroy a queue that still has threads waiting on it through
436     $(D SDL_WaitAsyncIOResult). You can call $(D SDL_SignalAsyncIOQueue) first to
437     unblock those threads, and take measures (such as $(D SDL_WaitThread)) to make
438     sure they have finished their wait and won't wait on the queue again.
439 
440     Params:
441         queue = the task queue to destroy.
442     
443     Threadsafety:
444         It is safe to call this function from any thread, so long as
445         no other thread is waiting on the queue with
446         $(D SDL_WaitAsyncIOResult).
447 
448     History:
449         Available since SDL 3.2.0.
450 */
451 extern void SDL_DestroyAsyncIOQueue(SDL_AsyncIOQueue* queue);
452 
453 /**
454     Query an async I/O task queue for completed tasks.
455 
456     If a task assigned to this queue has finished, this will return true and
457     fill in `outcome` with the details of the task. If no task in the queue has
458     finished, this function will return false. This function does not block.
459 
460     If a task has completed, this function will free its resources and the task
461     pointer will no longer be valid. The task will be removed from the queue.
462 
463     It is safe for multiple threads to call this function on the same queue at
464     once; a completed task will only go to one of the threads.
465 
466     Params:
467         queue =     the async I/O task queue to query.
468         outcome =   details of a finished task will be written here. 
469                     May not be $(D null).
470     Returns:
471         $(D true) if a task has completed, $(D false) otherwise.
472 
473     See_Also:
474         $(D SDL_WaitAsyncIOResult)
475 
476     Threadsafety:
477         It is safe to call this function from any thread.
478 
479     History:
480         Available since SDL 3.2.0.
481 */
482 extern bool SDL_GetAsyncIOResult(SDL_AsyncIOQueue* queue, SDL_AsyncIOOutcome* outcome);
483 
484 /**
485     Block until an async I/O task queue has a completed task.
486 
487     This function puts the calling thread to sleep until there a task assigned
488     to the queue that has finished.
489 
490     If a task assigned to the queue has finished, this will return true and
491     fill in `outcome` with the details of the task. If no task in the queue has
492     finished, this function will return false.
493 
494     If a task has completed, this function will free its resources and the task
495     pointer will no longer be valid. The task will be removed from the queue.
496 
497     It is safe for multiple threads to call this function on the same queue at
498     once; a completed task will only go to one of the threads.
499 
500     Note that by the nature of various platforms, more than one waiting thread
501     may wake to handle a single task, but only one will obtain it, so
502     `timeoutMS` is a *maximum* wait time, and this function may return false
503     sooner.
504 
505     This function may return false if there was a system error, the OS
506     inadvertently awoke multiple threads, or if $(D SDL_SignalAsyncIOQueue) was
507     called to wake up all waiting threads without a finished task.
508 
509     A timeout can be used to specify a maximum wait time, but rather than
510     polling, it is possible to have a timeout of -1 to wait forever, and use
511     $(D SDL_SignalAsyncIOQueue) to wake up the waiting threads later.
512 
513 
514     Params:
515         queue =     the async I/O task queue to wait on.
516         outcome =   details of a finished task will be written here. May not be
517                     $(D null).
518         timeoutMS = the maximum time to wait, in milliseconds, or -1 to wait
519                     indefinitely.
520     
521     Returns:
522         $(D true) if task has completed, $(D false) otherwise.
523 
524     See_Also:
525         $(D SDL_SignalAsyncIOQueue)
526 
527     Threadsafety:
528         It is safe to call this function from any thread.
529 
530     History:
531         Available since SDL 3.2.0.
532 */
533 extern bool SDL_WaitAsyncIOResult(SDL_AsyncIOQueue* queue, SDL_AsyncIOOutcome* outcome, Sint32 timeoutMS);
534 
535 /**
536     Wake up any threads that are blocking in $(D SDL_WaitAsyncIOResult).
537 
538     This will unblock any threads that are sleeping in a call to
539     $(D SDL_WaitAsyncIOResult) for the specified queue, and cause them to return
540     from that function.
541 
542     This can be useful when destroying a queue to make sure nothing is touching
543     it indefinitely. In this case, once this call completes, the caller should
544     take measures to make sure any previously-blocked threads have returned
545     from their wait and will not touch the queue again (perhaps by setting a
546     flag to tell the threads to terminate and then using $(D SDL_WaitThread) to
547     make sure they've done so).
548 
549     Params:
550         queue = the async I/O task queue to signal.
551 
552     See_Also:
553         $(D SDL_WaitAsyncIOResult)
554 
555     Threadsafety:
556         It is safe to call this function from any thread.
557 
558     History:
559         Available since SDL 3.2.0.
560 */
561 extern void SDL_SignalAsyncIOQueue(SDL_AsyncIOQueue* queue);
562 
563 /**
564     Load all the data from a file path, asynchronously.
565 
566     This function returns as quickly as possible; it does not wait for the read
567     to complete. On a successful return, this work will continue in the
568     background. If the work begins, even failure is asynchronous: a failing
569     return value from this function only means the work couldn't start at all.
570 
571     The data is allocated with a zero byte at the end (null terminated) for
572     convenience. This extra byte is not included in $(D SDL_AsyncIOOutcome)'s
573     bytes_transferred value.
574 
575     This function will allocate the buffer to contain the file. It must be
576     deallocated by calling $(D SDL_free) on $(D SDL_AsyncIOOutcome)'s buffer
577     field after completion.
578 
579     An $(D SDL_AsyncIOQueue) must be specified. The newly-created task will
580     be added to it when it completes its work.
581 
582     Params:
583         file =      the path to read all available data from.
584         queue =     a queue to add the new $(D SDL_AsyncIO) to.
585         userdata =  an app-defined pointer that will be provided with the task
586                     results.
587 
588     Returns:
589         $(D true) on success or $(D false) on failure; call $(D SDL_GetError)
590         for more information.
591 
592     See_Also:
593         $(D SDL_LoadFile_IO)
594 
595     History:
596         Available since SDL 3.2.0.
597 */
598 extern bool SDL_LoadFileAsync(const(char)* file, SDL_AsyncIOQueue* queue, void* userdata);