ASYNC_WAIT_CTX_NEW(3ossl)

πŸ’– NAME πŸ’–

okay bestie, welcome to the OpenSSL manual, section 3ossl β€” this page is all about the glow-up squad that manages waiting for asynchronous jobs to finish. say hi to the whole crew:

ASYNC_WAIT_CTX_new, ASYNC_WAIT_CTX_free, ASYNC_WAIT_CTX_set_wait_fd, ASYNC_WAIT_CTX_get_fd, ASYNC_WAIT_CTX_get_all_fds, ASYNC_WAIT_CTX_get_changed_fds, ASYNC_WAIT_CTX_clear_fd, ASYNC_WAIT_CTX_set_callback, ASYNC_WAIT_CTX_get_callback, ASYNC_WAIT_CTX_set_status, ASYNC_WAIT_CTX_get_status, ASYNC_callback_fn, ASYNC_STATUS_UNSUPPORTED, ASYNC_STATUS_ERR, ASYNC_STATUS_OK, ASYNC_STATUS_EAGAIN

they are, and I quote, "functions to manage waiting for asynchronous jobs to complete". iconic. πŸ’…


✨ SYNOPSIS ✨

#include <openssl/async.h>

#define ASYNC_STATUS_UNSUPPORTED    0
#define ASYNC_STATUS_ERR            1
#define ASYNC_STATUS_OK             2
#define ASYNC_STATUS_EAGAIN         3
typedef int (*ASYNC_callback_fn)(void *arg);
ASYNC_WAIT_CTX *ASYNC_WAIT_CTX_new(void);
void ASYNC_WAIT_CTX_free(ASYNC_WAIT_CTX *ctx);
int ASYNC_WAIT_CTX_set_wait_fd(ASYNC_WAIT_CTX *ctx, const void *key,
                               OSSL_ASYNC_FD fd,
                               void *custom_data,
                               void (*cleanup)(ASYNC_WAIT_CTX *, const void *,
                                               OSSL_ASYNC_FD, void *));
int ASYNC_WAIT_CTX_get_fd(ASYNC_WAIT_CTX *ctx, const void *key,
                          OSSL_ASYNC_FD *fd, void **custom_data);
int ASYNC_WAIT_CTX_get_all_fds(ASYNC_WAIT_CTX *ctx, OSSL_ASYNC_FD *fd,
                               size_t *numfds);
int ASYNC_WAIT_CTX_get_changed_fds(ASYNC_WAIT_CTX *ctx, OSSL_ASYNC_FD *addfd,
                                   size_t *numaddfds, OSSL_ASYNC_FD *delfd,
                                   size_t *numdelfds);
int ASYNC_WAIT_CTX_clear_fd(ASYNC_WAIT_CTX *ctx, const void *key);
int ASYNC_WAIT_CTX_set_callback(ASYNC_WAIT_CTX *ctx,
                                ASYNC_callback_fn callback,
                                void *callback_arg);
int ASYNC_WAIT_CTX_get_callback(ASYNC_WAIT_CTX *ctx,
                                ASYNC_callback_fn *callback,
                                void **callback_arg);
int ASYNC_WAIT_CTX_set_status(ASYNC_WAIT_CTX *ctx, int status);
int ASYNC_WAIT_CTX_get_status(ASYNC_WAIT_CTX *ctx);

no cap, that's the whole roster. 🫢


🌸 DESCRIPTION 🌸

first things first β€” if you wanna know the big picture of how async stuff works in OpenSSL, go read ASYNC_start_job(3). this page is the detail-oriented bestie.

so. an ASYNC_WAIT_CTX object represents an asynchronous "session" β€” meaning a related set of crypto operations. think of it like a little friend group of operations that belong together. for example, in SSL terms, this has a one-to-one correspondence with an SSL connection. literally a whole situationship. 😭

your application code must create an ASYNC_WAIT_CTX using ASYNC_WAIT_CTX_new() before calling ASYNC_start_job() (peep ASYNC_start_job(3) for that). when the job starts, it gets associated with the ASYNC_WAIT_CTX for the whole duration of that job.

⚠️ an ASYNC_WAIT_CTX should only be used for one ASYNC_JOB at any one time β€” but it can absolutely be reused after an ASYNC_JOB has finished for a subsequent ASYNC_JOB. recycling queen. ♻️

when the session is complete (like, say, the SSL connection is closed), your application code cleans up with ASYNC_WAIT_CTX_free(). bye bestie πŸ‘‹


πŸŽ€ wait, there are file descriptors? πŸŽ€

ASYNC_WAIT_CTXs can have "wait" file descriptors associated with them. here's the tea:

call ASYNC_WAIT_CTX_get_all_fds() and pass in a pointer to an ASYNC_WAIT_CTX in the ctx parameter, and it'll return the wait file descriptors associated with that job in *fd. the number of file descriptors returned gets stored in *numfds.

