diff options
-rwxr-xr-x | Configure | 19 | ||||
-rw-r--r-- | INSTALL.md | 21 | ||||
-rw-r--r-- | crypto/build.info | 2 | ||||
-rw-r--r-- | crypto/context.c | 20 | ||||
-rw-r--r-- | crypto/thread/api.c | 73 | ||||
-rw-r--r-- | crypto/thread/arch.c | 91 | ||||
-rw-r--r-- | crypto/thread/arch/thread_none.c | 82 | ||||
-rw-r--r-- | crypto/thread/arch/thread_posix.c | 305 | ||||
-rw-r--r-- | crypto/thread/arch/thread_win.c | 254 | ||||
-rw-r--r-- | crypto/thread/build.info | 8 | ||||
-rw-r--r-- | crypto/thread/internal.c | 161 | ||||
-rw-r--r-- | doc/man3/CRYPTO_THREAD_run_once.pod | 27 | ||||
-rw-r--r-- | include/crypto/context.h | 6 | ||||
-rw-r--r-- | include/internal/cryptlib.h | 3 | ||||
-rw-r--r-- | include/internal/thread.h | 39 | ||||
-rw-r--r-- | include/internal/thread_arch.h | 119 | ||||
-rw-r--r-- | include/openssl/thread.h | 23 | ||||
-rw-r--r-- | test/build.info | 4 | ||||
-rw-r--r-- | test/threadstest.c | 333 | ||||
-rw-r--r-- | util/libcrypto.num | 3 |
20 files changed, 1572 insertions, 21 deletions
@@ -655,6 +655,9 @@ my @disable_cascades = ( "fips" => [ "fips-securitychecks", "acvp-tests" ], + "threads" => [ "thread-pool" ], + "thread-pool" => [ "default-thread-pool" ], + "deprecated-3.0" => [ "engine", "srp" ] ); @@ -812,8 +815,6 @@ while (@argvcopy) s /^-?-?shared$/enable-shared/; s /^sctp$/enable-sctp/; s /^threads$/enable-threads/; - s /^thread-pool$/enable-thread-pool/; - s /^default-thread-pool$/enable-default-thread-pool/; s /^zlib$/enable-zlib/; s /^zlib-dynamic$/enable-zlib-dynamic/; s /^fips$/enable-fips/; @@ -1400,14 +1401,6 @@ if (grep { $_ =~ /(?:^|\s)-static(?:\s|$)/ } @{$config{LDFLAGS}}) { disable('static', 'pic', 'threads'); } -if ($disabled{threads}) { - disable('unavailable', 'thread-pool'); -} - -if ($disabled{"thread-pool"}) { - disable('unavailable', 'default-thread-pool'); -} - # Allow overriding the build file name $config{build_file} = env('BUILDFILE') || $target{build_file} || "Makefile"; @@ -1506,12 +1499,6 @@ foreach (grep /^-fsanitize=/, @{$config{CFLAGS} || []}) { unless($disabled{threads}) { push @{$config{openssl_feature_defines}}, "OPENSSL_THREADS"; } -unless($disabled{"thread-pool"}) { - push @{$config{openssl_feature_defines}}, "OPENSSL_THREAD_POOL"; -} -unless($disabled{"default-thread-pool"}) { - push @{$config{openssl_feature_defines}}, "OPENSSL_DEFAULT_THREAD_POOL"; -} my $no_shared_warn=0; if (($target{shared_target} // '') eq "") diff --git a/INSTALL.md b/INSTALL.md index 3c995349bd..f16ecf9c89 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -899,6 +899,27 @@ will usually require additional system-dependent options! See [Notes on multi-threading](#notes-on-multi-threading) below. +### no-thread-pool + +Don't build with support for thread pool functionality. + +### thread-pool + +Build with thread pool functionality. If enabled, OpenSSL algorithms may +use the thread pool to perform parallel computation. This option in itself +does not enable OpenSSL to spawn new threads. Currently the only supported +thread pool mechanism is the default thread pool. + +### no-default-thread-pool + +Don't build with support for default thread pool functionality. + +### default-thread-pool + +Build with default thread pool functionality. If enabled, OpenSSL may create +and manage threads up to a maximum number of threads authorized by the +application. Supported on POSIX compliant platforms and Windows. + ### enable-trace Build with support for the integrated tracing api. diff --git a/crypto/build.info b/crypto/build.info index f5b29cca1c..c064351b5a 100644 --- a/crypto/build.info +++ b/crypto/build.info @@ -6,7 +6,7 @@ SUBDIRS=objects buffer bio stack lhash rand evp asn1 pem x509 conf \ siphash sm3 des aes rc2 rc4 rc5 idea aria bf cast camellia \ seed sm4 chacha modes bn ec rsa dsa dh sm2 dso engine \ err comp http ocsp cms ts srp cmac ct async ess crmf cmp encode_decode \ - ffc hpke + ffc hpke thread LIBS=../libcrypto diff --git a/crypto/context.c b/crypto/context.c index aec9ecd4ac..a7b1832cbc 100644 --- a/crypto/context.c +++ b/crypto/context.c @@ -37,6 +37,9 @@ struct ossl_lib_ctx_st { OSSL_METHOD_STORE *store_loader_store; void *self_test_cb; #endif +#if defined(OPENSSL_THREADS) + void *threads; +#endif void *rand_crngt; #ifdef FIPS_MODULE void *thread_event_handler; @@ -171,6 +174,12 @@ static int context_init(OSSL_LIB_CTX *ctx) goto err; #endif +#if defined(OPENSSL_THREADS) + ctx->threads = ossl_threads_ctx_new(ctx); + if (ctx->threads == NULL) + goto err; +#endif + /* Low priority. */ #ifndef FIPS_MODULE ctx->child_provider = ossl_child_prov_ctx_new(ctx); @@ -299,6 +308,13 @@ static void context_deinit_objs(OSSL_LIB_CTX *ctx) } #endif +#if defined(OPENSSL_THREADS) + if (ctx->threads != NULL) { + ossl_threads_ctx_free(ctx->threads); + ctx->threads = NULL; + } +#endif + /* Low priority. */ #ifndef FIPS_MODULE if (ctx->child_provider != NULL) { @@ -526,6 +542,10 @@ void *ossl_lib_ctx_get_data(OSSL_LIB_CTX *ctx, int index) case OSSL_LIB_CTX_SELF_TEST_CB_INDEX: return ctx->self_test_cb; #endif +#if defined(OPENSSL_THREADS) + case OSSL_LIB_CTX_THREAD_INDEX: + return ctx->threads; +#endif case OSSL_LIB_CTX_RAND_CRNGT_INDEX: { diff --git a/crypto/thread/api.c b/crypto/thread/api.c new file mode 100644 index 0000000000..e025d24cea --- /dev/null +++ b/crypto/thread/api.c @@ -0,0 +1,73 @@ +/* + * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <openssl/configuration.h> +#include <openssl/thread.h> +#include <internal/thread.h> + +uint32_t OSSL_get_thread_support_flags(void) +{ + int support = 0; + +#if !defined(OPENSSL_NO_THREAD_POOL) + support |= OSSL_THREAD_SUPPORT_FLAG_THREAD_POOL; +#endif +#if !defined(OPENSSL_NO_DEFAULT_THREAD_POOL) + support |= OSSL_THREAD_SUPPORT_FLAG_DEFAULT_SPAWN; +#endif + + return support; +} + +#if defined(OPENSSL_NO_THREAD_POOL) || defined(OPENSSL_NO_DEFAULT_THREAD_POOL) + +int OSSL_set_max_threads(OSSL_LIB_CTX *ctx, uint64_t max_threads) +{ + return 0; +} + +uint64_t OSSL_get_max_threads(OSSL_LIB_CTX *ctx) +{ + return 0; +} + +#else + +uint64_t OSSL_get_max_threads(OSSL_LIB_CTX *ctx) +{ + uint64_t ret = 0; + OSSL_LIB_CTX_THREADS *tdata = OSSL_LIB_CTX_GET_THREADS(ctx); + + if (tdata == NULL) + goto fail; + + ossl_crypto_mutex_lock(tdata->lock); + ret = tdata->max_threads; + ossl_crypto_mutex_unlock(tdata->lock); + +fail: + return ret; +} + +int OSSL_set_max_threads(OSSL_LIB_CTX *ctx, uint64_t max_threads) +{ + OSSL_LIB_CTX_THREADS *tdata; + + tdata = OSSL_LIB_CTX_GET_THREADS(ctx); + if (tdata == NULL) + return 0; + + ossl_crypto_mutex_lock(tdata->lock); + tdata->max_threads = max_threads; + ossl_crypto_mutex_unlock(tdata->lock); + + return 1; +} + +#endif diff --git a/crypto/thread/arch.c b/crypto/thread/arch.c new file mode 100644 index 0000000000..565f87b93a --- /dev/null +++ b/crypto/thread/arch.c @@ -0,0 +1,91 @@ +/* + * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <openssl/configuration.h> +#include <internal/thread_arch.h> + +#if defined(OPENSSL_THREADS) + +CRYPTO_THREAD *ossl_crypto_thread_native_start(CRYPTO_THREAD_ROUTINE routine, + void *data, int joinable) +{ + CRYPTO_THREAD *handle; + + if (routine == NULL) + return NULL; + + handle = OPENSSL_zalloc(sizeof(*handle)); + if (handle == NULL) + return NULL; + + if ((handle->lock = ossl_crypto_mutex_new()) == NULL) + goto fail; + if ((handle->statelock = ossl_crypto_mutex_new()) == NULL) + goto fail; + if ((handle->condvar = ossl_crypto_condvar_new()) == NULL) + goto fail; + + handle->data = data; + handle->routine = routine; + handle->joinable = joinable; + + if (ossl_crypto_thread_native_spawn(handle) == 1) + return handle; + +fail: + ossl_crypto_condvar_free(&handle->condvar); + ossl_crypto_mutex_free(&handle->statelock); + ossl_crypto_mutex_free(&handle->lock); + OPENSSL_free(handle); + return NULL; +} + +int ossl_crypto_thread_native_clean(CRYPTO_THREAD *handle) +{ + uint64_t req_state_mask; + + if (handle == NULL) + return 0; + + req_state_mask = 0; + req_state_mask |= CRYPTO_THREAD_FINISHED; + req_state_mask |= CRYPTO_THREAD_TERMINATED; + req_state_mask |= CRYPTO_THREAD_JOINED; + + ossl_crypto_mutex_lock(handle->statelock); + if (CRYPTO_THREAD_GET_STATE(handle, req_state_mask) == 0) { + ossl_crypto_mutex_unlock(handle->statelock); + return 0; + } + ossl_crypto_mutex_unlock(handle->statelock); + + ossl_crypto_mutex_free(&handle->lock); + ossl_crypto_mutex_free(&handle->statelock); + ossl_crypto_condvar_free(&handle->condvar); + + OPENSSL_free(handle->handle); + OPENSSL_free(handle); + + return 1; +} + +#else + +CRYPTO_THREAD *ossl_crypto_thread_native_start(CRYPTO_THREAD_ROUTINE routine, + void *data, int joinable) +{ + return NULL; +} + +int ossl_crypto_thread_native_clean(CRYPTO_THREAD *handle) +{ + return 0; +} + +#endif diff --git a/crypto/thread/arch/thread_none.c b/crypto/thread/arch/thread_none.c new file mode 100644 index 0000000000..8a0389f5cb --- /dev/null +++ b/crypto/thread/arch/thread_none.c @@ -0,0 +1,82 @@ +/* + * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <internal/thread_arch.h> + +#if defined(OPENSSL_THREADS_NONE) + +int ossl_crypto_thread_native_spawn(CRYPTO_THREAD *thread) +{ + return 0; +} + +int ossl_crypto_thread_native_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval) +{ + return 0; +} + +int ossl_crypto_thread_native_terminate(CRYPTO_THREAD *thread) +{ + return 0; +} + +int ossl_crypto_thread_native_exit(void) +{ + return 0; +} + +int ossl_crypto_thread_native_is_self(CRYPTO_THREAD *thread) +{ + return 0; +} + +CRYPTO_MUTEX *ossl_crypto_mutex_new(void) +{ + return NULL; +} + +void ossl_crypto_mutex_lock(CRYPTO_MUTEX *mutex) +{ +} + +int ossl_crypto_mutex_try_lock(CRYPTO_MUTEX *mutex) +{ + return 0; +} + +void ossl_crypto_mutex_unlock(CRYPTO_MUTEX *mutex) +{ +} + +void ossl_crypto_mutex_free(CRYPTO_MUTEX **mutex) +{ +} + +CRYPTO_CONDVAR *ossl_crypto_condvar_new(void) +{ + return NULL; +} + +void ossl_crypto_condvar_wait(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex) +{ +} + +void ossl_crypto_condvar_broadcast(CRYPTO_CONDVAR *cv) +{ +} + +void ossl_crypto_condvar_free(CRYPTO_CONDVAR **cv) +{ +} + +void ossl_crypto_mem_barrier(void) +{ +} + +#endif diff --git a/crypto/thread/arch/thread_posix.c b/crypto/thread/arch/thread_posix.c new file mode 100644 index 0000000000..d74cfddab3 --- /dev/null +++ b/crypto/thread/arch/thread_posix.c @@ -0,0 +1,305 @@ +/* + * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <internal/thread_arch.h> + +#if defined(OPENSSL_THREADS_POSIX) +# define _GNU_SOURCE +# include <errno.h> +# include <sys/types.h> +# include <unistd.h> + +static void *thread_start_thunk(void *vthread) +{ + CRYPTO_THREAD *thread; + CRYPTO_THREAD_RETVAL ret; + + thread = (CRYPTO_THREAD *)vthread; + + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + + ret = thread->routine(thread->data); + ossl_crypto_mutex_lock(thread->statelock); + CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_FINISHED); + thread->retval = ret; + ossl_crypto_condvar_broadcast(thread->condvar); + ossl_crypto_mutex_unlock(thread->statelock); + + return NULL; +} + +int ossl_crypto_thread_native_spawn(CRYPTO_THREAD *thread) +{ + int ret; + pthread_attr_t attr; + pthread_t *handle; + + handle = OPENSSL_zalloc(sizeof(*handle)); + if (handle == NULL) + goto fail; + + pthread_attr_init(&attr); + if (!thread->joinable) + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + ret = pthread_create(handle, &attr, thread_start_thunk, thread); + pthread_attr_destroy(&attr); + + if (ret != 0) + goto fail; + + thread->handle = handle; + return 1; + +fail: + thread->handle = NULL; + OPENSSL_free(handle); + return 0; +} + +int ossl_crypto_thread_native_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval) +{ + void *thread_retval; + pthread_t *handle; + uint64_t req_state_mask; + + if (thread == NULL) + return 0; + + req_state_mask = CRYPTO_THREAD_TERMINATED | CRYPTO_THREAD_JOINED; + + ossl_crypto_mutex_lock(thread->statelock); + if (CRYPTO_THREAD_GET_STATE(thread, req_state_mask)) { + ossl_crypto_mutex_unlock(thread->statelock); + goto pass; + } + while (!CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_FINISHED)) + ossl_crypto_condvar_wait(thread->condvar, thread->statelock); + ossl_crypto_mutex_unlock(thread->statelock); + + handle = (pthread_t *) thread->handle; + if (handle == NULL) + goto fail; + + if (pthread_join(*handle, &thread_retval) != 0) + goto fail; + + /* + * Join return value may be non-NULL when the thread has been cancelled, + * as indicated by thread_retval set to PTHREAD_CANCELLED. + */ + if (thread_retval != NULL) + goto fail; + +pass: + if (retval != NULL) + *retval = thread->retval; + + ossl_crypto_mutex_lock(thread->statelock); + CRYPTO_THREAD_UNSET_ERROR(thread, CRYPTO_THREAD_JOINED); + CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_JOINED); + ossl_crypto_mutex_unlock(thread->statelock); + return 1; + +fail: + ossl_crypto_mutex_lock(thread->statelock); + CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_JOINED); + ossl_crypto_mutex_unlock(thread->statelock); + return 0; +} + +int ossl_crypto_thread_native_terminate(CRYPTO_THREAD *thread) +{ + void *res; + uint64_t mask; + pthread_t *handle; + + mask = CRYPTO_THREAD_FINISHED; + mask |= CRYPTO_THREAD_TERMINATED; + mask |= CRYPTO_THREAD_JOINED; + + if (thread == NULL) + return 0; + + ossl_crypto_mutex_lock(thread->statelock); + if (thread->handle == NULL || CRYPTO_THREAD_GET_STATE(thread, mask)) + goto terminated; + ossl_crypto_mutex_unlock(thread->statelock); + + handle = thread->handle; + if (pthread_cancel(*handle) != 0) { + ossl_crypto_mutex_lock(thread->statelock); + CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_TERMINATED); + ossl_crypto_mutex_unlock(thread->statelock); + return 0; + } + if (pthread_join(*handle, &res) != 0) + return 0; + if (res != PTHREAD_CANCELED) + return 0; + + thread->handle = NULL; + OPENSSL_free(handle); + + ossl_crypto_mutex_lock(thread->statelock); +terminated: + CRYPTO_THREAD_UNSET_ERROR(thread, CRYPTO_THREAD_TERMINATED); + CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_TERMINATED); + ossl_crypto_mutex_unlock(thread->statelock); + return 1; +} + +int ossl_crypto_thread_native_exit(void) +{ + pthread_exit(NULL); + return 1; +} + +int ossl_crypto_thread_native_is_self(CRYPTO_THREAD *thread) +{ + return pthread_equal(*(pthread_t *)thread->handle, pthread_self()); +} + +CRYPTO_MUTEX *ossl_crypto_mutex_new(void) +{ + pthread_mutex_t *mutex; + + if ((mutex = OPENSSL_zalloc(sizeof(*mutex))) == NULL) + return NULL; + if (pthread_mutex_init(mutex, NULL) != 0) { + OPENSSL_free(mutex); + return NULL; + } + return (CRYPTO_MUTEX *)mutex; +} + +int ossl_crypto_mutex_try_lock(CRYPTO_MUTEX *mutex) +{ + pthread_mutex_t *mutex_p; + + mutex_p = (pthread_mutex_t *)mutex; + + if (pthread_mutex_trylock(mutex_p) == EBUSY) + return 0; + + return 1; +} + +void ossl_crypto_mutex_lock(CRYPTO_MUTEX *mutex) +{ + pthread_mutex_t *mutex_p; + + mutex_p = (pthread_mutex_t *)mutex; + pthread_mutex_lock(mutex_p); +} + +void ossl_crypto_mutex_unlock(CRYPTO_MUTEX *mutex) +{ + pthread_mutex_t *mutex_p; + + mutex_p = (pthread_mutex_t *)mutex; + pthread_mutex_unlock(mutex_p); +} + +void ossl_crypto_mutex_free(CRYPTO_MUTEX **mutex) +{ + pthread_mutex_t **mutex_p; + + if (mutex == NULL) + return; + + mutex_p = (pthread_mutex_t **)mutex; + if (*mutex_p != NULL) + pthread_mutex_destroy(*mutex_p); + OPENSSL_free(*mutex_p); + *mutex = NULL; +} + +CRYPTO_CONDVAR *ossl_crypto_condvar_new(void) +{ + pthread_cond_t *cv_p; + + if ((cv_p = OPENSSL_zalloc(sizeof(*cv_p))) == NULL) + return NULL; + if (pthread_cond_init(cv_p, NULL) != 0) { + OPENSSL_free(cv_p); + return NULL; + } + return (CRYPTO_CONDVAR *) cv_p; +} + +void ossl_crypto_condvar_wait(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex) +{ + pthread_cond_t *cv_p; + pthread_mutex_t *mutex_p; + + cv_p = (pthread_cond_t *)cv; + mutex_p = (pthread_mutex_t *)mutex; + pthread_cond_wait(cv_p, mutex_p); +} + +void ossl_crypto_condvar_broadcast(CRYPTO_CONDVAR *cv) +{ + pthread_cond_t *cv_p; + + cv_p = (pthread_cond_t *)cv; + pthread_cond_broadcast(cv_p); +} + +void ossl_crypto_condvar_free(CRYPTO_CONDVAR **cv) +{ + pthread_cond_t **cv_p; + + if (cv == NULL) + return; + + cv_p = (pthread_cond_t **)cv; + if (*cv_p != NULL) + pthread_cond_destroy(*cv_p); + OPENSSL_free(*cv_p); + *cv_p = NULL; +} + +void ossl_crypto_mem_barrier(void) +{ +# if defined(__clang__) || defined(__GNUC__) + __sync_synchronize(); +# elif !defined(OPENSSL_NO_ASM) +# if defined(__alpha__) /* Alpha */ + __asm__ volatile("mb" : : : "memory"); +# elif defined(__amd64__) || defined(__i386__) || defined(__i486__) \ + || defined(__i586__) || defined(__i686__) || defined(__i386) /* x86 */ + __asm__ volatile("mfence" : : : "memory"); +# elif defined(__arm__) || defined(__aarch64__) /* ARMv7, ARMv8 */ + __asm__ volatile("dmb ish" : : : "memory"); +# elif defined(__hppa__) /* PARISC */ + __asm__ volatile("" : : : "memory"); +# elif defined(__mips__) /* MIPS */ + __asm__ volatile("sync" : : : "memory"); +# elif defined(__powerpc__) || defined(__powerpc64__) /* power, ppc64, ppc64le */ + __asm__ volatile("sync" : : : "memory"); +# elif defined(__sparc__) + __asm__ volatile("ba,pt %%xcc, 1f\n\t" \ + " membar #Sync\n" \ + "1:\n" \ + : : : "memory"); +# elif defined(__s390__) || defined(__s390x__) /* z */ + __asm__ volatile("bcr 15,0" : : : "memory"); +# elif defined(__riscv) || defined(__riscv__) /* riscv */ + __asm__ volatile("fence iorw,iorw" : : : "memory"); +# else /* others, compiler only */ + __asm__ volatile("" : : : "memory"); +# endif +# else + /* compiler only barrier */ + __asm__ volatile("" : : : "memory"); +# endif +} + +#endif diff --git a/crypto/thread/arch/thread_win.c b/crypto/thread/arch/thread_win.c new file mode 100644 index 0000000000..b71cda85ea --- /dev/null +++ b/crypto/thread/arch/thread_win.c @@ -0,0 +1,254 @@ +/* + * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <internal/thread_arch.h> + +#if defined(OPENSSL_THREADS_WINNT) +# include <process.h> +# include <windows.h> + +static DWORD __stdcall thread_start_thunk(LPVOID vthread) +{ + CRYPTO_THREAD *thread; + CRYPTO_THREAD_RETVAL ret; + + thread = (CRYPTO_THREAD *)vthread; + + thread->thread_id = GetCurrentThreadId(); + + ret = thread->routine(thread->data); + ossl_crypto_mutex_lock(thread->statelock); + CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_FINISHED); + thread->retval = ret; + ossl_crypto_condvar_broadcast(thread->condvar); + ossl_crypto_mutex_unlock(thread->statelock); + + return 0; +} + +int ossl_crypto_thread_native_spawn(CRYPTO_THREAD *thread) +{ + HANDLE *handle; + + handle = OPENSSL_zalloc(sizeof(*handle)); + if (handle == NULL) + goto fail; + + *handle = (HANDLE)_beginthreadex(NULL, 0, &thread_start_thunk, thread, 0, NULL); + if (*handle == NULL) + goto fail; + + thread->handle = handle; + return 1; + +fail: + thread->handle = NULL; + OPENSSL_free(handle); + return 0; +} + +int ossl_crypto_thread_native_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval) +{ + int req_state_mask; + DWORD thread_retval; + HANDLE *handle; + + if (thread == NULL) + return 0; + + req_state_mask = CRYPTO_THREAD_TERMINATED | CRYPTO_THREAD_JOINED; + + ossl_crypto_mutex_lock(thread->statelock); + if (CRYPTO_THREAD_GET_STATE(thread, req_state_mask)) + goto pass; + while (!CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_FINISHED)) + ossl_crypto_condvar_wait(thread->condvar, thread->statelock); + + handle = (HANDLE *) thread->handle; + if (handle == NULL) + goto fail; + + if (WaitForSingleObject(*handle, INFINITE) != WAIT_OBJECT_0) + goto fail; + + if (GetExitCodeThread(*handle, &thread_retval) == 0) + goto fail; + + /* + * GetExitCodeThread call followed by this check is to make sure that + * the thread exitted properly. In particular, thread_retval may be + * non-zero when exitted via explicit ExitThread/TerminateThread or + * if the thread is still active (returns STILL_ACTIVE (259)). + */ + if (thread_retval != 0) + goto fail; + + if (CloseHandle(*handle) == 0) + goto fail; + +pass: + if (retval != NULL) + *retval = thread->retval; + + CRYPTO_THREAD_UNSET_ERROR(thread, CRYPTO_THREAD_JOINED); + CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_JOINED); + ossl_crypto_mutex_unlock(thread->statelock); + return 1; + +fail: + CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_JOINED); + ossl_crypto_mutex_unlock(thread->statelock); + return 0; +} + +int ossl_crypto_thread_native_terminate(CRYPTO_THREAD *thread) +{ + uint64_t mask; + HANDLE *handle; + + mask = CRYPTO_THREAD_FINISHED; + mask |= CRYPTO_THREAD_TERMINATED; + mask |= CRYPTO_THREAD_JOINED; + + if (thread == NULL) + return 1; + + ossl_crypto_mutex_lock(thread->statelock); + if (thread->handle == NULL || CRYPTO_THREAD_GET_STATE(thread, mask)) + goto terminated; + ossl_crypto_mutex_unlock(thread->statelock); + + handle = thread->handle; + if (WaitForSingleObject(*handle, 0) != WAIT_OBJECT_0) { + if (TerminateThread(*handle, STILL_ACTIVE) == 0) { + ossl_crypto_mutex_lock(thread->statelock); + CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_TERMINATED); + ossl_crypto_mutex_unlock(thread->statelock); + return 0; + } + } + + if (CloseHandle(*handle) == 0) { + CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_TERMINATED); + return 0; + } + + thread->handle = NULL; + OPENSSL_free(handle); + + ossl_crypto_mutex_lock(thread->statelock); +terminated: + CRYPTO_THREAD_UNSET_ERROR(thread, CRYPTO_THREAD_TERMINATED); + CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_TERMINATED); + ossl_crypto_mutex_unlock(thread->statelock); + return 1; +} + +int ossl_crypto_thread_native_exit(void) +{ + _endthreadex(0); + return 1; +} + +int ossl_crypto_thread_native_is_self(CRYPTO_THREAD *thread) +{ + return thread->thread_id == GetCurrentThreadId(); +} + +CRYPTO_MUTEX *ossl_crypto_mutex_new(void) +{ + CRITICAL_SECTION *mutex; + + if ((mutex = OPENSSL_zalloc(sizeof(*mutex))) == NULL) + return NULL; + InitializeCriticalSection(mutex); + return (CRYPTO_MUTEX *)mutex; +} + +void ossl_crypto_mutex_lock(CRYPTO_MUTEX *mutex) +{ + CRITICAL_SECTION *mutex_p; + + mutex_p = (CRITICAL_SECTION *)mutex; + EnterCriticalSection(mutex_p); +} + +int ossl_crypto_mutex_try_lock(CRYPTO_MUTEX *mutex) +{ + CRITICAL_SECTION *mutex_p; + + mutex_p = (CRITICAL_SECTION *)mutex; + if (TryEnterCriticalSection(mutex_p)) + return 1; + + return 0; +} + +void ossl_crypto_mutex_unlock(CRYPTO_MUTEX *mutex) +{ + CRITICAL_SECTION *mutex_p; + + mutex_p = (CRITICAL_SECTION *)mutex; + LeaveCriticalSection(mutex_p); +} + +void ossl_crypto_mutex_free(CRYPTO_MUTEX **mutex) +{ + CRITICAL_SECTION **mutex_p; + + mutex_p = (CRITICAL_SECTION **)mutex; + if (*mutex_p != NULL) + DeleteCriticalSection(*mutex_p); + OPENSSL_free(*mutex_p); + *mutex = NULL; +} + +CRYPTO_CONDVAR *ossl_crypto_condvar_new(void) +{ + CONDITION_VARIABLE *cv_p; + + if ((cv_p = OPENSSL_zalloc(sizeof(*cv_p))) == NULL) + return NULL; + InitializeConditionVariable(cv_p); + return (CRYPTO_CONDVAR *)cv_p; +} + +void ossl_crypto_condvar_wait(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex) +{ + CONDITION_VARIABLE *cv_p; + CRITICAL_SECTION *mutex_p; + + cv_p = (CONDITION_VARIABLE *)cv; + mutex_p = (CRITICAL_SECTION *)mutex; + SleepConditionVariableCS(cv_p, mutex_p, INFINITE); +} + +void ossl_crypto_condvar_broadcast(CRYPTO_CONDVAR *cv) +{ + CONDITION_VARIABLE *cv_p; + + cv_p = (CONDITION_VARIABLE *)cv; + WakeAllConditionVariable(cv_p); +} + +void ossl_crypto_condvar_free(CRYPTO_CONDVAR **cv) +{ + CONDITION_VARIABLE **cv_p; + + cv_p = (CONDITION_VARIABLE **)cv; + OPENSSL_free(*cv_p); + *cv_p = NULL; +} + +void ossl_crypto_mem_barrier(void) +{ + MemoryBarrier(); +} + +#endif diff --git a/crypto/thread/build.info b/crypto/thread/build.info new file mode 100644 index 0000000000..3ab689d4a4 --- /dev/null +++ b/crypto/thread/build.info @@ -0,0 +1,8 @@ +LIBS=../../libcrypto + +$THREADS=\ + api.c internal.c arch.c \ + arch/thread_win.c arch/thread_posix.c arch/thread_none.c + +SOURCE[../../libcrypto]=$THREADS +SOURCE[../../providers/libfips.a]=$THREADS diff --git a/crypto/thread/internal.c b/crypto/thread/internal.c new file mode 100644 index 0000000000..22af876cd7 --- /dev/null +++ b/crypto/thread/internal.c @@ -0,0 +1,161 @@ +/* + * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <openssl/configuration.h> +#include <openssl/e_os2.h> +#include <openssl/types.h> +#include <openssl/crypto.h> +#include <internal/thread.h> +#include |