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