1e71b7053SJung-uk Kim=pod 2e71b7053SJung-uk Kim 3e71b7053SJung-uk Kim=head1 NAME 4e71b7053SJung-uk Kim 5e71b7053SJung-uk KimASYNC_get_wait_ctx, 6e71b7053SJung-uk KimASYNC_init_thread, ASYNC_cleanup_thread, ASYNC_start_job, ASYNC_pause_job, 7e71b7053SJung-uk KimASYNC_get_current_job, ASYNC_block_pause, ASYNC_unblock_pause, ASYNC_is_capable 8e71b7053SJung-uk Kim- asynchronous job management functions 9e71b7053SJung-uk Kim 10e71b7053SJung-uk Kim=head1 SYNOPSIS 11e71b7053SJung-uk Kim 12e71b7053SJung-uk Kim #include <openssl/async.h> 13e71b7053SJung-uk Kim 14e71b7053SJung-uk Kim int ASYNC_init_thread(size_t max_size, size_t init_size); 15e71b7053SJung-uk Kim void ASYNC_cleanup_thread(void); 16e71b7053SJung-uk Kim 17e71b7053SJung-uk Kim int ASYNC_start_job(ASYNC_JOB **job, ASYNC_WAIT_CTX *ctx, int *ret, 18e71b7053SJung-uk Kim int (*func)(void *), void *args, size_t size); 19e71b7053SJung-uk Kim int ASYNC_pause_job(void); 20e71b7053SJung-uk Kim 21e71b7053SJung-uk Kim ASYNC_JOB *ASYNC_get_current_job(void); 22e71b7053SJung-uk Kim ASYNC_WAIT_CTX *ASYNC_get_wait_ctx(ASYNC_JOB *job); 23e71b7053SJung-uk Kim void ASYNC_block_pause(void); 24e71b7053SJung-uk Kim void ASYNC_unblock_pause(void); 25e71b7053SJung-uk Kim 26e71b7053SJung-uk Kim int ASYNC_is_capable(void); 27e71b7053SJung-uk Kim 28e71b7053SJung-uk Kim=head1 DESCRIPTION 29e71b7053SJung-uk Kim 30*b077aed3SPierre ProncheryOpenSSL implements asynchronous capabilities through an B<ASYNC_JOB>. This 31e71b7053SJung-uk Kimrepresents code that can be started and executes until some event occurs. At 32e71b7053SJung-uk Kimthat point the code can be paused and control returns to user code until some 33e71b7053SJung-uk Kimsubsequent event indicates that the job can be resumed. 34e71b7053SJung-uk Kim 35*b077aed3SPierre ProncheryThe creation of an B<ASYNC_JOB> is a relatively expensive operation. Therefore, 36*b077aed3SPierre Proncheryfor efficiency reasons, jobs can be created up front and reused many times. They 37*b077aed3SPierre Proncheryare held in a pool until they are needed, at which point they are removed from 38*b077aed3SPierre Proncherythe pool, used, and then returned to the pool when the job completes. If the 39*b077aed3SPierre Proncheryuser application is multi-threaded, then ASYNC_init_thread() may be called for 40*b077aed3SPierre Proncheryeach thread that will initiate asynchronous jobs. Before 41e71b7053SJung-uk Kimuser code exits per-thread resources need to be cleaned up. This will normally 42e71b7053SJung-uk Kimoccur automatically (see L<OPENSSL_init_crypto(3)>) but may be explicitly 43e71b7053SJung-uk Kiminitiated by using ASYNC_cleanup_thread(). No asynchronous jobs must be 44e71b7053SJung-uk Kimoutstanding for the thread when ASYNC_cleanup_thread() is called. Failing to 45e71b7053SJung-uk Kimensure this will result in memory leaks. 46e71b7053SJung-uk Kim 47*b077aed3SPierre ProncheryThe I<max_size> argument limits the number of B<ASYNC_JOB>s that will be held in 48*b077aed3SPierre Proncherythe pool. If I<max_size> is set to 0 then no upper limit is set. When an 49*b077aed3SPierre ProncheryB<ASYNC_JOB> is needed but there are none available in the pool already then one 50*b077aed3SPierre Proncherywill be automatically created, as long as the total of B<ASYNC_JOB>s managed by 51*b077aed3SPierre Proncherythe pool does not exceed I<max_size>. When the pool is first initialised 52*b077aed3SPierre ProncheryI<init_size> B<ASYNC_JOB>s will be created immediately. If ASYNC_init_thread() 53*b077aed3SPierre Proncheryis not called before the pool is first used then it will be called automatically 54*b077aed3SPierre Proncherywith a I<max_size> of 0 (no upper limit) and an I<init_size> of 0 (no 55*b077aed3SPierre ProncheryB<ASYNC_JOB>s created up front). 56e71b7053SJung-uk Kim 57e71b7053SJung-uk KimAn asynchronous job is started by calling the ASYNC_start_job() function. 58*b077aed3SPierre ProncheryInitially I<*job> should be NULL. I<ctx> should point to an B<ASYNC_WAIT_CTX> 59*b077aed3SPierre Proncheryobject created through the L<ASYNC_WAIT_CTX_new(3)> function. I<ret> should 60e71b7053SJung-uk Kimpoint to a location where the return value of the asynchronous function should 61*b077aed3SPierre Proncherybe stored on completion of the job. I<func> represents the function that should 62*b077aed3SPierre Proncherybe started asynchronously. The data pointed to by I<args> and of size I<size> 63*b077aed3SPierre Proncherywill be copied and then passed as an argument to I<func> when the job starts. 64e71b7053SJung-uk KimASYNC_start_job will return one of the following values: 65e71b7053SJung-uk Kim 66e71b7053SJung-uk Kim=over 4 67e71b7053SJung-uk Kim 68e71b7053SJung-uk Kim=item B<ASYNC_ERR> 69e71b7053SJung-uk Kim 70e71b7053SJung-uk KimAn error occurred trying to start the job. Check the OpenSSL error queue (e.g. 71e71b7053SJung-uk Kimsee L<ERR_print_errors(3)>) for more details. 72e71b7053SJung-uk Kim 73e71b7053SJung-uk Kim=item B<ASYNC_NO_JOBS> 74e71b7053SJung-uk Kim 75e71b7053SJung-uk KimThere are no jobs currently available in the pool. This call can be retried 76e71b7053SJung-uk Kimagain at a later time. 77e71b7053SJung-uk Kim 78e71b7053SJung-uk Kim=item B<ASYNC_PAUSE> 79e71b7053SJung-uk Kim 80e71b7053SJung-uk KimThe job was successfully started but was "paused" before it completed (see 81*b077aed3SPierre ProncheryASYNC_pause_job() below). A handle to the job is placed in I<*job>. Other work 82e71b7053SJung-uk Kimcan be performed (if desired) and the job restarted at a later time. To restart 83*b077aed3SPierre Proncherya job call ASYNC_start_job() again passing the job handle in I<*job>. The 84*b077aed3SPierre ProncheryI<func>, I<args> and I<size> parameters will be ignored when restarting a job. 85e71b7053SJung-uk KimWhen restarting a job ASYNC_start_job() B<must> be called from the same thread 86e71b7053SJung-uk Kimthat the job was originally started from. 87e71b7053SJung-uk Kim 88e71b7053SJung-uk Kim=item B<ASYNC_FINISH> 89e71b7053SJung-uk Kim 90*b077aed3SPierre ProncheryThe job completed. I<*job> will be NULL and the return value from I<func> will 91*b077aed3SPierre Proncherybe placed in I<*ret>. 92e71b7053SJung-uk Kim 93e71b7053SJung-uk Kim=back 94e71b7053SJung-uk Kim 95e71b7053SJung-uk KimAt any one time there can be a maximum of one job actively running per thread 96e71b7053SJung-uk Kim(you can have many that are paused). ASYNC_get_current_job() can be used to get 97*b077aed3SPierre Proncherya pointer to the currently executing B<ASYNC_JOB>. If no job is currently 98*b077aed3SPierre Proncheryexecuting then this will return NULL. 99e71b7053SJung-uk Kim 100e71b7053SJung-uk KimIf executing within the context of a job (i.e. having been called directly or 101e71b7053SJung-uk Kimindirectly by the function "func" passed as an argument to ASYNC_start_job()) 102e71b7053SJung-uk Kimthen ASYNC_pause_job() will immediately return control to the calling 103*b077aed3SPierre Proncheryapplication with B<ASYNC_PAUSE> returned from the ASYNC_start_job() call. A 104*b077aed3SPierre Proncherysubsequent call to ASYNC_start_job passing in the relevant B<ASYNC_JOB> in the 105*b077aed3SPierre ProncheryI<*job> parameter will resume execution from the ASYNC_pause_job() call. If 106e71b7053SJung-uk KimASYNC_pause_job() is called whilst not within the context of a job then no 107e71b7053SJung-uk Kimaction is taken and ASYNC_pause_job() returns immediately. 108e71b7053SJung-uk Kim 109*b077aed3SPierre ProncheryASYNC_get_wait_ctx() can be used to get a pointer to the B<ASYNC_WAIT_CTX> 110*b077aed3SPierre Proncheryfor the I<job>. B<ASYNC_WAIT_CTX>s contain two different ways to notify 111*b077aed3SPierre Proncheryapplications that a job is ready to be resumed. One is a "wait" file 112*b077aed3SPierre Proncherydescriptor, and the other is a "callback" mechanism. 113e71b7053SJung-uk Kim 114*b077aed3SPierre ProncheryThe "wait" file descriptor associated with B<ASYNC_WAIT_CTX> is used for 115*b077aed3SPierre Proncheryapplications to wait for the file descriptor to be ready for "read" using a 116*b077aed3SPierre Proncherysystem function call such as select or poll (being ready for "read" indicates 117*b077aed3SPierre Proncherythat the job should be resumed). If no file descriptor is made available then 118*b077aed3SPierre Proncheryan application will have to periodically "poll" the job by attempting to restart 119*b077aed3SPierre Proncheryit to see if it is ready to continue. 120*b077aed3SPierre Pronchery 121*b077aed3SPierre ProncheryB<ASYNC_WAIT_CTX>s also have a "callback" mechanism to notify applications. The 122*b077aed3SPierre Proncherycallback is set by an application, and it will be automatically called when an 123*b077aed3SPierre Proncheryengine completes a cryptography operation, so that the application can resume 124*b077aed3SPierre Proncherythe paused work flow without polling. An engine could be written to look whether 125*b077aed3SPierre Proncherythe callback has been set. If it has then it would use the callback mechanism 126*b077aed3SPierre Proncheryin preference to the file descriptor notifications. If a callback is not set 127*b077aed3SPierre Proncherythen the engine may use file descriptor based notifications. Please note that 128*b077aed3SPierre Proncherynot all engines may support the callback mechanism, so the callback may not be 129*b077aed3SPierre Proncheryused even if it has been set. See ASYNC_WAIT_CTX_new() for more details. 130e71b7053SJung-uk Kim 131e71b7053SJung-uk KimThe ASYNC_block_pause() function will prevent the currently active job from 132e71b7053SJung-uk Kimpausing. The block will remain in place until a subsequent call to 133e71b7053SJung-uk KimASYNC_unblock_pause(). These functions can be nested, e.g. if you call 134e71b7053SJung-uk KimASYNC_block_pause() twice then you must call ASYNC_unblock_pause() twice in 135e71b7053SJung-uk Kimorder to re-enable pausing. If these functions are called while there is no 136e71b7053SJung-uk Kimcurrently active job then they have no effect. This functionality can be useful 137*b077aed3SPierre Proncheryto avoid deadlock scenarios. For example during the execution of an B<ASYNC_JOB> 138*b077aed3SPierre Proncheryan application acquires a lock. It then calls some cryptographic function which 139e71b7053SJung-uk Kiminvokes ASYNC_pause_job(). This returns control back to the code that created 140*b077aed3SPierre Proncherythe B<ASYNC_JOB>. If that code then attempts to acquire the same lock before 141e71b7053SJung-uk Kimresuming the original job then a deadlock can occur. By calling 142e71b7053SJung-uk KimASYNC_block_pause() immediately after acquiring the lock and 143e71b7053SJung-uk KimASYNC_unblock_pause() immediately before releasing it then this situation cannot 144e71b7053SJung-uk Kimoccur. 145e71b7053SJung-uk Kim 146e71b7053SJung-uk KimSome platforms cannot support async operations. The ASYNC_is_capable() function 147e71b7053SJung-uk Kimcan be used to detect whether the current platform is async capable or not. 148e71b7053SJung-uk Kim 149e71b7053SJung-uk Kim=head1 RETURN VALUES 150e71b7053SJung-uk Kim 151e71b7053SJung-uk KimASYNC_init_thread returns 1 on success or 0 otherwise. 152e71b7053SJung-uk Kim 153*b077aed3SPierre ProncheryASYNC_start_job returns one of B<ASYNC_ERR>, B<ASYNC_NO_JOBS>, B<ASYNC_PAUSE> or 154*b077aed3SPierre ProncheryB<ASYNC_FINISH> as described above. 155e71b7053SJung-uk Kim 156e71b7053SJung-uk KimASYNC_pause_job returns 0 if an error occurred or 1 on success. If called when 157*b077aed3SPierre Proncherynot within the context of an B<ASYNC_JOB> then this is counted as success so 1 158*b077aed3SPierre Proncheryis returned. 159e71b7053SJung-uk Kim 160*b077aed3SPierre ProncheryASYNC_get_current_job returns a pointer to the currently executing B<ASYNC_JOB> 161*b077aed3SPierre Proncheryor NULL if not within the context of a job. 162e71b7053SJung-uk Kim 163*b077aed3SPierre ProncheryASYNC_get_wait_ctx() returns a pointer to the B<ASYNC_WAIT_CTX> for the job. 164e71b7053SJung-uk Kim 165e71b7053SJung-uk KimASYNC_is_capable() returns 1 if the current platform is async capable or 0 166e71b7053SJung-uk Kimotherwise. 167e71b7053SJung-uk Kim 168e71b7053SJung-uk Kim=head1 NOTES 169e71b7053SJung-uk Kim 170*b077aed3SPierre ProncheryOn Windows platforms the F<< <openssl/async.h> >> header is dependent on some 171*b077aed3SPierre Proncheryof the types customarily made available by including F<< <windows.h> >>. The 172e71b7053SJung-uk Kimapplication developer is likely to require control over when the latter 17358f35182SJung-uk Kimis included, commonly as one of the first included headers. Therefore, 174e71b7053SJung-uk Kimit is defined as an application developer's responsibility to include 175*b077aed3SPierre ProncheryF<< <windows.h> >> prior to F<< <openssl/async.h> >>. 176e71b7053SJung-uk Kim 177da327cd2SJung-uk Kim=head1 EXAMPLES 178e71b7053SJung-uk Kim 179e71b7053SJung-uk KimThe following example demonstrates how to use most of the core async APIs: 180e71b7053SJung-uk Kim 181e71b7053SJung-uk Kim #ifdef _WIN32 182e71b7053SJung-uk Kim # include <windows.h> 183e71b7053SJung-uk Kim #endif 184e71b7053SJung-uk Kim #include <stdio.h> 185e71b7053SJung-uk Kim #include <unistd.h> 186e71b7053SJung-uk Kim #include <openssl/async.h> 187e71b7053SJung-uk Kim #include <openssl/crypto.h> 188e71b7053SJung-uk Kim 189e71b7053SJung-uk Kim int unique = 0; 190e71b7053SJung-uk Kim 191e71b7053SJung-uk Kim void cleanup(ASYNC_WAIT_CTX *ctx, const void *key, OSSL_ASYNC_FD r, void *vw) 192e71b7053SJung-uk Kim { 193e71b7053SJung-uk Kim OSSL_ASYNC_FD *w = (OSSL_ASYNC_FD *)vw; 194e71b7053SJung-uk Kim 195e71b7053SJung-uk Kim close(r); 196e71b7053SJung-uk Kim close(*w); 197e71b7053SJung-uk Kim OPENSSL_free(w); 198e71b7053SJung-uk Kim } 199e71b7053SJung-uk Kim 200e71b7053SJung-uk Kim int jobfunc(void *arg) 201e71b7053SJung-uk Kim { 202e71b7053SJung-uk Kim ASYNC_JOB *currjob; 203e71b7053SJung-uk Kim unsigned char *msg; 204e71b7053SJung-uk Kim int pipefds[2] = {0, 0}; 205e71b7053SJung-uk Kim OSSL_ASYNC_FD *wptr; 206e71b7053SJung-uk Kim char buf = 'X'; 207e71b7053SJung-uk Kim 208e71b7053SJung-uk Kim currjob = ASYNC_get_current_job(); 209e71b7053SJung-uk Kim if (currjob != NULL) { 210e71b7053SJung-uk Kim printf("Executing within a job\n"); 211e71b7053SJung-uk Kim } else { 212e71b7053SJung-uk Kim printf("Not executing within a job - should not happen\n"); 213e71b7053SJung-uk Kim return 0; 214e71b7053SJung-uk Kim } 215e71b7053SJung-uk Kim 216e71b7053SJung-uk Kim msg = (unsigned char *)arg; 217e71b7053SJung-uk Kim printf("Passed in message is: %s\n", msg); 218e71b7053SJung-uk Kim 219e71b7053SJung-uk Kim if (pipe(pipefds) != 0) { 220e71b7053SJung-uk Kim printf("Failed to create pipe\n"); 221e71b7053SJung-uk Kim return 0; 222e71b7053SJung-uk Kim } 223e71b7053SJung-uk Kim wptr = OPENSSL_malloc(sizeof(OSSL_ASYNC_FD)); 224e71b7053SJung-uk Kim if (wptr == NULL) { 225e71b7053SJung-uk Kim printf("Failed to malloc\n"); 226e71b7053SJung-uk Kim return 0; 227e71b7053SJung-uk Kim } 228e71b7053SJung-uk Kim *wptr = pipefds[1]; 229e71b7053SJung-uk Kim ASYNC_WAIT_CTX_set_wait_fd(ASYNC_get_wait_ctx(currjob), &unique, 230e71b7053SJung-uk Kim pipefds[0], wptr, cleanup); 231e71b7053SJung-uk Kim 232e71b7053SJung-uk Kim /* 233e71b7053SJung-uk Kim * Normally some external event would cause this to happen at some 234e71b7053SJung-uk Kim * later point - but we do it here for demo purposes, i.e. 235e71b7053SJung-uk Kim * immediately signalling that the job is ready to be woken up after 236e71b7053SJung-uk Kim * we return to main via ASYNC_pause_job(). 237e71b7053SJung-uk Kim */ 238e71b7053SJung-uk Kim write(pipefds[1], &buf, 1); 239e71b7053SJung-uk Kim 240e71b7053SJung-uk Kim /* Return control back to main */ 241e71b7053SJung-uk Kim ASYNC_pause_job(); 242e71b7053SJung-uk Kim 243e71b7053SJung-uk Kim /* Clear the wake signal */ 244e71b7053SJung-uk Kim read(pipefds[0], &buf, 1); 245e71b7053SJung-uk Kim 246e71b7053SJung-uk Kim printf ("Resumed the job after a pause\n"); 247e71b7053SJung-uk Kim 248e71b7053SJung-uk Kim return 1; 249e71b7053SJung-uk Kim } 250e71b7053SJung-uk Kim 251e71b7053SJung-uk Kim int main(void) 252e71b7053SJung-uk Kim { 253e71b7053SJung-uk Kim ASYNC_JOB *job = NULL; 254e71b7053SJung-uk Kim ASYNC_WAIT_CTX *ctx = NULL; 255e71b7053SJung-uk Kim int ret; 256e71b7053SJung-uk Kim OSSL_ASYNC_FD waitfd; 257e71b7053SJung-uk Kim fd_set waitfdset; 258e71b7053SJung-uk Kim size_t numfds; 259e71b7053SJung-uk Kim unsigned char msg[13] = "Hello world!"; 260e71b7053SJung-uk Kim 261e71b7053SJung-uk Kim printf("Starting...\n"); 262e71b7053SJung-uk Kim 263e71b7053SJung-uk Kim ctx = ASYNC_WAIT_CTX_new(); 264e71b7053SJung-uk Kim if (ctx == NULL) { 265e71b7053SJung-uk Kim printf("Failed to create ASYNC_WAIT_CTX\n"); 266e71b7053SJung-uk Kim abort(); 267e71b7053SJung-uk Kim } 268e71b7053SJung-uk Kim 269e71b7053SJung-uk Kim for (;;) { 270e71b7053SJung-uk Kim switch (ASYNC_start_job(&job, ctx, &ret, jobfunc, msg, sizeof(msg))) { 271e71b7053SJung-uk Kim case ASYNC_ERR: 272e71b7053SJung-uk Kim case ASYNC_NO_JOBS: 273e71b7053SJung-uk Kim printf("An error occurred\n"); 274e71b7053SJung-uk Kim goto end; 275e71b7053SJung-uk Kim case ASYNC_PAUSE: 276e71b7053SJung-uk Kim printf("Job was paused\n"); 277e71b7053SJung-uk Kim break; 278e71b7053SJung-uk Kim case ASYNC_FINISH: 279e71b7053SJung-uk Kim printf("Job finished with return value %d\n", ret); 280e71b7053SJung-uk Kim goto end; 281e71b7053SJung-uk Kim } 282e71b7053SJung-uk Kim 283e71b7053SJung-uk Kim /* Wait for the job to be woken */ 284e71b7053SJung-uk Kim printf("Waiting for the job to be woken up\n"); 285e71b7053SJung-uk Kim 286e71b7053SJung-uk Kim if (!ASYNC_WAIT_CTX_get_all_fds(ctx, NULL, &numfds) 287e71b7053SJung-uk Kim || numfds > 1) { 288e71b7053SJung-uk Kim printf("Unexpected number of fds\n"); 289e71b7053SJung-uk Kim abort(); 290e71b7053SJung-uk Kim } 291e71b7053SJung-uk Kim ASYNC_WAIT_CTX_get_all_fds(ctx, &waitfd, &numfds); 292e71b7053SJung-uk Kim FD_ZERO(&waitfdset); 293e71b7053SJung-uk Kim FD_SET(waitfd, &waitfdset); 294e71b7053SJung-uk Kim select(waitfd + 1, &waitfdset, NULL, NULL, NULL); 295e71b7053SJung-uk Kim } 296e71b7053SJung-uk Kim 297e71b7053SJung-uk Kim end: 298e71b7053SJung-uk Kim ASYNC_WAIT_CTX_free(ctx); 299e71b7053SJung-uk Kim printf("Finishing\n"); 300e71b7053SJung-uk Kim 301e71b7053SJung-uk Kim return 0; 302e71b7053SJung-uk Kim } 303e71b7053SJung-uk Kim 304e71b7053SJung-uk KimThe expected output from executing the above example program is: 305e71b7053SJung-uk Kim 306e71b7053SJung-uk Kim Starting... 307e71b7053SJung-uk Kim Executing within a job 308e71b7053SJung-uk Kim Passed in message is: Hello world! 309e71b7053SJung-uk Kim Job was paused 310e71b7053SJung-uk Kim Waiting for the job to be woken up 311e71b7053SJung-uk Kim Resumed the job after a pause 312e71b7053SJung-uk Kim Job finished with return value 1 313e71b7053SJung-uk Kim Finishing 314e71b7053SJung-uk Kim 315e71b7053SJung-uk Kim=head1 SEE ALSO 316e71b7053SJung-uk Kim 317e71b7053SJung-uk KimL<crypto(7)>, L<ERR_print_errors(3)> 318e71b7053SJung-uk Kim 319e71b7053SJung-uk Kim=head1 HISTORY 320e71b7053SJung-uk Kim 321e71b7053SJung-uk KimASYNC_init_thread, ASYNC_cleanup_thread, 322e71b7053SJung-uk KimASYNC_start_job, ASYNC_pause_job, ASYNC_get_current_job, ASYNC_get_wait_ctx(), 323e71b7053SJung-uk KimASYNC_block_pause(), ASYNC_unblock_pause() and ASYNC_is_capable() were first 3246935a639SJung-uk Kimadded in OpenSSL 1.1.0. 325e71b7053SJung-uk Kim 326e71b7053SJung-uk Kim=head1 COPYRIGHT 327e71b7053SJung-uk Kim 328*b077aed3SPierre ProncheryCopyright 2015-2021 The OpenSSL Project Authors. All Rights Reserved. 329e71b7053SJung-uk Kim 330*b077aed3SPierre ProncheryLicensed under the Apache License 2.0 (the "License"). You may not use 331e71b7053SJung-uk Kimthis file except in compliance with the License. You can obtain a copy 332e71b7053SJung-uk Kimin the file LICENSE in the source distribution or at 333e71b7053SJung-uk KimL<https://www.openssl.org/source/license.html>. 334e71b7053SJung-uk Kim 335e71b7053SJung-uk Kim=cut 336