Atlas - SDL_asyncio.h

Home / ext / SDL / include / SDL3 Lines: 1 | Size: 23310 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/* WIKI CATEGORY: AsyncIO */ 23 24/** 25 * # CategoryAsyncIO 26 * 27 * SDL offers a way to perform I/O asynchronously. This allows an app to read 28 * or write files without waiting for data to actually transfer; the functions 29 * that request I/O never block while the request is fulfilled. 30 * 31 * Instead, the data moves in the background and the app can check for results 32 * at their leisure. 33 * 34 * This is more complicated than just reading and writing files in a 35 * synchronous way, but it can allow for more efficiency, and never having 36 * framerate drops as the hard drive catches up, etc. 37 * 38 * The general usage pattern for async I/O is: 39 * 40 * - Create one or more SDL_AsyncIOQueue objects. 41 * - Open files with SDL_AsyncIOFromFile. 42 * - Start I/O tasks to the files with SDL_ReadAsyncIO or SDL_WriteAsyncIO, 43 * putting those tasks into one of the queues. 44 * - Later on, use SDL_GetAsyncIOResult on a queue to see if any task is 45 * finished without blocking. Tasks might finish in any order with success 46 * or failure. 47 * - When all your tasks are done, close the file with SDL_CloseAsyncIO. This 48 * also generates a task, since it might flush data to disk! 49 * 50 * This all works, without blocking, in a single thread, but one can also wait 51 * on a queue in a background thread, sleeping until new results have arrived: 52 * 53 * - Call SDL_WaitAsyncIOResult from one or more threads to efficiently block 54 * until new tasks complete. 55 * - When shutting down, call SDL_SignalAsyncIOQueue to unblock any sleeping 56 * threads despite there being no new tasks completed. 57 * 58 * And, of course, to match the synchronous SDL_LoadFile, we offer 59 * SDL_LoadFileAsync as a convenience function. This will handle allocating a 60 * buffer, slurping in the file data, and null-terminating it; you still check 61 * for results later. 62 * 63 * Behind the scenes, SDL will use newer, efficient APIs on platforms that 64 * support them: Linux's io_uring and Windows 11's IoRing, for example. If 65 * those technologies aren't available, SDL will offload the work to a thread 66 * pool that will manage otherwise-synchronous loads without blocking the app. 67 * 68 * ## Best Practices 69 * 70 * Simple non-blocking I/O--for an app that just wants to pick up data 71 * whenever it's ready without losing framerate waiting on disks to spin--can 72 * use whatever pattern works well for the program. In this case, simply call 73 * SDL_ReadAsyncIO, or maybe SDL_LoadFileAsync, as needed. Once a frame, call 74 * SDL_GetAsyncIOResult to check for any completed tasks and deal with the 75 * data as it arrives. 76 * 77 * If two separate pieces of the same program need their own I/O, it is legal 78 * for each to create their own queue. This will prevent either piece from 79 * accidentally consuming the other's completed tasks. Each queue does require 80 * some amount of resources, but it is not an overwhelming cost. Do not make a 81 * queue for each task, however. It is better to put many tasks into a single 82 * queue. They will be reported in order of completion, not in the order they 83 * were submitted, so it doesn't generally matter what order tasks are 84 * started. 85 * 86 * One async I/O queue can be shared by multiple threads, or one thread can 87 * have more than one queue, but the most efficient way--if ruthless 88 * efficiency is the goal--is to have one queue per thread, with multiple 89 * threads working in parallel, and attempt to keep each queue loaded with 90 * tasks that are both started by and consumed by the same thread. On modern 91 * platforms that can use newer interfaces, this can keep data flowing as 92 * efficiently as possible all the way from storage hardware to the app, with 93 * no contention between threads for access to the same queue. 94 * 95 * Written data is not guaranteed to make it to physical media by the time a 96 * closing task is completed, unless SDL_CloseAsyncIO is called with its 97 * `flush` parameter set to true, which is to say that a successful result 98 * here can still result in lost data during an unfortunately-timed power 99 * outage if not flushed. However, flushing will take longer and may be 100 * unnecessary, depending on the app's needs. 101 */ 102 103#ifndef SDL_asyncio_h_ 104#define SDL_asyncio_h_ 105 106#include <SDL3/SDL_stdinc.h> 107 108#include <SDL3/SDL_begin_code.h> 109/* Set up for C function definitions, even when using C++ */ 110#ifdef __cplusplus 111extern "C" { 112#endif 113 114/** 115 * The asynchronous I/O operation structure. 116 * 117 * This operates as an opaque handle. One can then request read or write 118 * operations on it. 119 * 120 * \since This struct is available since SDL 3.2.0. 121 * 122 * \sa SDL_AsyncIOFromFile 123 */ 124typedef struct SDL_AsyncIO SDL_AsyncIO; 125 126/** 127 * Types of asynchronous I/O tasks. 128 * 129 * \since This enum is available since SDL 3.2.0. 130 */ 131typedef enum SDL_AsyncIOTaskType 132{ 133 SDL_ASYNCIO_TASK_READ, /**< A read operation. */ 134 SDL_ASYNCIO_TASK_WRITE, /**< A write operation. */ 135 SDL_ASYNCIO_TASK_CLOSE /**< A close operation. */ 136} SDL_AsyncIOTaskType; 137 138/** 139 * Possible outcomes of an asynchronous I/O task. 140 * 141 * \since This enum is available since SDL 3.2.0. 142 */ 143typedef enum SDL_AsyncIOResult 144{ 145 SDL_ASYNCIO_COMPLETE, /**< request was completed without error */ 146 SDL_ASYNCIO_FAILURE, /**< request failed for some reason; check SDL_GetError()! */ 147 SDL_ASYNCIO_CANCELED /**< request was canceled before completing. */ 148} SDL_AsyncIOResult; 149 150/** 151 * Information about a completed asynchronous I/O request. 152 * 153 * \since This struct is available since SDL 3.2.0. 154 */ 155typedef struct SDL_AsyncIOOutcome 156{ 157 SDL_AsyncIO *asyncio; /**< what generated this task. This pointer will be invalid if it was closed! */ 158 SDL_AsyncIOTaskType type; /**< What sort of task was this? Read, write, etc? */ 159 SDL_AsyncIOResult result; /**< the result of the work (success, failure, cancellation). */ 160 void *buffer; /**< buffer where data was read/written. */ 161 Uint64 offset; /**< offset in the SDL_AsyncIO where data was read/written. */ 162 Uint64 bytes_requested; /**< number of bytes the task was to read/write. */ 163 Uint64 bytes_transferred; /**< actual number of bytes that were read/written. */ 164 void *userdata; /**< pointer provided by the app when starting the task */ 165} SDL_AsyncIOOutcome; 166 167/** 168 * A queue of completed asynchronous I/O tasks. 169 * 170 * When starting an asynchronous operation, you specify a queue for the new 171 * task. A queue can be asked later if any tasks in it have completed, 172 * allowing an app to manage multiple pending tasks in one place, in whatever 173 * order they complete. 174 * 175 * \since This struct is available since SDL 3.2.0. 176 * 177 * \sa SDL_CreateAsyncIOQueue 178 * \sa SDL_ReadAsyncIO 179 * \sa SDL_WriteAsyncIO 180 * \sa SDL_GetAsyncIOResult 181 * \sa SDL_WaitAsyncIOResult 182 */ 183typedef struct SDL_AsyncIOQueue SDL_AsyncIOQueue; 184 185/** 186 * Use this function to create a new SDL_AsyncIO object for reading from 187 * and/or writing to a named file. 188 * 189 * The `mode` string understands the following values: 190 * 191 * - "r": Open a file for reading only. It must exist. 192 * - "w": Open a file for writing only. It will create missing files or 193 * truncate existing ones. 194 * - "r+": Open a file for update both reading and writing. The file must 195 * exist. 196 * - "w+": Create an empty file for both reading and writing. If a file with 197 * the same name already exists its content is erased and the file is 198 * treated as a new empty file. 199 * 200 * There is no "b" mode, as there is only "binary" style I/O, and no "a" mode 201 * for appending, since you specify the position when starting a task. 202 * 203 * This function supports Unicode filenames, but they must be encoded in UTF-8 204 * format, regardless of the underlying operating system. 205 * 206 * This call is _not_ asynchronous; it will open the file before returning, 207 * under the assumption that doing so is generally a fast operation. Future 208 * reads and writes to the opened file will be async, however. 209 * 210 * \param file a UTF-8 string representing the filename to open. 211 * \param mode an ASCII string representing the mode to be used for opening 212 * the file. 213 * \returns a pointer to the SDL_AsyncIO structure that is created or NULL on 214 * failure; call SDL_GetError() for more information. 215 * 216 * \since This function is available since SDL 3.2.0. 217 * 218 * \sa SDL_CloseAsyncIO 219 * \sa SDL_ReadAsyncIO 220 * \sa SDL_WriteAsyncIO 221 */ 222extern SDL_DECLSPEC SDL_AsyncIO * SDLCALL SDL_AsyncIOFromFile(const char *file, const char *mode); 223 224/** 225 * Use this function to get the size of the data stream in an SDL_AsyncIO. 226 * 227 * This call is _not_ asynchronous; it assumes that obtaining this info is a 228 * non-blocking operation in most reasonable cases. 229 * 230 * \param asyncio the SDL_AsyncIO to get the size of the data stream from. 231 * \returns the size of the data stream in the SDL_IOStream on success or a 232 * negative error code on failure; call SDL_GetError() for more 233 * information. 234 * 235 * \threadsafety It is safe to call this function from any thread. 236 * 237 * \since This function is available since SDL 3.2.0. 238 */ 239extern SDL_DECLSPEC Sint64 SDLCALL SDL_GetAsyncIOSize(SDL_AsyncIO *asyncio); 240 241/** 242 * Start an async read. 243 * 244 * This function reads up to `size` bytes from `offset` position in the data 245 * source to the area pointed at by `ptr`. This function may read less bytes 246 * than requested. 247 * 248 * This function returns as quickly as possible; it does not wait for the read 249 * to complete. On a successful return, this work will continue in the 250 * background. If the work begins, even failure is asynchronous: a failing 251 * return value from this function only means the work couldn't start at all. 252 * 253 * `ptr` must remain available until the work is done, and may be accessed by 254 * the system at any time until then. Do not allocate it on the stack, as this 255 * might take longer than the life of the calling function to complete! 256 * 257 * An SDL_AsyncIOQueue must be specified. The newly-created task will be added 258 * to it when it completes its work. 259 * 260 * \param asyncio a pointer to an SDL_AsyncIO structure. 261 * \param ptr a pointer to a buffer to read data into. 262 * \param offset the position to start reading in the data source. 263 * \param size the number of bytes to read from the data source. 264 * \param queue a queue to add the new SDL_AsyncIO to. 265 * \param userdata an app-defined pointer that will be provided with the task 266 * results. 267 * \returns true on success or false on failure; call SDL_GetError() for more 268 * information. 269 * 270 * \threadsafety It is safe to call this function from any thread. 271 * 272 * \since This function is available since SDL 3.2.0. 273 * 274 * \sa SDL_WriteAsyncIO 275 * \sa SDL_CreateAsyncIOQueue 276 */ 277extern SDL_DECLSPEC bool SDLCALL SDL_ReadAsyncIO(SDL_AsyncIO *asyncio, void *ptr, Uint64 offset, Uint64 size, SDL_AsyncIOQueue *queue, void *userdata); 278 279/** 280 * Start an async write. 281 * 282 * This function writes `size` bytes from `offset` position in the data source 283 * to the area pointed at by `ptr`. 284 * 285 * This function returns as quickly as possible; it does not wait for the 286 * write to complete. On a successful return, this work will continue in the 287 * background. If the work begins, even failure is asynchronous: a failing 288 * return value from this function only means the work couldn't start at all. 289 * 290 * `ptr` must remain available until the work is done, and may be accessed by 291 * the system at any time until then. Do not allocate it on the stack, as this 292 * might take longer than the life of the calling function to complete! 293 * 294 * An SDL_AsyncIOQueue must be specified. The newly-created task will be added 295 * to it when it completes its work. 296 * 297 * \param asyncio a pointer to an SDL_AsyncIO structure. 298 * \param ptr a pointer to a buffer to write data from. 299 * \param offset the position to start writing to the data source. 300 * \param size the number of bytes to write to the data source. 301 * \param queue a queue to add the new SDL_AsyncIO to. 302 * \param userdata an app-defined pointer that will be provided with the task 303 * results. 304 * \returns true on success or false on failure; call SDL_GetError() for more 305 * information. 306 * 307 * \threadsafety It is safe to call this function from any thread. 308 * 309 * \since This function is available since SDL 3.2.0. 310 * 311 * \sa SDL_ReadAsyncIO 312 * \sa SDL_CreateAsyncIOQueue 313 */ 314extern SDL_DECLSPEC bool SDLCALL SDL_WriteAsyncIO(SDL_AsyncIO *asyncio, void *ptr, Uint64 offset, Uint64 size, SDL_AsyncIOQueue *queue, void *userdata); 315 316/** 317 * Close and free any allocated resources for an async I/O object. 318 * 319 * Closing a file is _also_ an asynchronous task! If a write failure were to 320 * happen during the closing process, for example, the task results will 321 * report it as usual. 322 * 323 * Closing a file that has been written to does not guarantee the data has 324 * made it to physical media; it may remain in the operating system's file 325 * cache, for later writing to disk. This means that a successfully-closed 326 * file can be lost if the system crashes or loses power in this small window. 327 * To prevent this, call this function with the `flush` parameter set to true. 328 * This will make the operation take longer, and perhaps increase system load 329 * in general, but a successful result guarantees that the data has made it to 330 * physical storage. Don't use this for temporary files, caches, and 331 * unimportant data, and definitely use it for crucial irreplaceable files, 332 * like game saves. 333 * 334 * This function guarantees that the close will happen after any other pending 335 * tasks to `asyncio`, so it's safe to open a file, start several operations, 336 * close the file immediately, then check for all results later. This function 337 * will not block until the tasks have completed. 338 * 339 * Once this function returns true, `asyncio` is no longer valid, regardless 340 * of any future outcomes. Any completed tasks might still contain this 341 * pointer in their SDL_AsyncIOOutcome data, in case the app was using this 342 * value to track information, but it should not be used again. 343 * 344 * If this function returns false, the close wasn't started at all, and it's 345 * safe to attempt to close again later. 346 * 347 * An SDL_AsyncIOQueue must be specified. The newly-created task will be added 348 * to it when it completes its work. 349 * 350 * \param asyncio a pointer to an SDL_AsyncIO structure to close. 351 * \param flush true if data should sync to disk before the task completes. 352 * \param queue a queue to add the new SDL_AsyncIO to. 353 * \param userdata an app-defined pointer that will be provided with the task 354 * results. 355 * \returns true on success or false on failure; call SDL_GetError() for more 356 * information. 357 * 358 * \threadsafety It is safe to call this function from any thread, but two 359 * threads should not attempt to close the same object. 360 * 361 * \since This function is available since SDL 3.2.0. 362 */ 363extern SDL_DECLSPEC bool SDLCALL SDL_CloseAsyncIO(SDL_AsyncIO *asyncio, bool flush, SDL_AsyncIOQueue *queue, void *userdata); 364 365/** 366 * Create a task queue for tracking multiple I/O operations. 367 * 368 * Async I/O operations are assigned to a queue when started. The queue can be 369 * checked for completed tasks thereafter. 370 * 371 * \returns a new task queue object or NULL if there was an error; call 372 * SDL_GetError() for more information. 373 * 374 * \threadsafety It is safe to call this function from any thread. 375 * 376 * \since This function is available since SDL 3.2.0. 377 * 378 * \sa SDL_DestroyAsyncIOQueue 379 * \sa SDL_GetAsyncIOResult 380 * \sa SDL_WaitAsyncIOResult 381 */ 382extern SDL_DECLSPEC SDL_AsyncIOQueue * SDLCALL SDL_CreateAsyncIOQueue(void); 383 384/** 385 * Destroy a previously-created async I/O task queue. 386 * 387 * If there are still tasks pending for this queue, this call will block until 388 * those tasks are finished. All those tasks will be deallocated. Their 389 * results will be lost to the app. 390 * 391 * Any pending reads from SDL_LoadFileAsync() that are still in this queue 392 * will have their buffers deallocated by this function, to prevent a memory 393 * leak. 394 * 395 * Once this function is called, the queue is no longer valid and should not 396 * be used, including by other threads that might access it while destruction 397 * is blocking on pending tasks. 398 * 399 * Do not destroy a queue that still has threads waiting on it through 400 * SDL_WaitAsyncIOResult(). You can call SDL_SignalAsyncIOQueue() first to 401 * unblock those threads, and take measures (such as SDL_WaitThread()) to make 402 * sure they have finished their wait and won't wait on the queue again. 403 * 404 * \param queue the task queue to destroy. 405 * 406 * \threadsafety It is safe to call this function from any thread, so long as 407 * no other thread is waiting on the queue with 408 * SDL_WaitAsyncIOResult. 409 * 410 * \since This function is available since SDL 3.2.0. 411 */ 412extern SDL_DECLSPEC void SDLCALL SDL_DestroyAsyncIOQueue(SDL_AsyncIOQueue *queue); 413 414/** 415 * Query an async I/O task queue for completed tasks. 416 * 417 * If a task assigned to this queue has finished, this will return true and 418 * fill in `outcome` with the details of the task. If no task in the queue has 419 * finished, this function will return false. This function does not block. 420 * 421 * If a task has completed, this function will free its resources and the task 422 * pointer will no longer be valid. The task will be removed from the queue. 423 * 424 * It is safe for multiple threads to call this function on the same queue at 425 * once; a completed task will only go to one of the threads. 426 * 427 * \param queue the async I/O task queue to query. 428 * \param outcome details of a finished task will be written here. May not be 429 * NULL. 430 * \returns true if a task has completed, false otherwise. 431 * 432 * \threadsafety It is safe to call this function from any thread. 433 * 434 * \since This function is available since SDL 3.2.0. 435 * 436 * \sa SDL_WaitAsyncIOResult 437 */ 438extern SDL_DECLSPEC bool SDLCALL SDL_GetAsyncIOResult(SDL_AsyncIOQueue *queue, SDL_AsyncIOOutcome *outcome); 439 440/** 441 * Block until an async I/O task queue has a completed task. 442 * 443 * This function puts the calling thread to sleep until there a task assigned 444 * to the queue that has finished. 445 * 446 * If a task assigned to the queue has finished, this will return true and 447 * fill in `outcome` with the details of the task. If no task in the queue has 448 * finished, this function will return false. 449 * 450 * If a task has completed, this function will free its resources and the task 451 * pointer will no longer be valid. The task will be removed from the queue. 452 * 453 * It is safe for multiple threads to call this function on the same queue at 454 * once; a completed task will only go to one of the threads. 455 * 456 * Note that by the nature of various platforms, more than one waiting thread 457 * may wake to handle a single task, but only one will obtain it, so 458 * `timeoutMS` is a _maximum_ wait time, and this function may return false 459 * sooner. 460 * 461 * This function may return false if there was a system error, the OS 462 * inadvertently awoke multiple threads, or if SDL_SignalAsyncIOQueue() was 463 * called to wake up all waiting threads without a finished task. 464 * 465 * A timeout can be used to specify a maximum wait time, but rather than 466 * polling, it is possible to have a timeout of -1 to wait forever, and use 467 * SDL_SignalAsyncIOQueue() to wake up the waiting threads later. 468 * 469 * \param queue the async I/O task queue to wait on. 470 * \param outcome details of a finished task will be written here. May not be 471 * NULL. 472 * \param timeoutMS the maximum time to wait, in milliseconds, or -1 to wait 473 * indefinitely. 474 * \returns true if task has completed, false otherwise. 475 * 476 * \threadsafety It is safe to call this function from any thread. 477 * 478 * \since This function is available since SDL 3.2.0. 479 * 480 * \sa SDL_SignalAsyncIOQueue 481 */ 482extern SDL_DECLSPEC bool SDLCALL SDL_WaitAsyncIOResult(SDL_AsyncIOQueue *queue, SDL_AsyncIOOutcome *outcome, Sint32 timeoutMS); 483 484/** 485 * Wake up any threads that are blocking in SDL_WaitAsyncIOResult(). 486 * 487 * This will unblock any threads that are sleeping in a call to 488 * SDL_WaitAsyncIOResult for the specified queue, and cause them to return 489 * from that function. 490 * 491 * This can be useful when destroying a queue to make sure nothing is touching 492 * it indefinitely. In this case, once this call completes, the caller should 493 * take measures to make sure any previously-blocked threads have returned 494 * from their wait and will not touch the queue again (perhaps by setting a 495 * flag to tell the threads to terminate and then using SDL_WaitThread() to 496 * make sure they've done so). 497 * 498 * \param queue the async I/O task queue to signal. 499 * 500 * \threadsafety It is safe to call this function from any thread. 501 * 502 * \since This function is available since SDL 3.2.0. 503 * 504 * \sa SDL_WaitAsyncIOResult 505 */ 506extern SDL_DECLSPEC void SDLCALL SDL_SignalAsyncIOQueue(SDL_AsyncIOQueue *queue); 507 508/** 509 * Load all the data from a file path, asynchronously. 510 * 511 * This function returns as quickly as possible; it does not wait for the read 512 * to complete. On a successful return, this work will continue in the 513 * background. If the work begins, even failure is asynchronous: a failing 514 * return value from this function only means the work couldn't start at all. 515 * 516 * The data is allocated with a zero byte at the end (null terminated) for 517 * convenience. This extra byte is not included in SDL_AsyncIOOutcome's 518 * bytes_transferred value. 519 * 520 * This function will allocate the buffer to contain the file. It must be 521 * deallocated by calling SDL_free() on SDL_AsyncIOOutcome's buffer field 522 * after completion. 523 * 524 * An SDL_AsyncIOQueue must be specified. The newly-created task will be added 525 * to it when it completes its work. 526 * 527 * \param file the path to read all available data from. 528 * \param queue a queue to add the new SDL_AsyncIO to. 529 * \param userdata an app-defined pointer that will be provided with the task 530 * results. 531 * \returns true on success or false on failure; call SDL_GetError() for more 532 * information. 533 * 534 * \since This function is available since SDL 3.2.0. 535 * 536 * \sa SDL_LoadFile_IO 537 */ 538extern SDL_DECLSPEC bool SDLCALL SDL_LoadFileAsync(const char *file, SDL_AsyncIOQueue *queue, void *userdata); 539 540/* Ends C function definitions when using C++ */ 541#ifdef __cplusplus 542} 543#endif 544#include <SDL3/SDL_close_code.h> 545 546#endif /* SDL_asyncio_h_ */ 547
[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.