🫢 heads up: it is the caller's responsibility to make sure enough memory has been allocated in *fd to receive all the file descriptors.

calling ASYNC_WAIT_CTX_get_all_fds() with a NULL fd value will return no file descriptors but will still populate *numfds. so yeah, application code is typically expected to call this function twice: once to get the number of fds, and then again once sufficient memory has been allocated. two-step. very main character. ✨

if only one asynchronous engine is being used, then normally this call will only ever return one fd. if multiple asynchronous engines are being used, then more could be returned. the more the merrier I guess πŸ’€


πŸ¦‹ changed fds? say less πŸ¦‹

the function ASYNC_WAIT_CTX_get_changed_fds() can be used to detect if any fds have changed since the last time ASYNC_start_job() returned ASYNC_PAUSE (or since the ASYNC_WAIT_CTX was created, if no ASYNC_PAUSE result has been received).

  • the numaddfds and numdelfds parameters will be populated with the number of fds added or deleted respectively.
  • *addfd and *delfd will be populated with the list of added and deleted fds respectively.

same energy as ASYNC_WAIT_CTX_get_all_fds() β€” either of these can be NULL, but if they are not NULL then the caller is responsible for ensuring sufficient memory is allocated. no shortcuts bestie πŸ’…


🌈 a lil note for the engine girlies 🌈

implementers of async aware code (hi, engines πŸ‘‹) are encouraged to return a stable fd for the lifetime of the ASYNC_WAIT_CTX in order to reduce the "churn" of regularly changing fds β€” although no guarantees of this are provided to applications. so like, nice if you do it, but don't promise anything. πŸ™ˆ

applications can wait for the file descriptor to be ready for "read" using a system function call such as select or poll (being ready for "read" indicates that the job should be resumed). if no file descriptor is made available, then an application will have to periodically "poll" the job by attempting to restart it to see if it is ready to continue. it's giving patience. ⏳


πŸ’œ the fd lifecycle πŸ’œ

async aware code (again β€” engines, hello) can get the current ASYNC_WAIT_CTX from the job via ASYNC_get_wait_ctx(3) and provide a file descriptor to use for waiting on by calling ASYNC_WAIT_CTX_set_wait_fd(). typically this would be done by an engine immediately prior to calling ASYNC_pause_job(), and not by end user code. know your lane, bestie πŸŽ€

  • an existing association with a file descriptor can be obtained using ASYNC_WAIT_CTX_get_fd() and cleared using ASYNC_WAIT_CTX_clear_fd().
  • both of these functions require a key value which is unique to the async aware code. this could be any unique value, but a good candidate might be the ENGINE * for the engine. pick your poison ✨
  • the custom_data parameter can be any value, and will be returned in a subsequent call to ASYNC_WAIT_CTX_get_fd().
  • ASYNC_WAIT_CTX_set_wait_fd() also expects a pointer to a "cleanup" routine. this can be NULL, but if provided it will automatically get called when the ASYNC_WAIT_CTX is freed, and gives the engine the opportunity to close the fd or any other resources.
πŸ’€ NOTE: the "cleanup" routine does not get called if the fd is cleared directly via a call to ASYNC_WAIT_CTX_clear_fd(). don't say I didn't warn you.

πŸ“ a whole example, periodt πŸ“

an example of typical usage might be an async capable engine. user code would initiate cryptographic operations. the engine would initiate those operations asynchronously and then call ASYNC_WAIT_CTX_set_wait_fd() followed by ASYNC_pause_job() to return control to the user code. the user code can then perform other tasks or wait for the job to be ready by calling "select" or other similar function on the wait file descriptor. the engine can signal to the user code that the job should be resumed by making the wait file descriptor "readable". once resumed, the engine should clear the wake signal on the wait file descriptor. clean. simple. slay. πŸ’…


🧚 callback era 🧚

as well as a file descriptor, user code may also be notified via a callback. the callback and data pointers are stored within the ASYNC_WAIT_CTX along with an additional status field that can be used for the notification of retries from an engine. this additional method can be used when the user thinks that a file descriptor is too costly in terms of CPU cycles, or in some context where a file descriptor is not appropriate. sometimes you just don't want an fd, and that's valid 🫢

  • ASYNC_WAIT_CTX_set_callback() sets the callback and the callback argument. the callback will be called to notify user code when an engine completes a cryptography operation.
⚠️ it is a requirement that the callback function is small and nonblocking, as it will be run in the context of a polling mechanism or an interrupt. keep it snappy bestie, no yapping πŸ’…
  • ASYNC_WAIT_CTX_get_callback() returns the callback set in the ASYNC_WAIT_CTX structure.

πŸ‘‘ status check πŸ‘‘ (vibe check but for engines)

