1 =pod 2 3 =head1 NAME 4 5 ASYNC_get_wait_ctx, 6 ASYNC_init_thread, ASYNC_cleanup_thread, ASYNC_start_job, ASYNC_pause_job, 7 ASYNC_get_current_job, ASYNC_block_pause, ASYNC_unblock_pause, ASYNC_is_capable, 8 ASYNC_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 38 OpenSSL implements asynchronous capabilities through an B<ASYNC_JOB>. This 39 represents code that can be started and executes until some event occurs. At 40 that point the code can be paused and control returns to user code until some 41 subsequent event indicates that the job can be resumed. It's OpenSSL 42 specific implementation of cooperative multitasking. 43 44 The creation of an B<ASYNC_JOB> is a relatively expensive operation. Therefore, 45 for efficiency reasons, jobs can be created up front and reused many times. They 46 are held in a pool until they are needed, at which point they are removed from 47 the pool, used, and then returned to the pool when the job completes. If the 48 user application is multi-threaded, then ASYNC_init_thread() may be called for 49 each thread that will initiate asynchronous jobs. Before 50 user code exits per-thread resources need to be cleaned up. This will normally 51 occur automatically (see L<OPENSSL_init_crypto(3)>) but may be explicitly 52 initiated by using ASYNC_cleanup_thread(). No asynchronous jobs must be 53 outstanding for the thread when ASYNC_cleanup_thread() is called. Failing to 54 ensure this will result in memory leaks. 55 56 The I<max_size> argument limits the number of B<ASYNC_JOB>s that will be held in 57 the pool. If I<max_size> is set to 0 then no upper limit is set. When an 58 B<ASYNC_JOB> is needed but there are none available in the pool already then one 59 will be automatically created, as long as the total of B<ASYNC_JOB>s managed by 60 the pool does not exceed I<max_size>. When the pool is first initialised 61 I<init_size> B<ASYNC_JOB>s will be created immediately. If ASYNC_init_thread() 62 is not called before the pool is first used then it will be called automatically 63 with a I<max_size> of 0 (no upper limit) and an I<init_size> of 0 (no 64 B<ASYNC_JOB>s created up front). 65 66 An asynchronous job is started by calling the ASYNC_start_job() function. 67 Initially I<*job> should be NULL. I<ctx> should point to an B<ASYNC_WAIT_CTX> 68 object created through the L<ASYNC_WAIT_CTX_new(3)> function. I<ret> should 69 point to a location where the return value of the asynchronous function should 70 be stored on completion of the job. I<func> represents the function that should 71 be started asynchronously. The data pointed to by I<args> and of size I<size> 72 will be copied and then passed as an argument to I<func> when the job starts. 73 ASYNC_start_job will return one of the following values: 74 75 =over 4 76 77 =item B<ASYNC_ERR> 78 79 An error occurred trying to start the job. Check the OpenSSL error queue (e.g. 80 see L<ERR_print_errors(3)>) for more details. 81 82 =item B<ASYNC_NO_JOBS> 83 84 There are no jobs currently available in the pool. This call can be retried 85 again at a later time. 86 87 =item B<ASYNC_PAUSE> 88 89 The job was successfully started but was "paused" before it completed (see 90 ASYNC_pause_job() below). A handle to the job is placed in I<*job>. Other work 91 can be performed (if desired) and the job restarted at a later time. To restart 92 a job call ASYNC_start_job() again passing the job handle in I<*job>. The 93 I<func>, I<args> and I<size> parameters will be ignored when restarting a job. 94 When restarting a job ASYNC_start_job() B<must> be called from the same thread 95 that the job was originally started from. B<ASYNC_WAIT_CTX> is used to 96 know when a job is ready to be restarted. 97 98 =item B<ASYNC_FINISH> 99 100 The job completed. I<*job> will be NULL and the return value from I<func> will 101 be placed in I<*ret>. 102 103 =back 104 105 At 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 107 a pointer to the currently executing B<ASYNC_JOB>. If no job is currently 108 executing then this will return NULL. 109 110 If executing within the context of a job (i.e. having been called directly or 111 indirectly by the function "func" passed as an argument to ASYNC_start_job()) 112 then ASYNC_pause_job() will immediately return control to the calling 113 application with B<ASYNC_PAUSE> returned from the ASYNC_start_job() call. A 114 subsequent call to ASYNC_start_job passing in the relevant B<ASYNC_JOB> in the 115 I<*job> parameter will resume execution from the ASYNC_pause_job() call. If 116 ASYNC_pause_job() is called whilst not within the context of a job then no 117 action is taken and ASYNC_pause_job() returns immediately. 118 119 ASYNC_get_wait_ctx() can be used to get a pointer to the B<ASYNC_WAIT_CTX> 120 for the I<job> (see L<ASYNC_WAIT_CTX_new(3)>). 121 B<ASYNC_WAIT_CTX>s contain two different ways to notify 122 applications that a job is ready to be resumed. One is a "wait" file 123 descriptor, and the other is a "callback" mechanism. 124 125 The "wait" file descriptor associated with B<ASYNC_WAIT_CTX> is used for 126 applications to wait for the file descriptor to be ready for "read" using a 127 system function call such as select(2) or poll(2) (being ready for "read" 128 indicates 129 that the job should be resumed). If no file descriptor is made available then 130 an application will have to periodically "poll" the job by attempting to restart 131 it to see if it is ready to continue. 132 133 B<ASYNC_WAIT_CTX>s also have a "callback" mechanism to notify applications. The 134 callback is set by an application, and it will be automatically called when an 135 engine completes a cryptography operation, so that the application can resume 136 the paused work flow without polling. An engine could be written to look whether 137 the callback has been set. If it has then it would use the callback mechanism 138 in preference to the file descriptor notifications. If a callback is not set 139 then the engine may use file descriptor based notifications. Please note that 140 not all engines may support the callback mechanism, so the callback may not be 141 used even if it has been set. See ASYNC_WAIT_CTX_new() for more details. 142 143 The ASYNC_block_pause() function will prevent the currently active job from 144 pausing. The block will remain in place until a subsequent call to 145 ASYNC_unblock_pause(). These functions can be nested, e.g. if you call 146 ASYNC_block_pause() twice then you must call ASYNC_unblock_pause() twice in 147 order to re-enable pausing. If these functions are called while there is no 148 currently active job then they have no effect. This functionality can be useful 149 to avoid deadlock scenarios. For example during the execution of an B<ASYNC_JOB> 150 an application acquires a lock. It then calls some cryptographic function which 151 invokes ASYNC_pause_job(). This returns control back to the code that created 152 the B<ASYNC_JOB>. If that code then attempts to acquire the same lock before 153 resuming the original job then a deadlock can occur. By calling 154 ASYNC_block_pause() immediately after acquiring the lock and 155 ASYNC_unblock_pause() immediately before releasing it then this situation cannot 156 occur. 157 158 Some platforms cannot support async operations. The ASYNC_is_capable() function 159 can be used to detect whether the current platform is async capable or not. 160 161 Custom memory allocation functions are supported for the POSIX platform. 162 Custom memory allocation functions allow alternative methods of allocating 163 stack memory such as mmap, or using stack memory from the current thread. 164 Using an ASYNC_stack_alloc_fn callback also allows manipulation of the stack 165 size, which defaults to 32k. 166 The stack size can be altered by allocating a stack of a size different to 167 the requested size, and passing back the new stack size in the callback's I<*num> 168 parameter. 169 170 =head1 RETURN VALUES 171 172 ASYNC_init_thread returns 1 on success or 0 otherwise. 173 174 ASYNC_start_job returns one of B<ASYNC_ERR>, B<ASYNC_NO_JOBS>, B<ASYNC_PAUSE> or 175 B<ASYNC_FINISH> as described above. 176 177 ASYNC_pause_job returns 0 if an error occurred or 1 on success. If called when 178 not within the context of an B<ASYNC_JOB> then this is counted as success so 1 179 is returned. 180 181 ASYNC_get_current_job returns a pointer to the currently executing B<ASYNC_JOB> 182 or NULL if not within the context of a job. 183 184 ASYNC_get_wait_ctx() returns a pointer to the B<ASYNC_WAIT_CTX> for the job. 185 186 ASYNC_is_capable() returns 1 if the current platform is async capable or 0 187 otherwise. 188 189 ASYNC_set_mem_functions returns 1 if custom stack allocators are supported by 190 the current platform and no allocations have already occurred or 0 otherwise. 191 192 =head1 NOTES 193 194 On Windows platforms the F<< <openssl/async.h> >> header is dependent on some 195 of the types customarily made available by including F<< <windows.h> >>. The 196 application developer is likely to require control over when the latter 197 is included, commonly as one of the first included headers. Therefore, 198 it is defined as an application developer's responsibility to include 199 F<< <windows.h> >> prior to F<< <openssl/async.h> >>. 200 201 =head1 EXAMPLES 202 203 The 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 345 The 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 358 L<crypto(7)>, L<ERR_print_errors(3)> 359 360 =head1 HISTORY 361 362 ASYNC_init_thread, ASYNC_cleanup_thread, 363 ASYNC_start_job, ASYNC_pause_job, ASYNC_get_current_job, ASYNC_get_wait_ctx(), 364 ASYNC_block_pause(), ASYNC_unblock_pause() and ASYNC_is_capable() were first 365 added in OpenSSL 1.1.0. 366 ASYNC_set_mem_functions(), ASYNC_get_mem_functions() were added 367 in OpenSSL 3.2. 368 369 =head1 COPYRIGHT 370 371 Copyright 2015-2024 The OpenSSL Project Authors. All Rights Reserved. 372 373 Licensed under the Apache License 2.0 (the "License"). You may not use 374 this file except in compliance with the License. You can obtain a copy 375 in the file LICENSE in the source distribution or at 376 L<https://www.openssl.org/source/license.html>. 377 378 =cut 379