Initial Async notify code changes
[openssl.git] / crypto / async / async.c
1 /* crypto/async/async.c */
2 /*
3  * Written by Matt Caswell (matt@openssl.org) for the OpenSSL project.
4  */
5 /* ====================================================================
6  * Copyright (c) 2015 The OpenSSL Project.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. All advertising materials mentioning features or use of this
21  *    software must display the following acknowledgment:
22  *    "This product includes software developed by the OpenSSL Project
23  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
24  *
25  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
26  *    endorse or promote products derived from this software without
27  *    prior written permission. For written permission, please contact
28  *    licensing@OpenSSL.org.
29  *
30  * 5. Products derived from this software may not be called "OpenSSL"
31  *    nor may "OpenSSL" appear in their names without prior written
32  *    permission of the OpenSSL Project.
33  *
34  * 6. Redistributions of any form whatsoever must retain the following
35  *    acknowledgment:
36  *    "This product includes software developed by the OpenSSL Project
37  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
38  *
39  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
40  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
41  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
43  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
45  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
48  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
50  * OF THE POSSIBILITY OF SUCH DAMAGE.
51  * ====================================================================
52  */
53
54 #include <openssl/crypto.h>
55 #include <openssl/async.h>
56 #include <string.h>
57 #include "async_locl.h"
58
59 #define ASYNC_JOB_RUNNING   0
60 #define ASYNC_JOB_PAUSING   1
61 #define ASYNC_JOB_PAUSED    2
62 #define ASYNC_JOB_STOPPING  3
63
64 static size_t pool_max_size = 0;
65 static size_t curr_size = 0;
66
67 DECLARE_STACK_OF(ASYNC_JOB)
68 static STACK_OF(ASYNC_JOB) *pool = NULL;
69
70
71 static ASYNC_CTX *ASYNC_CTX_new(void)
72 {
73     ASYNC_CTX *nctx = NULL;
74
75     if(!(nctx = OPENSSL_malloc(sizeof (ASYNC_CTX)))) {
76         /* Error here */
77         goto err;
78     }
79
80     ASYNC_FIBRE_init_dispatcher(&nctx->dispatcher);
81     nctx->currjob = NULL;
82     if(!ASYNC_set_ctx(nctx))
83         goto err;
84
85     return nctx;
86 err:
87     if(nctx) {
88         OPENSSL_free(nctx);
89     }
90
91     return NULL;
92 }
93
94 static int ASYNC_CTX_free(void)
95 {
96     if(ASYNC_get_ctx()) {
97         OPENSSL_free(ASYNC_get_ctx());
98     }
99
100     if(!ASYNC_set_ctx(NULL))
101         return 0;
102
103     return 1;
104 }
105
106 static ASYNC_JOB *ASYNC_JOB_new(void)
107 {
108     ASYNC_JOB *job = NULL;
109     int pipefds[2];
110
111     if(!(job = OPENSSL_malloc(sizeof (ASYNC_JOB)))) {
112         return NULL;
113     }
114
115     if(!async_pipe(pipefds)) {
116         OPENSSL_free(job);
117         return NULL;
118     }
119
120     job->wake_set = 0;
121     job->wait_fd = pipefds[0];
122     job->wake_fd = pipefds[1];
123
124     job->status = ASYNC_JOB_RUNNING;
125     job->funcargs = NULL;
126
127     return job;
128 }
129
130 static void ASYNC_JOB_free(ASYNC_JOB *job)
131 {
132     if(job) {
133         if(job->funcargs)
134             OPENSSL_free(job->funcargs);
135         ASYNC_FIBRE_free(&job->fibrectx);
136         OPENSSL_free(job);
137     }
138 }
139
140 static ASYNC_JOB *async_get_pool_job(void) {
141     ASYNC_JOB *job;
142
143     if (pool == NULL) {
144         /*
145          * Pool has not been initialised, so init with the defaults, i.e.
146          * global pool, with no max size and no pre-created jobs
147          */
148         if (ASYNC_init_pool(0, 0, 0) == 0)
149             return NULL;
150     }
151
152     job = sk_ASYNC_JOB_pop(pool);
153     if (job == NULL) {
154         /* Pool is empty */
155         if (pool_max_size && curr_size >= pool_max_size) {
156             /* Pool is at max size. We cannot continue */
157             return NULL;
158         }
159         job = ASYNC_JOB_new();
160         if (job) {
161             ASYNC_FIBRE_makecontext(&job->fibrectx);
162             curr_size++;
163         }
164     }
165     return job;
166 }
167
168 static void async_release_job(ASYNC_JOB *job) {
169     if(job->funcargs)
170         OPENSSL_free(job->funcargs);
171     job->funcargs = NULL;
172     /* Ignore error return */
173     sk_ASYNC_JOB_push(pool, job);
174 }
175
176 void ASYNC_start_func(void)
177 {
178     ASYNC_JOB *job;
179
180     while (1) {
181         /* Run the job */
182         job = ASYNC_get_ctx()->currjob;
183         job->ret = job->func(job->funcargs);
184
185         /* Stop the job */
186         job->status = ASYNC_JOB_STOPPING;
187         if(!ASYNC_FIBRE_swapcontext(&job->fibrectx,
188                                     &ASYNC_get_ctx()->dispatcher, 1)) {
189             /*
190              * Should not happen. Getting here will close the thread...can't do much
191              * about it
192              */
193         }
194     }
195 }
196
197 int ASYNC_start_job(ASYNC_JOB **job, int *ret, int (*func)(void *),
198                          void *args, size_t size)
199 {
200     if(ASYNC_get_ctx() || !ASYNC_CTX_new()) {
201         return ASYNC_ERR;
202     }
203
204     if(*job) {
205         ASYNC_get_ctx()->currjob = *job;
206     }
207
208     for (;;) {
209         if(ASYNC_get_ctx()->currjob) {
210             if(ASYNC_get_ctx()->currjob->status == ASYNC_JOB_STOPPING) {
211                 *ret = ASYNC_get_ctx()->currjob->ret;
212                 async_release_job(ASYNC_get_ctx()->currjob);
213                 ASYNC_get_ctx()->currjob = NULL;
214                 *job = NULL;
215                 ASYNC_CTX_free();
216                 return ASYNC_FINISH;
217             }
218
219             if(ASYNC_get_ctx()->currjob->status == ASYNC_JOB_PAUSING) {
220                 *job = ASYNC_get_ctx()->currjob;
221                 ASYNC_get_ctx()->currjob->status = ASYNC_JOB_PAUSED;
222                 ASYNC_CTX_free();
223                 return ASYNC_PAUSE;
224             }
225
226             if(ASYNC_get_ctx()->currjob->status == ASYNC_JOB_PAUSED) {
227                 ASYNC_get_ctx()->currjob = *job;
228                 /* Resume previous job */
229                 if(!ASYNC_FIBRE_swapcontext(&ASYNC_get_ctx()->dispatcher,
230                     &ASYNC_get_ctx()->currjob->fibrectx, 1))
231                     goto err;
232                 continue;
233             }
234
235             /* Should not happen */
236             async_release_job(ASYNC_get_ctx()->currjob);
237             ASYNC_get_ctx()->currjob = NULL;
238             *job = NULL;
239             ASYNC_CTX_free();
240             return ASYNC_ERR;
241         }
242
243         /* Start a new job */
244         if(!(ASYNC_get_ctx()->currjob = async_get_pool_job())) {
245             ASYNC_CTX_free();
246             return ASYNC_NO_JOBS;
247         }
248
249         if(args != NULL) {
250             ASYNC_get_ctx()->currjob->funcargs = OPENSSL_malloc(size);
251             if(!ASYNC_get_ctx()->currjob->funcargs) {
252                 async_release_job(ASYNC_get_ctx()->currjob);
253                 ASYNC_get_ctx()->currjob = NULL;
254                 ASYNC_CTX_free();
255                 return ASYNC_ERR;
256             }
257             memcpy(ASYNC_get_ctx()->currjob->funcargs, args, size);
258         } else {
259             ASYNC_get_ctx()->currjob->funcargs = NULL;
260         }
261
262         ASYNC_get_ctx()->currjob->func = func;
263         if(!ASYNC_FIBRE_swapcontext(&ASYNC_get_ctx()->dispatcher,
264             &ASYNC_get_ctx()->currjob->fibrectx, 1))
265             goto err;
266     }
267
268 err:
269     async_release_job(ASYNC_get_ctx()->currjob);
270     ASYNC_get_ctx()->currjob = NULL;
271     *job = NULL;
272     ASYNC_CTX_free();
273     return ASYNC_ERR;
274 }
275
276
277 int ASYNC_pause_job(void)
278 {
279     ASYNC_JOB *job;
280
281     if(!ASYNC_get_ctx() || !ASYNC_get_ctx()->currjob)
282         return 0;
283
284     job = ASYNC_get_ctx()->currjob;
285     job->status = ASYNC_JOB_PAUSING;
286
287     if(!ASYNC_FIBRE_swapcontext(&job->fibrectx,
288                                &ASYNC_get_ctx()->dispatcher, 1)) {
289         /* Error */
290         return 0;
291     }
292
293     return 1;
294 }
295
296 int ASYNC_in_job(void)
297 {
298     if(ASYNC_get_ctx())
299         return 1;
300
301     return 0;
302 }
303
304 int ASYNC_init_pool(unsigned int local, size_t max_size, size_t init_size)
305 {
306     if (local != 0) {
307         /* We only support a global pool so far */
308         return 0;
309     }
310
311     pool_max_size = max_size;
312     pool = sk_ASYNC_JOB_new_null();
313     if (pool == NULL) {
314         return 0;
315     }
316     /* Pre-create jobs as required */
317     while (init_size) {
318         ASYNC_JOB *job;
319         job = ASYNC_JOB_new();
320         if (job) {
321             ASYNC_FIBRE_makecontext(&job->fibrectx);
322             job->funcargs = NULL;
323             sk_ASYNC_JOB_push(pool, job);
324             curr_size++;
325             init_size--;
326         } else {
327             /*
328              * Not actually fatal because we already created the pool, just skip
329              * creation of any more jobs
330              */
331             init_size = 0;
332         }
333     }
334
335     return 1;
336 }
337
338 void ASYNC_free_pool(void)
339 {
340     ASYNC_JOB *job;
341
342     do {
343         job = sk_ASYNC_JOB_pop(pool);
344         ASYNC_JOB_free(job);
345     } while (job);
346     sk_ASYNC_JOB_free(pool);
347 }
348
349 ASYNC_JOB *ASYNC_get_current_job(void)
350 {
351     ASYNC_CTX *ctx;
352     if((ctx = ASYNC_get_ctx()) == NULL)
353         return NULL;
354
355     return ctx->currjob;
356 }
357
358 int ASYNC_get_wait_fd(ASYNC_JOB *job)
359 {
360     return job->wait_fd;
361 }
362
363 void ASYNC_wake(ASYNC_JOB *job)
364 {
365     char dummy = 0;
366
367     if (job->wake_set)
368         return;
369     async_write1(job->wake_fd, &dummy);
370     job->wake_set = 1;
371 }
372
373 void ASYNC_clear_wake(ASYNC_JOB *job)
374 {
375     char dummy = 0;
376     if (!job->wake_set)
377         return;
378     async_read1(job->wait_fd, &dummy);
379     job->wake_set = 0;
380 }