ASYNC_WAIT_CTX_set_status() allows an engine to set the current engine status. the possible status values are the following:

  • ASYNC_STATUS_UNSUPPORTED β€” the engine does not support the callback mechanism. this is the default value. the engine must call ASYNC_WAIT_CTX_set_status() to set the status to some value other than ASYNC_STATUS_UNSUPPORTED if it intends to enable the callback mechanism.
  • ASYNC_STATUS_ERR β€” the engine has a fatal problem with this request. the user code should clean up this session. 😭 it's giving emergency
  • ASYNC_STATUS_OK β€” the request has been successfully submitted. we love to see it πŸŽ€
  • ASYNC_STATUS_EAGAIN β€” the engine has some problem which will be recovered soon, such as a buffer is full, so user code should resume the job. minor setback, we bounce back ✨

ASYNC_WAIT_CTX_get_status() allows user code to obtain the current status value.

🫢 if the status is any value other than ASYNC_STATUS_OK, then the user code should not expect to receive a callback from the engine, even if one has been set. setting expectations is self care.

🌈 callback example, live from the group chat 🌈

an example of the usage of the callback method might be the following. user code would initiate cryptographic operations, and the engine code would dispatch this operation to hardware, and if the dispatch is successful, then the engine code would call ASYNC_pause_job() to return control to the user code. after that, user code can perform other tasks. when the hardware completes the operation, normally it is detected by a polling function or an interrupt β€” and since the user code set a callback by calling ASYNC_WAIT_CTX_set_callback() previously, then the registered callback will be called. the circle of life πŸ¦‹


πŸ’– cleanup πŸ’–

ASYNC_WAIT_CTX_free() frees up a single ASYNC_WAIT_CTX object. if the argument is NULL, nothing is done. no crash, no drama, just vibes. ✨


⭐ RETURN VALUES ⭐

  • ASYNC_WAIT_CTX_new() returns a pointer to the newly allocated ASYNC_WAIT_CTX or NULL on error.
  • ASYNC_WAIT_CTX_set_wait_fd, ASYNC_WAIT_CTX_get_fd, ASYNC_WAIT_CTX_get_all_fds, ASYNC_WAIT_CTX_get_changed_fds, ASYNC_WAIT_CTX_clear_fd, ASYNC_WAIT_CTX_set_callback, ASYNC_WAIT_CTX_get_callback and ASYNC_WAIT_CTX_set_status all return 1 on success or 0 on error.
  • ASYNC_WAIT_CTX_get_status() returns the engine status.

πŸ’€ NOTES πŸ’€

on Windows platforms, the <openssl/async.h> header is dependent on some of the types customarily made available by including <windows.h>. the application developer is likely to require control over when the latter is included, commonly as one of the first included headers. therefore, it is defined as an application developer's responsibility to include <windows.h> prior to <openssl/async.h>. you got this, I believe in you 🫢


🌸 SEE ALSO 🌸

crypto(7), ASYNC_start_job(3)


πŸŽ€ HISTORY πŸŽ€

  • ASYNC_WAIT_CTX_new(), ASYNC_WAIT_CTX_free(), ASYNC_WAIT_CTX_set_wait_fd(), ASYNC_WAIT_CTX_get_fd(), ASYNC_WAIT_CTX_get_all_fds(), ASYNC_WAIT_CTX_get_changed_fds() and ASYNC_WAIT_CTX_clear_fd() were added in OpenSSL 1.1.0.
  • ASYNC_WAIT_CTX_set_callback(), ASYNC_WAIT_CTX_get_callback(), ASYNC_WAIT_CTX_set_status(), and ASYNC_WAIT_CTX_get_status() were added in OpenSSL 3.0.

and that's only part 1 bestie... πŸ‘€


okay bestie, legal era, but make it cute πŸ’–

Copyright 2016-2024 The OpenSSL Project Authors. All Rights Reserved.

that's it, that's the whole vibe. the OpenSSL girlies said "this is ours" and honestly? periodt. πŸ‘‘


then we got the license moment, and you already know the drill: it's licensed under the Apache License 2.0 β€” literally "the License" in quotes, lowercase the, very official β€” and the rules are the rules, no cap:

  • you may not use this file except in compliance with the License πŸ™ˆ
  • wanna read the whole thing? you can grab a copy in the file LICENSE in the source distribution πŸŽ€
  • or, if you're feeling online today, it's also hanging out at https://www.openssl.org/source/license.html πŸ¦‹
⚠️ fr fr though, non-compliance is not the vibe. read the License before you do literally anything. no cap, no exceptions. πŸ’…

and that's the whole page, bestie!! you made it to the end, main character energy fr fr 🌸✨

little sign-off moment to send you off: this icon of a manual page comes from OpenSSL 3.5.9, and it was dated 2026-09-29 β€” so yeah, she's fresh, she's official, and she's a whole legend. πŸ’–

now go forth and ASYNC_WAIT_CTX responsibly, iykyk πŸ«ΆπŸ§šπŸ’œ

πŸ“¦ where this page comes from