2 * Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
4 * Licensed under the OpenSSL license (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
11 * Without this we start getting longjmp crashes because it thinks we're jumping
12 * up the stack when in fact we are jumping to an entirely different stack. The
13 * cost of this is not having certain buffer overrun/underrun checks etc for
14 * this source file :-(
16 #undef _FORTIFY_SOURCE
18 /* This must be the first #include file */
19 #include "async_locl.h"
21 #include <openssl/err.h>
22 #include "internal/cryptlib_int.h"
25 #define ASYNC_JOB_RUNNING 0
26 #define ASYNC_JOB_PAUSING 1
27 #define ASYNC_JOB_PAUSED 2
28 #define ASYNC_JOB_STOPPING 3
30 static CRYPTO_THREAD_LOCAL ctxkey
;
31 static CRYPTO_THREAD_LOCAL poolkey
;
33 static void async_free_pool_internal(async_pool
*pool
);
35 static async_ctx
*async_ctx_new(void)
37 async_ctx
*nctx
= NULL
;
39 nctx
= OPENSSL_malloc(sizeof(*nctx
));
41 ASYNCerr(ASYNC_F_ASYNC_CTX_NEW
, ERR_R_MALLOC_FAILURE
);
45 async_fibre_init_dispatcher(&nctx
->dispatcher
);
48 if (!CRYPTO_THREAD_set_local(&ctxkey
, nctx
))
58 async_ctx
*async_get_ctx(void)
60 if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC
, NULL
))
63 return (async_ctx
*)CRYPTO_THREAD_get_local(&ctxkey
);
66 static int async_ctx_free(void)
70 ctx
= async_get_ctx();
72 if (!CRYPTO_THREAD_set_local(&ctxkey
, NULL
))
80 static ASYNC_JOB
*async_job_new(void)
82 ASYNC_JOB
*job
= NULL
;
84 job
= OPENSSL_zalloc(sizeof(*job
));
86 ASYNCerr(ASYNC_F_ASYNC_JOB_NEW
, ERR_R_MALLOC_FAILURE
);
90 job
->status
= ASYNC_JOB_RUNNING
;
95 static void async_job_free(ASYNC_JOB
*job
)
98 OPENSSL_free(job
->funcargs
);
99 async_fibre_free(&job
->fibrectx
);
104 static ASYNC_JOB
*async_get_pool_job(void) {
108 pool
= (async_pool
*)CRYPTO_THREAD_get_local(&poolkey
);
111 * Pool has not been initialised, so init with the defaults, i.e.
112 * no max size and no pre-created jobs
114 if (ASYNC_init_thread(0, 0) == 0)
116 pool
= (async_pool
*)CRYPTO_THREAD_get_local(&poolkey
);
119 job
= sk_ASYNC_JOB_pop(pool
->jobs
);
122 if ((pool
->max_size
!= 0) && (pool
->curr_size
>= pool
->max_size
))
125 job
= async_job_new();
127 if (! async_fibre_makecontext(&job
->fibrectx
)) {
137 static void async_release_job(ASYNC_JOB
*job
) {
140 pool
= (async_pool
*)CRYPTO_THREAD_get_local(&poolkey
);
141 OPENSSL_free(job
->funcargs
);
142 job
->funcargs
= NULL
;
143 sk_ASYNC_JOB_push(pool
->jobs
, job
);
146 void async_start_func(void)
149 async_ctx
*ctx
= async_get_ctx();
154 job
->ret
= job
->func(job
->funcargs
);
157 job
->status
= ASYNC_JOB_STOPPING
;
158 if (!async_fibre_swapcontext(&job
->fibrectx
,
159 &ctx
->dispatcher
, 1)) {
161 * Should not happen. Getting here will close the thread...can't do
164 ASYNCerr(ASYNC_F_ASYNC_START_FUNC
, ASYNC_R_FAILED_TO_SWAP_CONTEXT
);
169 int ASYNC_start_job(ASYNC_JOB
**job
, ASYNC_WAIT_CTX
*wctx
, int *ret
,
170 int (*func
)(void *), void *args
, size_t size
)
172 async_ctx
*ctx
= async_get_ctx();
174 ctx
= async_ctx_new();
184 if (ctx
->currjob
!= NULL
) {
185 if (ctx
->currjob
->status
== ASYNC_JOB_STOPPING
) {
186 *ret
= ctx
->currjob
->ret
;
187 ctx
->currjob
->waitctx
= NULL
;
188 async_release_job(ctx
->currjob
);
194 if (ctx
->currjob
->status
== ASYNC_JOB_PAUSING
) {
196 ctx
->currjob
->status
= ASYNC_JOB_PAUSED
;
201 if (ctx
->currjob
->status
== ASYNC_JOB_PAUSED
) {
203 /* Resume previous job */
204 if (!async_fibre_swapcontext(&ctx
->dispatcher
,
205 &ctx
->currjob
->fibrectx
, 1)) {
206 ASYNCerr(ASYNC_F_ASYNC_START_JOB
,
207 ASYNC_R_FAILED_TO_SWAP_CONTEXT
);
213 /* Should not happen */
214 ASYNCerr(ASYNC_F_ASYNC_START_JOB
, ERR_R_INTERNAL_ERROR
);
215 async_release_job(ctx
->currjob
);
221 /* Start a new job */
222 if ((ctx
->currjob
= async_get_pool_job()) == NULL
) {
223 return ASYNC_NO_JOBS
;
227 ctx
->currjob
->funcargs
= OPENSSL_malloc(size
);
228 if (ctx
->currjob
->funcargs
== NULL
) {
229 ASYNCerr(ASYNC_F_ASYNC_START_JOB
, ERR_R_MALLOC_FAILURE
);
230 async_release_job(ctx
->currjob
);
234 memcpy(ctx
->currjob
->funcargs
, args
, size
);
236 ctx
->currjob
->funcargs
= NULL
;
239 ctx
->currjob
->func
= func
;
240 ctx
->currjob
->waitctx
= wctx
;
241 if (!async_fibre_swapcontext(&ctx
->dispatcher
,
242 &ctx
->currjob
->fibrectx
, 1)) {
243 ASYNCerr(ASYNC_F_ASYNC_START_JOB
, ASYNC_R_FAILED_TO_SWAP_CONTEXT
);
249 async_release_job(ctx
->currjob
);
255 int ASYNC_pause_job(void)
258 async_ctx
*ctx
= async_get_ctx();
261 || ctx
->currjob
== NULL
264 * Could be we've deliberately not been started within a job so this is
265 * counted as success.
271 job
->status
= ASYNC_JOB_PAUSING
;
273 if (!async_fibre_swapcontext(&job
->fibrectx
,
274 &ctx
->dispatcher
, 1)) {
275 ASYNCerr(ASYNC_F_ASYNC_PAUSE_JOB
, ASYNC_R_FAILED_TO_SWAP_CONTEXT
);
278 /* Reset counts of added and deleted fds */
279 async_wait_ctx_reset_counts(job
->waitctx
);
284 static void async_empty_pool(async_pool
*pool
)
288 if (!pool
|| !pool
->jobs
)
292 job
= sk_ASYNC_JOB_pop(pool
->jobs
);
299 if (!CRYPTO_THREAD_init_local(&ctxkey
, NULL
))
302 if (!CRYPTO_THREAD_init_local(&poolkey
, NULL
)) {
303 CRYPTO_THREAD_cleanup_local(&ctxkey
);
310 void async_deinit(void)
312 CRYPTO_THREAD_cleanup_local(&ctxkey
);
313 CRYPTO_THREAD_cleanup_local(&poolkey
);
316 int ASYNC_init_thread(size_t max_size
, size_t init_size
)
319 size_t curr_size
= 0;
321 if (init_size
> max_size
) {
322 ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD
, ASYNC_R_INVALID_POOL_SIZE
);
326 if (!OPENSSL_init_crypto(OPENSSL_INIT_ASYNC
, NULL
)) {
329 if (!ossl_init_thread_start(OPENSSL_INIT_THREAD_ASYNC
)) {
333 pool
= OPENSSL_zalloc(sizeof(*pool
));
335 ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD
, ERR_R_MALLOC_FAILURE
);
339 pool
->jobs
= sk_ASYNC_JOB_new_reserve(NULL
, init_size
);
340 if (pool
->jobs
== NULL
) {
341 ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD
, ERR_R_MALLOC_FAILURE
);
346 pool
->max_size
= max_size
;
348 /* Pre-create jobs as required */
349 while (init_size
--) {
351 job
= async_job_new();
352 if (job
== NULL
|| !async_fibre_makecontext(&job
->fibrectx
)) {
354 * Not actually fatal because we already created the pool, just
355 * skip creation of any more jobs
360 job
->funcargs
= NULL
;
361 sk_ASYNC_JOB_push(pool
->jobs
, job
); /* Cannot fail due to reserve */
364 pool
->curr_size
= curr_size
;
365 if (!CRYPTO_THREAD_set_local(&poolkey
, pool
)) {
366 ASYNCerr(ASYNC_F_ASYNC_INIT_THREAD
, ASYNC_R_FAILED_TO_SET_POOL
);
372 async_free_pool_internal(pool
);
376 static void async_free_pool_internal(async_pool
*pool
)
381 async_empty_pool(pool
);
382 sk_ASYNC_JOB_free(pool
->jobs
);
384 CRYPTO_THREAD_set_local(&poolkey
, NULL
);
385 async_local_cleanup();
389 void ASYNC_cleanup_thread(void)
391 async_free_pool_internal((async_pool
*)CRYPTO_THREAD_get_local(&poolkey
));
394 ASYNC_JOB
*ASYNC_get_current_job(void)
398 ctx
= async_get_ctx();
405 ASYNC_WAIT_CTX
*ASYNC_get_wait_ctx(ASYNC_JOB
*job
)
410 void ASYNC_block_pause(void)
412 async_ctx
*ctx
= async_get_ctx();
413 if (ctx
== NULL
|| ctx
->currjob
== NULL
) {
415 * We're not in a job anyway so ignore this
422 void ASYNC_unblock_pause(void)
424 async_ctx
*ctx
= async_get_ctx();
425 if (ctx
== NULL
|| ctx
->currjob
== NULL
) {
427 * We're not in a job anyway so ignore this
431 if (ctx
->blocked
> 0)