SDL_asyncio.h 21 KB

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