1=pod 2 3=head1 NAME 4 5ASYNC_get_wait_ctx, 6ASYNC_init_thread, ASYNC_cleanup_thread, ASYNC_start_job, ASYNC_pause_job, 7ASYNC_get_current_job, ASYNC_block_pause, ASYNC_unblock_pause, ASYNC_is_capable, 8ASYNC_stack_alloc_fn, ASYNC_stack_free_fn, ASYNC_set_mem_functions, ASYNC_get_mem_functions 9- asynchronous job management functions 10 11=head1 SYNOPSIS 12 13 #include <openssl/async.h> 14 15 int ASYNC_init_thread(size_t max_size, size_t init_size); 16 void ASYNC_cleanup_thread(void); 17 18 int ASYNC_start_job(ASYNC_JOB **job, ASYNC_WAIT_CTX *ctx, int *ret, 19 int (*func)(void *), void *args, size_t size); 20 int ASYNC_pause_job(void); 21 22 ASYNC_JOB *ASYNC_get_current_job(void); 23 ASYNC_WAIT_CTX *ASYNC_get_wait_ctx(ASYNC_JOB *job); 24 void ASYNC_block_pause(void); 25 void ASYNC_unblock_pause(void); 26 27 int ASYNC_is_capable(void); 28 29 typedef void *(*ASYNC_stack_alloc_fn)(size_t *num); 30 typedef void (*ASYNC_stack_free_fn)(void *addr); 31 int ASYNC_set_mem_functions(ASYNC_stack_alloc_fn alloc_fn, 32 ASYNC_stack_free_fn free_fn); 33 void ASYNC_get_mem_functions(ASYNC_stack_alloc_fn *alloc_fn, 34 ASYNC_stack_free_fn *free_fn); 35 36=head1 DESCRIPTION 37 38OpenSSL implements asynchronous capabilities through an B<ASYNC_JOB>. This 39represents code that can be started and executes until some event occurs. At 40that point the code can be paused and control returns to user code until some 41subsequent event indicates that the job can be resumed. It's OpenSSL 42specific implementation of cooperative multitasking. 43 44The creation of an B<ASYNC_JOB> is a relatively expensive operation. Therefore, 45for efficiency reasons, jobs can be created up front and reused many times. They 46are held in a pool until they are needed, at which point they are removed from 47the pool, used, and then returned to the pool when the job completes. If the 48user application is multi-threaded, then ASYNC_init_thread() may be called for 49each thread that will initiate asynchronous jobs. Before 50user code exits per-thread resources need to be cleaned up. This will normally 51occur automatically (see L<OPENSSL_init_crypto(3)>) but may be explicitly 52initiated by using ASYNC_cleanup_thread(). No asynchronous jobs must be 53outstanding for the thread when ASYNC_cleanup_thread() is called. Failing to 54ensure this will result in memory leaks. 55 56The I<max_size> argument limits the number of B<ASYNC_JOB>s that will be held in 57the pool. If I<max_size> is set to 0 then no upper limit is set. When an 58B<ASYNC_JOB> is needed but there are none available in the pool already then one 59will be automatically created, as long as the total of B<ASYNC_JOB>s managed by 60the pool does not exceed I<max_size>. When the pool is first initialised 61I<init_size> B<ASYNC_JOB>s will be created immediately. If ASYNC_init_thread() 62is not called before the pool is first used then it will be called automatically 63with a I<max_size> of 0 (no upper limit) and an I<init_size> of 0 (no 64B<ASYNC_JOB>s created up front). 65 66An asynchronous job is started by calling the ASYNC_start_job() function. 67Initially I<*job> should be NULL. I<ctx> should point to an B<ASYNC_WAIT_CTX> 68object created through the L<ASYNC_WAIT_CTX_new(3)> function. I<ret> should 69point to a location where the return value of the asynchronous function should 70be stored on completion of the job. I<func> represents the function that should 71be started asynchronously. The data pointed to by I<args> and of size I<size> 72will be copied and then passed as an argument to I<func> when the job starts. 73ASYNC_start_job will return one of the following values: 74 75=over 4 76 77=item B<ASYNC_ERR> 78 79An error occurred trying to start the job. Check the OpenSSL error queue (e.g. 80see L<ERR_print_errors(3)>) for more details. 81 82=item B<ASYNC_NO_JOBS> 83 84There are no jobs currently available in the pool. This call can be retried 85again at a later time. 86 87=item B<ASYNC_PAUSE> 88 89The job was successfully started but was "paused" before it completed (see 90ASYNC_pause_job() below). A handle to the job is placed in I<*job>. Other work 91can be performed (if desired) and the job restarted at a later time. To restart 92a job call ASYNC_start_job() again passing the job handle in I<*job>. The 93I<func>, I<args> and I<size> parameters will be ignored when restarting a job. 94When restarting a job ASYNC_start_job() B<must> be called from the same thread 95that the job was originally started from. B<ASYNC_WAIT_CTX> is used to 96know when a job is ready to be restarted. 97 98=item B<ASYNC_FINISH> 99 100The job completed. I<*job> will be NULL and the return value from I<func> will 101be placed in I<*ret>. 102 103=back 104 105At any one time there can be a maximum of one job actively running per thread 106(you can have many that are paused). ASYNC_get_current_job() can be used to get 107a pointer to the currently executing B<ASYNC_JOB>. If no job is currently 108executing then this will return NULL. 109 110If executing within the context of a job (i.e. having been called directly or 111indirectly by the function "func" passed as an argument to ASYNC_start_job()) 112then ASYNC_pause_job() will immediately return control to the calling 113application with B<ASYNC_PAUSE> returned from the ASYNC_start_job() call. A 114subsequent call to ASYNC_start_job passing in the relevant B<ASYNC_JOB> in the 115I<*job> parameter will resume execution from the ASYNC_pause_job() call. If 116ASYNC_pause_job() is called whilst not within the context of a job then no 117action is taken and ASYNC_pause_job() returns immediately. 118 119ASYNC_get_wait_ctx() can be used to get a pointer to the B<ASYNC_WAIT_CTX> 120for the I<job> (see L<ASYNC_WAIT_CTX_new(3)>). 121B<ASYNC_WAIT_CTX>s contain two different ways to notify 122applications that a job is ready to be resumed. One is a "wait" file 123descriptor, and the other is a "callback" mechanism. 124 125The "wait" file descriptor associated with B<ASYNC_WAIT_CTX> is used for 126applications to wait for the file descriptor to be ready for "read" using a 127system function call such as select(2) or poll(2) (being ready for "read" 128indicates 129that the job should be resumed). If no file descriptor is made available then 130an application will have to periodically "poll" the job by attempting to restart 131it to see if it is ready to continue. 132 133B<ASYNC_WAIT_CTX>s also have a "callback" mechanism to notify applications. The 134callback is set by an application, and it will be automatically called when an 135engine completes a cryptography operation, so that the application can resume 136the paused work flow without polling. An engine could be written to look whether 137the callback has been set. If it has then it would use the callback mechanism 138in preference to the file descriptor notifications. If a callback is not set 139then the engine may use file descriptor based notifications. Please note that 140not all engines may support the callback mechanism, so the callback may not be 141used even if it has been set. See ASYNC_WAIT_CTX_new() for more details. 142 143The ASYNC_block_pause() function will prevent the currently active job from 144pausing. The block will remain in place until a subsequent call to 145ASYNC_unblock_pause(). These functions can be nested, e.g. if you call 146ASYNC_block_pause() twice then you must call ASYNC_unblock_pause() twice in 147order to re-enable pausing. If these functions are called while there is no 148currently active job then they have no effect. This functionality can be useful 149to avoid deadlock scenarios. For example during the execution of an B<ASYNC_JOB> 150an application acquires a lock. It then calls some cryptographic function which 151invokes ASYNC_pause_job(). This returns control back to the code that created 152the B<ASYNC_JOB>. If that code then attempts to acquire the same lock before 153resuming the original job then a deadlock can occur. By calling 154ASYNC_block_pause() immediately after acquiring the lock and 155ASYNC_unblock_pause() immediately before releasing it then this situation cannot 156occur. 157 158Some platforms cannot support async operations. The ASYNC_is_capable() function 159can be used to detect whether the current platform is async capable or not. 160 161Custom memory allocation functions are supported for the POSIX platform. 162Custom memory allocation functions allow alternative methods of allocating 163stack memory such as mmap, or using stack memory from the current thread. 164Using an ASYNC_stack_alloc_fn callback also allows manipulation of the stack 165size, which defaults to 32k. 166The stack size can be altered by allocating a stack of a size different to 167the requested size, and passing back the new stack size in the callback's I<*num> 168parameter. 169 170=head1 RETURN VALUES 171 172ASYNC_init_thread returns 1 on success or 0 otherwise. 173 174ASYNC_start_job returns one of B<ASYNC_ERR>, B<ASYNC_NO_JOBS>, B<ASYNC_PAUSE> or 175B<ASYNC_FINISH> as described above. 176 177ASYNC_pause_job returns 0 if an error occurred or 1 on success. If called when 178not within the context of an B<ASYNC_JOB> then this is counted as success so 1 179is returned. 180 181ASYNC_get_current_job returns a pointer to the currently executing B<ASYNC_JOB> 182or NULL if not within the context of a job. 183 184ASYNC_get_wait_ctx() returns a pointer to the B<ASYNC_WAIT_CTX> for the job. 185 186ASYNC_is_capable() returns 1 if the current platform is async capable or 0 187otherwise. 188 189ASYNC_set_mem_functions returns 1 if custom stack allocators are supported by 190the current platform and no allocations have already occurred or 0 otherwise. 191 192=head1 NOTES 193 194On Windows platforms the F<< <openssl/async.h> >> header is dependent on some 195of the types customarily made available by including F<< <windows.h> >>. The 196application developer is likely to require control over when the latter 197is included, commonly as one of the first included headers. Therefore, 198it is defined as an application developer's responsibility to include 199F<< <windows.h> >> prior to F<< <openssl/async.h> >>. 200 201=head1 EXAMPLES 202 203The following example demonstrates how to use most of the core async APIs: 204 205 #ifdef _WIN32 206 # include <windows.h> 207 #endif 208 #include <stdio.h> 209 #include <unistd.h> 210 #include <openssl/async.h> 211 #include <openssl/crypto.h> 212 213 int unique = 0; 214 215 void cleanup(ASYNC_WAIT_CTX *ctx, const void *key, OSSL_ASYNC_FD r, void *vw) 216 { 217 OSSL_ASYNC_FD *w = (OSSL_ASYNC_FD *)vw; 218 219 close(r); 220 close(*w); 221 OPENSSL_free(w); 222 } 223 224 int jobfunc(void *arg) 225 { 226 ASYNC_JOB *currjob; 227 unsigned char *msg; 228 int pipefds[2] = {0, 0}; 229 OSSL_ASYNC_FD *wptr; 230 char buf = 'X'; 231 232 currjob = ASYNC_get_current_job(); 233 if (currjob != NULL) { 234 printf("Executing within a job\n"); 235 } else { 236 printf("Not executing within a job - should not happen\n"); 237 return 0; 238 } 239 240 msg = (unsigned char *)arg; 241 printf("Passed in message is: %s\n", msg); 242 243 /* 244 * Create a way to inform the calling thread when this job is ready 245 * to resume, in this example we're using file descriptors. 246 * For offloading the task to an asynchronous ENGINE it's not necessary, 247 * the ENGINE should handle that internally. 248 */ 249 250 if (pipe(pipefds) != 0) { 251 printf("Failed to create pipe\n"); 252 return 0; 253 } 254 wptr = OPENSSL_malloc(sizeof(OSSL_ASYNC_FD)); 255 if (wptr == NULL) { 256 printf("Failed to malloc\n"); 257 return 0; 258 } 259 *wptr = pipefds[1]; 260 ASYNC_WAIT_CTX_set_wait_fd(ASYNC_get_wait_ctx(currjob), &unique, 261 pipefds[0], wptr, cleanup); 262 263 /* 264 * Normally some external event (like a network read being ready, 265 * disk access being finished, or some hardware offload operation 266 * completing) would cause this to happen at some 267 * later point - but we do it here for demo purposes, i.e. 268 * immediately signalling that the job is ready to be woken up after 269 * we return to main via ASYNC_pause_job(). 270 */ 271 write(pipefds[1], &buf, 1); 272 273 /* 274 * Return control back to main just before calling a blocking 275 * method. The main thread will wait until pipefds[0] is ready 276 * for reading before returning control to this thread. 277 */ 278 ASYNC_pause_job(); 279 280 /* Perform the blocking call (it won't block with this example code) */ 281 read(pipefds[0], &buf, 1); 282 283 printf ("Resumed the job after a pause\n"); 284 285 return 1; 286 } 287 288 int main(void) 289 { 290 ASYNC_JOB *job = NULL; 291 ASYNC_WAIT_CTX *ctx = NULL; 292 int ret; 293 OSSL_ASYNC_FD waitfd; 294 fd_set waitfdset; 295 size_t numfds; 296 unsigned char msg[13] = "Hello world!"; 297 298 printf("Starting...\n"); 299 300 ctx = ASYNC_WAIT_CTX_new(); 301 if (ctx == NULL) { 302 printf("Failed to create ASYNC_WAIT_CTX\n"); 303 abort(); 304 } 305 306 for (;;) { 307 switch (ASYNC_start_job(&job, ctx, &ret, jobfunc, msg, sizeof(msg))) { 308 case ASYNC_ERR: 309 case ASYNC_NO_JOBS: 310 printf("An error occurred\n"); 311 goto end; 312 case ASYNC_PAUSE: 313 printf("Job was paused\n"); 314 break; 315 case ASYNC_FINISH: 316 printf("Job finished with return value %d\n", ret); 317 goto end; 318 } 319 320 /* Get the file descriptor we can use to wait for the job 321 * to be ready to be woken up 322 */ 323 printf("Waiting for the job to be woken up\n"); 324 325 if (!ASYNC_WAIT_CTX_get_all_fds(ctx, NULL, &numfds) 326 || numfds > 1) { 327 printf("Unexpected number of fds\n"); 328 abort(); 329 } 330 ASYNC_WAIT_CTX_get_all_fds(ctx, &waitfd, &numfds); 331 FD_ZERO(&waitfdset); 332 FD_SET(waitfd, &waitfdset); 333 334 /* Wait for the job to be ready for wakeup */ 335 select(waitfd + 1, &waitfdset, NULL, NULL, NULL); 336 } 337 338 end: 339 ASYNC_WAIT_CTX_free(ctx); 340 printf("Finishing\n"); 341 342 return 0; 343 } 344 345The expected output from executing the above example program is: 346 347 Starting... 348 Executing within a job 349 Passed in message is: Hello world! 350 Job was paused 351 Waiting for the job to be woken up 352 Resumed the job after a pause 353 Job finished with return value 1 354 Finishing 355 356=head1 SEE ALSO 357 358L<crypto(7)>, L<ERR_print_errors(3)> 359 360=head1 HISTORY 361 362ASYNC_init_thread, ASYNC_cleanup_thread, 363ASYNC_start_job, ASYNC_pause_job, ASYNC_get_current_job, ASYNC_get_wait_ctx(), 364ASYNC_block_pause(), ASYNC_unblock_pause() and ASYNC_is_capable() were first 365added in OpenSSL 1.1.0. 366ASYNC_set_mem_functions(), ASYNC_get_mem_functions() were added 367in OpenSSL 3.2. 368 369=head1 COPYRIGHT 370 371Copyright 2015-2024 The OpenSSL Project Authors. All Rights Reserved. 372 373Licensed under the Apache License 2.0 (the "License"). You may not use 374this file except in compliance with the License. You can obtain a copy 375in the file LICENSE in the source distribution or at 376L<https://www.openssl.org/source/license.html>. 377 378=cut 379