summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorČestmír Kalina <ckalina@redhat.com>2021-09-27 22:42:11 +0200
committerMatt Caswell <matt@openssl.org>2022-10-17 09:45:39 +0100
commit4574a7fd8dda070b129d76defca07703cab53842 (patch)
treea6e03446b0f784e550010a2c51efe5b7861e9473
parentb1372197496650c3cb318cade911a3bd6af14adc (diff)
crypto: add preemptive threading support
Some primitives are designed to be used in a multi-threaded environment, if supported, e.g., Argon2. This patch adds support for preemptive threading and basic synchronization primitives for platforms compliant with POSIX threads or Windows CRT. Native functions are wrapped to provide a common (internal) API. Threading support can be disabled at compile time. If enabled, threading is disabled by default and needs to be explicitly enabled by the user. Thread enablement requires an explicit limit on the number of threads that OpenSSL may spawn (non-negative integer/infinity). The limit may be changed. Signed-off-by: Čestmír Kalina <ckalina@redhat.com> Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from https://github.com/openssl/openssl/pull/12255)
-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();