]> git.ipfire.org Git - thirdparty/openssl.git/blame - crypto/async/async.c
Convert ASYNC code to use new Thread API
[thirdparty/openssl.git] / crypto / async / async.c
CommitLineData
a3667c31
MC
1/*
2 * Written by Matt Caswell (matt@openssl.org) for the OpenSSL project.
3 */
4/* ====================================================================
5 * Copyright (c) 2015 The OpenSSL Project. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * 3. All advertising materials mentioning features or use of this
20 * software must display the following acknowledgment:
21 * "This product includes software developed by the OpenSSL Project
22 * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
23 *
24 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
25 * endorse or promote products derived from this software without
26 * prior written permission. For written permission, please contact
27 * licensing@OpenSSL.org.
28 *
29 * 5. Products derived from this software may not be called "OpenSSL"
30 * nor may "OpenSSL" appear in their names without prior written
31 * permission of the OpenSSL Project.
32 *
33 * 6. Redistributions of any form whatsoever must retain the following
34 * acknowledgment:
35 * "This product includes software developed by the OpenSSL Project
36 * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
37 *
38 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
39 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
41 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
42 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
43 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
45 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
47 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
48 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
49 * OF THE POSSIBILITY OF SUCH DAMAGE.
50 * ====================================================================
51 */
52
4abc7681
MC
53/*
54 * Without this we start getting longjmp crashes because it thinks we're jumping
55 * up the stack when in fact we are jumping to an entirely different stack. The
56 * cost of this is not having certain buffer overrun/underrun checks etc for
57 * this source file :-(
58 */
59#undef _FORTIFY_SOURCE
60
6e8ac508
VD
61/* This must be the first #include file */
62#include "async_locl.h"
63
224905f8 64#include <internal/threads.h>
079a1a90 65#include <openssl/err.h>
7b9f8f7f 66#include <internal/cryptlib_int.h>
a3667c31
MC
67#include <string.h>
68
69#define ASYNC_JOB_RUNNING 0
70#define ASYNC_JOB_PAUSING 1
71#define ASYNC_JOB_PAUSED 2
72#define ASYNC_JOB_STOPPING 3
73
224905f8
MC
74static CRYPTO_THREAD_LOCAL ctxkey;
75static CRYPTO_THREAD_LOCAL poolkey;
76
27949c35
MC
77static void async_free_pool_internal(async_pool *pool);
78
636ca4ff 79static async_ctx *async_ctx_new(void)
a3667c31 80{
636ca4ff 81 async_ctx *nctx = NULL;
a3667c31 82
e38565f5
MC
83 nctx = OPENSSL_malloc(sizeof (async_ctx));
84 if (nctx == NULL) {
079a1a90 85 ASYNCerr(ASYNC_F_ASYNC_CTX_NEW, ERR_R_MALLOC_FAILURE);
a3667c31
MC
86 goto err;
87 }
88
636ca4ff 89 async_fibre_init_dispatcher(&nctx->dispatcher);
a3667c31 90 nctx->currjob = NULL;
e8dfb5bf 91 nctx->blocked = 0;
224905f8 92 if (!CRYPTO_THREAD_set_local(&ctxkey, nctx))
9ec1e031 93 goto err;
a3667c31
MC
94
95 return nctx;
96err:
e38565f5 97 OPENSSL_free(nctx);
a3667c31
MC
98
99 return NULL;
100}
101
224905f8 102async_ctx *async_get_ctx(void)
7b9f8f7f 103{
0fc32b07
MC
104 if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC, NULL))
105 return NULL;
224905f8
MC
106
107 return (async_ctx *)CRYPTO_THREAD_get_local(&ctxkey);
7b9f8f7f
MC
108}
109
636ca4ff 110static int async_ctx_free(void)
a3667c31 111{
e38565f5 112 async_ctx *ctx;
a3667c31 113
e38565f5
MC
114 ctx = async_get_ctx();
115
224905f8 116 if (!CRYPTO_THREAD_set_local(&ctxkey, NULL))
9ec1e031 117 return 0;
a3667c31 118
e38565f5
MC
119 OPENSSL_free(ctx);
120
a3667c31
MC
121 return 1;
122}
123
636ca4ff 124static ASYNC_JOB *async_job_new(void)
a3667c31
MC
125{
126 ASYNC_JOB *job = NULL;
a3667c31 127
ff75a257 128 job = OPENSSL_zalloc(sizeof (ASYNC_JOB));
e38565f5 129 if (job == NULL) {
079a1a90 130 ASYNCerr(ASYNC_F_ASYNC_JOB_NEW, ERR_R_MALLOC_FAILURE);
50108304 131 return NULL;
a3667c31
MC
132 }
133
a3667c31 134 job->status = ASYNC_JOB_RUNNING;
a3667c31
MC
135
136 return job;
a3667c31
MC
137}
138
636ca4ff 139static void async_job_free(ASYNC_JOB *job)
a3667c31 140{
e38565f5
MC
141 if (job != NULL) {
142 OPENSSL_free(job->funcargs);
636ca4ff 143 async_fibre_free(&job->fibrectx);
a3667c31
MC
144 OPENSSL_free(job);
145 }
146}
147
252d6d3a 148static ASYNC_JOB *async_get_pool_job(void) {
a3667c31 149 ASYNC_JOB *job;
27949c35 150 async_pool *pool;
a3667c31 151
224905f8 152 pool = (async_pool *)CRYPTO_THREAD_get_local(&poolkey);
252d6d3a 153 if (pool == NULL) {
50108304 154 /*
252d6d3a 155 * Pool has not been initialised, so init with the defaults, i.e.
9f078e19 156 * no max size and no pre-created jobs
50108304 157 */
68487a9b 158 if (ASYNC_init_thread(0, 0) == 0)
252d6d3a 159 return NULL;
224905f8 160 pool = (async_pool *)CRYPTO_THREAD_get_local(&poolkey);
252d6d3a
MC
161 }
162
27949c35 163 job = sk_ASYNC_JOB_pop(pool->jobs);
252d6d3a
MC
164 if (job == NULL) {
165 /* Pool is empty */
27949c35 166 if ((pool->max_size != 0) && (pool->curr_size >= pool->max_size))
252d6d3a 167 return NULL;
0ff2b9ac 168
636ca4ff 169 job = async_job_new();
6e8ac508
VD
170 if (job != NULL) {
171 if (! async_fibre_makecontext(&job->fibrectx)) {
172 async_job_free(job);
173 return NULL;
174 }
27949c35 175 pool->curr_size++;
252d6d3a
MC
176 }
177 }
178 return job;
179}
180
181static void async_release_job(ASYNC_JOB *job) {
27949c35
MC
182 async_pool *pool;
183
224905f8 184 pool = (async_pool *)CRYPTO_THREAD_get_local(&poolkey);
e38565f5 185 OPENSSL_free(job->funcargs);
252d6d3a 186 job->funcargs = NULL;
27949c35 187 sk_ASYNC_JOB_push(pool->jobs, job);
252d6d3a
MC
188}
189
636ca4ff 190void async_start_func(void)
252d6d3a
MC
191{
192 ASYNC_JOB *job;
7b9f8f7f 193 async_ctx *ctx = async_get_ctx();
252d6d3a
MC
194
195 while (1) {
196 /* Run the job */
7b9f8f7f 197 job = ctx->currjob;
252d6d3a
MC
198 job->ret = job->func(job->funcargs);
199
200 /* Stop the job */
201 job->status = ASYNC_JOB_STOPPING;
e38565f5 202 if (!async_fibre_swapcontext(&job->fibrectx,
7b9f8f7f 203 &ctx->dispatcher, 1)) {
252d6d3a 204 /*
079a1a90
MC
205 * Should not happen. Getting here will close the thread...can't do
206 * much about it
252d6d3a 207 */
079a1a90 208 ASYNCerr(ASYNC_F_ASYNC_START_FUNC, ASYNC_R_FAILED_TO_SWAP_CONTEXT);
252d6d3a 209 }
50108304 210 }
a3667c31
MC
211}
212
ff75a257
MC
213int ASYNC_start_job(ASYNC_JOB **job, ASYNC_WAIT_CTX *wctx, int *ret,
214 int (*func)(void *), void *args, size_t size)
a3667c31 215{
7b9f8f7f
MC
216 async_ctx *ctx = async_get_ctx();
217 if (ctx == NULL)
218 ctx = async_ctx_new();
219 if (ctx == NULL) {
a3667c31
MC
220 return ASYNC_ERR;
221 }
222
e38565f5 223 if (*job) {
7b9f8f7f 224 ctx->currjob = *job;
a3667c31
MC
225 }
226
50108304 227 for (;;) {
7b9f8f7f
MC
228 if (ctx->currjob != NULL) {
229 if (ctx->currjob->status == ASYNC_JOB_STOPPING) {
230 *ret = ctx->currjob->ret;
ff75a257 231 ctx->currjob->waitctx = NULL;
7b9f8f7f
MC
232 async_release_job(ctx->currjob);
233 ctx->currjob = NULL;
82676094 234 *job = NULL;
50108304
MC
235 return ASYNC_FINISH;
236 }
237
7b9f8f7f
MC
238 if (ctx->currjob->status == ASYNC_JOB_PAUSING) {
239 *job = ctx->currjob;
240 ctx->currjob->status = ASYNC_JOB_PAUSED;
241 ctx->currjob = NULL;
50108304
MC
242 return ASYNC_PAUSE;
243 }
244
7b9f8f7f
MC
245 if (ctx->currjob->status == ASYNC_JOB_PAUSED) {
246 ctx->currjob = *job;
50108304 247 /* Resume previous job */
7b9f8f7f
MC
248 if (!async_fibre_swapcontext(&ctx->dispatcher,
249 &ctx->currjob->fibrectx, 1)) {
079a1a90
MC
250 ASYNCerr(ASYNC_F_ASYNC_START_JOB,
251 ASYNC_R_FAILED_TO_SWAP_CONTEXT);
50108304 252 goto err;
079a1a90 253 }
50108304
MC
254 continue;
255 }
256
257 /* Should not happen */
079a1a90 258 ASYNCerr(ASYNC_F_ASYNC_START_JOB, ERR_R_INTERNAL_ERROR);
7b9f8f7f
MC
259 async_release_job(ctx->currjob);
260 ctx->currjob = NULL;
82676094 261 *job = NULL;
50108304 262 return ASYNC_ERR;
a3667c31
MC
263 }
264
50108304 265 /* Start a new job */
7b9f8f7f 266 if ((ctx->currjob = async_get_pool_job()) == NULL) {
252d6d3a 267 return ASYNC_NO_JOBS;
a3667c31
MC
268 }
269
e38565f5 270 if (args != NULL) {
7b9f8f7f
MC
271 ctx->currjob->funcargs = OPENSSL_malloc(size);
272 if (ctx->currjob->funcargs == NULL) {
079a1a90 273 ASYNCerr(ASYNC_F_ASYNC_START_JOB, ERR_R_MALLOC_FAILURE);
7b9f8f7f
MC
274 async_release_job(ctx->currjob);
275 ctx->currjob = NULL;
50108304
MC
276 return ASYNC_ERR;
277 }
7b9f8f7f 278 memcpy(ctx->currjob->funcargs, args, size);
50108304 279 } else {
7b9f8f7f 280 ctx->currjob->funcargs = NULL;
a3667c31
MC
281 }
282
7b9f8f7f 283 ctx->currjob->func = func;
ff75a257 284 ctx->currjob->waitctx = wctx;
7b9f8f7f
MC
285 if (!async_fibre_swapcontext(&ctx->dispatcher,
286 &ctx->currjob->fibrectx, 1)) {
079a1a90 287 ASYNCerr(ASYNC_F_ASYNC_START_JOB, ASYNC_R_FAILED_TO_SWAP_CONTEXT);
50108304 288 goto err;
079a1a90 289 }
a3667c31
MC
290 }
291
50108304 292err:
7b9f8f7f
MC
293 async_release_job(ctx->currjob);
294 ctx->currjob = NULL;
82676094 295 *job = NULL;
a3667c31
MC
296 return ASYNC_ERR;
297}
298
a3667c31
MC
299int ASYNC_pause_job(void)
300{
301 ASYNC_JOB *job;
7b9f8f7f 302 async_ctx *ctx = async_get_ctx();
a3667c31 303
7b9f8f7f
MC
304 if (ctx == NULL
305 || ctx->currjob == NULL
306 || ctx->blocked) {
079a1a90 307 /*
05a6347f
MC
308 * Could be we've deliberately not been started within a job so this is
309 * counted as success.
079a1a90 310 */
05a6347f 311 return 1;
079a1a90 312 }
a3667c31 313
7b9f8f7f 314 job = ctx->currjob;
a3667c31
MC
315 job->status = ASYNC_JOB_PAUSING;
316
e8dfb5bf 317 if (!async_fibre_swapcontext(&job->fibrectx,
7b9f8f7f 318 &ctx->dispatcher, 1)) {
079a1a90 319 ASYNCerr(ASYNC_F_ASYNC_PAUSE_JOB, ASYNC_R_FAILED_TO_SWAP_CONTEXT);
a3667c31
MC
320 return 0;
321 }
ff75a257
MC
322 /* Reset counts of added and deleted fds */
323 async_wait_ctx_reset_counts(job->waitctx);
a3667c31
MC
324
325 return 1;
326}
327
27949c35 328static void async_empty_pool(async_pool *pool)
d63de0eb
MC
329{
330 ASYNC_JOB *job;
331
27949c35
MC
332 if (!pool || !pool->jobs)
333 return;
334
d63de0eb 335 do {
27949c35 336 job = sk_ASYNC_JOB_pop(pool->jobs);
636ca4ff 337 async_job_free(job);
d63de0eb
MC
338 } while (job);
339}
340
7b9f8f7f 341int async_init(void)
68487a9b 342{
224905f8
MC
343 if (!CRYPTO_THREAD_init_local(&ctxkey, NULL))
344 return 0;
345
346 if (!CRYPTO_THREAD_init_local(&poolkey, NULL)) {
347 CRYPTO_THREAD_cleanup_local(&ctxkey);
68487a9b 348 return 0;
224905f8 349 }
68487a9b 350
68487a9b
MC
351 return 1;
352}
353
224905f8
MC
354/* TODO: FIXME: This needs to be called by something!!! */
355void async_deinit(void);
356void async_deinit(void)
357{
358 CRYPTO_THREAD_cleanup_local(&ctxkey);
359 CRYPTO_THREAD_cleanup_local(&poolkey);
360}
361
68487a9b 362int ASYNC_init_thread(size_t max_size, size_t init_size)
252d6d3a 363{
27949c35 364 async_pool *pool;
0ff2b9ac
MC
365 size_t curr_size = 0;
366
68487a9b
MC
367 if (init_size > max_size) {
368 ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD, ASYNC_R_INVALID_POOL_SIZE);
27949c35
MC
369 return 0;
370 }
371
0fc32b07 372 if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC, NULL)) {
0fc32b07
MC
373 return 0;
374 }
7b9f8f7f 375 if (!ossl_init_thread_start(OPENSSL_INIT_THREAD_ASYNC)) {
22a34c2f
MC
376 return 0;
377 }
7b9f8f7f 378
27949c35 379 pool = OPENSSL_zalloc(sizeof *pool);
252d6d3a 380 if (pool == NULL) {
68487a9b 381 ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD, ERR_R_MALLOC_FAILURE);
252d6d3a
MC
382 return 0;
383 }
27949c35
MC
384
385 pool->jobs = sk_ASYNC_JOB_new_null();
386 if (pool->jobs == NULL) {
68487a9b 387 ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD, ERR_R_MALLOC_FAILURE);
27949c35
MC
388 OPENSSL_free(pool);
389 return 0;
390 }
391
392 pool->max_size = max_size;
393
252d6d3a 394 /* Pre-create jobs as required */
6e8ac508 395 while (init_size--) {
252d6d3a 396 ASYNC_JOB *job;
636ca4ff 397 job = async_job_new();
6e8ac508 398 if (job == NULL || !async_fibre_makecontext(&job->fibrectx)) {
252d6d3a 399 /*
6e8ac508
VD
400 * Not actually fatal because we already created the pool, just
401 * skip creation of any more jobs
252d6d3a 402 */
6e8ac508
VD
403 async_job_free(job);
404 break;
252d6d3a 405 }
6e8ac508
VD
406 job->funcargs = NULL;
407 sk_ASYNC_JOB_push(pool->jobs, job);
408 curr_size++;
252d6d3a 409 }
27949c35 410 pool->curr_size = curr_size;
224905f8 411 if (!CRYPTO_THREAD_set_local(&poolkey, pool)) {
68487a9b 412 ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD, ASYNC_R_FAILED_TO_SET_POOL);
27949c35 413 goto err;
d63de0eb 414 }
0ff2b9ac 415
252d6d3a 416 return 1;
27949c35
MC
417err:
418 async_free_pool_internal(pool);
419 return 0;
252d6d3a
MC
420}
421
27949c35 422static void async_free_pool_internal(async_pool *pool)
252d6d3a 423{
0ff2b9ac
MC
424 if (pool == NULL)
425 return;
d63de0eb
MC
426
427 async_empty_pool(pool);
27949c35
MC
428 sk_ASYNC_JOB_free(pool->jobs);
429 OPENSSL_free(pool);
224905f8 430 CRYPTO_THREAD_set_local(&poolkey, NULL);
22a34c2f 431 async_local_cleanup();
636ca4ff 432 async_ctx_free();
252d6d3a 433}
f4da39d2 434
68487a9b 435void ASYNC_cleanup_thread(void)
27949c35 436{
224905f8 437 async_free_pool_internal((async_pool *)CRYPTO_THREAD_get_local(&poolkey));
27949c35
MC
438}
439
f4da39d2
MC
440ASYNC_JOB *ASYNC_get_current_job(void)
441{
636ca4ff 442 async_ctx *ctx;
e38565f5
MC
443
444 ctx = async_get_ctx();
445 if(ctx == NULL)
f4da39d2
MC
446 return NULL;
447
448 return ctx->currjob;
449}
450
ff75a257 451ASYNC_WAIT_CTX *ASYNC_get_wait_ctx(ASYNC_JOB *job)
f4da39d2 452{
ff75a257 453 return job->waitctx;
f4da39d2 454}
e8dfb5bf
MC
455
456void ASYNC_block_pause(void)
457{
7b9f8f7f
MC
458 async_ctx *ctx = async_get_ctx();
459 if (ctx == NULL || ctx->currjob == NULL) {
e8dfb5bf
MC
460 /*
461 * We're not in a job anyway so ignore this
462 */
463 return;
464 }
7b9f8f7f 465 ctx->blocked++;
e8dfb5bf
MC
466}
467
468void ASYNC_unblock_pause(void)
469{
7b9f8f7f
MC
470 async_ctx *ctx = async_get_ctx();
471 if (ctx == NULL || ctx->currjob == NULL) {
e8dfb5bf
MC
472 /*
473 * We're not in a job anyway so ignore this
474 */
475 return;
476 }
7b9f8f7f
MC
477 if(ctx->blocked > 0)
478 ctx->blocked--;
e8dfb5bf 479}