summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xConfigure19
-rw-r--r--INSTALL.md21
-rw-r--r--crypto/build.info2
-rw-r--r--crypto/context.c20
-rw-r--r--crypto/thread/api.c73
-rw-r--r--crypto/thread/arch.c91
-rw-r--r--crypto/thread/arch/thread_none.c82
-rw-r--r--crypto/thread/arch/thread_posix.c305
-rw-r--r--crypto/thread/arch/thread_win.c254
-rw-r--r--crypto/thread/build.info8
-rw-r--r--crypto/thread/internal.c161
-rw-r--r--doc/man3/CRYPTO_THREAD_run_once.pod27
-rw-r--r--include/crypto/context.h6
-rw-r--r--include/internal/cryptlib.h3
-rw-r--r--include/internal/thread.h39
-rw-r--r--include/internal/thread_arch.h119
-rw-r--r--include/openssl/thread.h23
-rw-r--r--test/build.info4
-rw-r--r--test/threadstest.c333
-rw-r--r--util/libcrypto.num3
20 files changed, 1572 insertions, 21 deletions
diff --git a/Configure b/Configure
index 01c2d0bafe..fbafe0e867 100755
--- a/Configure
+++ b/Configure
@@ -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