"fips" => [ "fips-securitychecks", "acvp-tests" ],
+ "threads" => [ "thread-pool" ],
+ "thread-pool" => [ "default-thread-pool" ],
+
"deprecated-3.0" => [ "engine", "srp" ]
);
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/;
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";
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 "")
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.
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
OSSL_METHOD_STORE *encoder_store;
OSSL_METHOD_STORE *store_loader_store;
void *self_test_cb;
+#endif
+#if defined(OPENSSL_THREADS)
+ void *threads;
#endif
void *rand_crngt;
#ifdef FIPS_MODULE
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);
}
#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) {
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: {
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+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
--- /dev/null
+/*
+ * 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 <internal/thread_arch.h>
+
+#if !defined(OPENSSL_NO_DEFAULT_THREAD_POOL)
+
+static ossl_inline uint64_t _ossl_get_avail_threads(OSSL_LIB_CTX_THREADS *tdata)
+{
+ /* assumes that tdata->lock is taken */
+ return tdata->max_threads - tdata->active_threads;
+}
+
+uint64_t ossl_get_avail_threads(OSSL_LIB_CTX *ctx)
+{
+ uint64_t retval = 0;
+ OSSL_LIB_CTX_THREADS *tdata = OSSL_LIB_CTX_GET_THREADS(ctx);
+
+ if (tdata == NULL)
+ return retval;
+
+ ossl_crypto_mutex_lock(tdata->lock);
+ retval = _ossl_get_avail_threads(tdata);
+ ossl_crypto_mutex_unlock(tdata->lock);
+
+ return retval;
+}
+
+void *ossl_crypto_thread_start(OSSL_LIB_CTX *ctx, CRYPTO_THREAD_ROUTINE start,
+ void *data)
+{
+ CRYPTO_THREAD *thread;
+ OSSL_LIB_CTX_THREADS *tdata = OSSL_LIB_CTX_GET_THREADS(ctx);
+
+ if (tdata == NULL)
+ return NULL;
+
+ ossl_crypto_mutex_lock(tdata->lock);
+ if (tdata == NULL || tdata->max_threads == 0) {
+ ossl_crypto_mutex_unlock(tdata->lock);
+ return NULL;
+ }
+
+ while (_ossl_get_avail_threads(tdata) == 0)
+ ossl_crypto_condvar_wait(tdata->cond_finished, tdata->lock);
+ tdata->active_threads++;
+ ossl_crypto_mutex_unlock(tdata->lock);
+
+ thread = ossl_crypto_thread_native_start(start, data, 1);
+ if (thread == NULL) {
+ ossl_crypto_mutex_lock(tdata->lock);
+ tdata->active_threads--;
+ ossl_crypto_mutex_unlock(tdata->lock);
+ goto fail;
+ }
+ thread->ctx = ctx;
+
+fail:
+ return (void *) thread;
+}
+
+int ossl_crypto_thread_join(void *vhandle, CRYPTO_THREAD_RETVAL *retval)
+{
+ CRYPTO_THREAD *handle = vhandle;
+ OSSL_LIB_CTX_THREADS *tdata;
+
+ if (vhandle == NULL)
+ return 0;
+
+ tdata = OSSL_LIB_CTX_GET_THREADS(handle->ctx);
+ if (tdata == NULL)
+ return 0;
+
+ if (ossl_crypto_thread_native_join(handle, retval) == 0)
+ return 0;
+
+ ossl_crypto_mutex_lock(tdata->lock);
+ tdata->active_threads--;
+ ossl_crypto_condvar_broadcast(tdata->cond_finished);
+ ossl_crypto_mutex_unlock(tdata->lock);
+ return 1;
+}
+
+int ossl_crypto_thread_clean(void *vhandle)
+{
+ CRYPTO_THREAD *handle = vhandle;
+
+ return ossl_crypto_thread_native_clean(handle);
+}
+
+#else
+
+ossl_inline uint64_t ossl_get_avail_threads(OSSL_LIB_CTX *ctx)
+{
+ return 0;
+}
+
+void *ossl_crypto_thread_start(OSSL_LIB_CTX *ctx, CRYPTO_THREAD_ROUTINE start,
+ void *data)
+{
+ return NULL;
+}
+
+int ossl_crypto_thread_join(void *vhandle, CRYPTO_THREAD_RETVAL *retval)
+{
+ return 0;
+}
+
+int ossl_crypto_thread_clean(void *vhandle)
+{
+ return 0;
+}
+
+#endif
+
+#if defined(OPENSSL_THREADS)
+
+void *ossl_threads_ctx_new(OSSL_LIB_CTX *ctx)
+{
+ struct openssl_threads_st *t = OPENSSL_zalloc(sizeof(*t));
+
+ if (t == NULL)
+ return NULL;
+
+ t->lock = ossl_crypto_mutex_new();
+ t->cond_finished = ossl_crypto_condvar_new();
+
+ if (t->lock == NULL || t->cond_finished == NULL)
+ goto fail;
+
+ return t;
+
+fail:
+ ossl_threads_ctx_free((void *)t);
+ return NULL;
+}
+
+void ossl_threads_ctx_free(void *vdata)
+{
+ OSSL_LIB_CTX_THREADS *t = (OSSL_LIB_CTX_THREADS *) vdata;
+
+ if (t == NULL)
+ return;
+
+ ossl_crypto_mutex_free(&t->lock);
+ ossl_crypto_condvar_free(&t->cond_finished);
+ OPENSSL_free(t);
+}
+
+#endif
CRYPTO_THREAD_run_once,
CRYPTO_THREAD_lock_new, CRYPTO_THREAD_read_lock, CRYPTO_THREAD_write_lock,
CRYPTO_THREAD_unlock, CRYPTO_THREAD_lock_free,
-CRYPTO_atomic_add, CRYPTO_atomic_or, CRYPTO_atomic_load - OpenSSL thread support
+CRYPTO_atomic_add, CRYPTO_atomic_or, CRYPTO_atomic_load,
+OSSL_set_max_threads, OSSL_get_max_threads,
+OSSL_get_thread_support_flags - OpenSSL thread support
=head1 SYNOPSIS
CRYPTO_RWLOCK *lock);
int CRYPTO_atomic_load(uint64_t *val, uint64_t *ret, CRYPTO_RWLOCK *lock);
+ int OSSL_set_max_threads(OSSL_LIB_CTX *ctx, uint64_t max_threads);
+ uint64_t OSSL_get_max_threads(OSSL_LIB_CTX *ctx);
+ uint32_t OSSL_get_thread_support_flags(void);
+
=head1 DESCRIPTION
OpenSSL can be safely used in multi-threaded applications provided that
the variable is read. If atomic operations are not supported and I<lock> is
NULL, then the function will fail.
+=item *
+
+OSSL_set_max_threads() sets the maximum number of threads to be used by the
+thread pool. If the argument is 0, thread pooling is disabled. OpenSSL will
+not create any threads and existing threads in the thread pool will be torn
+down. The maximum thread count is a limit, not a target. Threads will not be
+spawned unless (and until) there is demand. Thread polling is disabled by
+default. To enable threading you must call OSSL_set_max_threads() explicitly.
+Under no circumstances is this done for you.
+
=back
=head1 RETURN VALUES
CRYPTO_THREAD_lock_free() returns no value.
+OSSL_set_max_threads() returns 1 on success and 0 on failure. Returns failure
+if OpenSSL-managed thread pooling is not supported (for example, if it is not
+supported on the current platform, or because OpenSSL is not built with the
+necessary support).
+
+OSSL_get_max_threads() returns the maximum number of threads currently allowed
+to be used by the thread pool. If thread pooling is disabled or not available,
+returns 0.
+
The other functions return 1 on success, or 0 on error.
=head1 NOTES
void *ossl_rand_crng_ctx_new(OSSL_LIB_CTX *);
void *ossl_thread_event_ctx_new(OSSL_LIB_CTX *);
void *ossl_fips_prov_ossl_ctx_new(OSSL_LIB_CTX *);
+#if defined(OPENSSL_THREADS)
+void *ossl_threads_ctx_new(OSSL_LIB_CTX *);
+#endif
void ossl_provider_store_free(void *);
void ossl_property_string_data_free(void *);
void ossl_rand_crng_ctx_free(void *);
void ossl_thread_event_ctx_free(void *);
void ossl_fips_prov_ossl_ctx_free(void *);
+#if defined(OPENSSL_THREADS)
+void ossl_threads_ctx_free(void *);
+#endif
# define OSSL_LIB_CTX_PROVIDER_CONF_INDEX 16
# define OSSL_LIB_CTX_BIO_CORE_INDEX 17
# define OSSL_LIB_CTX_CHILD_PROVIDER_INDEX 18
-# define OSSL_LIB_CTX_MAX_INDEXES 19
+# define OSSL_LIB_CTX_THREAD_INDEX 19
+# define OSSL_LIB_CTX_MAX_INDEXES 20
OSSL_LIB_CTX *ossl_lib_ctx_get_concrete(OSSL_LIB_CTX *ctx);
int ossl_lib_ctx_is_default(OSSL_LIB_CTX *ctx);
--- /dev/null
+/*
+ * 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
+ */
+
+#ifndef OPENSSL_INTERNAL_THREAD_H
+# define OPENSSL_INTERNAL_THREAD_H
+# include <openssl/configuration.h>
+# include <internal/thread_arch.h>
+# include <openssl/e_os2.h>
+# include <openssl/types.h>
+# include <internal/cryptlib.h>
+# include "crypto/context.h"
+
+void *ossl_crypto_thread_start(OSSL_LIB_CTX *ctx, CRYPTO_THREAD_ROUTINE start,
+ void *data);
+int ossl_crypto_thread_join(void *task, CRYPTO_THREAD_RETVAL *retval);
+int ossl_crypto_thread_clean(void *vhandle);
+uint64_t ossl_get_avail_threads(OSSL_LIB_CTX *ctx);
+
+# if defined(OPENSSL_THREADS)
+
+# define OSSL_LIB_CTX_GET_THREADS(CTX) \
+ ossl_lib_ctx_get_data(CTX, OSSL_LIB_CTX_THREAD_INDEX);
+
+typedef struct openssl_threads_st {
+ uint64_t max_threads;
+ uint64_t active_threads;
+ CRYPTO_MUTEX *lock;
+ CRYPTO_CONDVAR *cond_finished;
+} OSSL_LIB_CTX_THREADS;
+
+# endif /* defined(OPENSSL_THREADS) */
+
+#endif /* OPENSSL_INTERNAL_THREAD_H */
--- /dev/null
+/*
+ * 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
+ */
+
+#ifndef OSSL_INTERNAL_THREAD_ARCH_H
+# define OSSL_INTERNAL_THREAD_ARCH_H
+# include <openssl/configuration.h>
+# include <openssl/e_os2.h>
+
+# if defined(_WIN32)
+# include <windows.h>
+# endif
+
+# if defined(OPENSSL_THREADS) && defined(OPENSSL_SYS_UNIX)
+# define OPENSSL_THREADS_POSIX
+# elif defined(OPENSSL_THREADS) && defined(OPENSSL_SYS_WINDOWS) && \
+ defined(_WIN32_WINNT)
+# if _WIN32_WINNT >= 0x0600
+# define OPENSSL_THREADS_WINNT
+# else
+# define OPENSSL_THREADS_NONE
+# endif
+# else
+# define OPENSSL_THREADS_NONE
+# endif
+
+# include <openssl/crypto.h>
+
+typedef void CRYPTO_MUTEX;
+typedef void CRYPTO_CONDVAR;
+
+CRYPTO_MUTEX *ossl_crypto_mutex_new(void);
+void ossl_crypto_mutex_lock(CRYPTO_MUTEX *mutex);
+int ossl_crypto_mutex_try_lock(CRYPTO_MUTEX *mutex);
+void ossl_crypto_mutex_unlock(CRYPTO_MUTEX *mutex);
+void ossl_crypto_mutex_free(CRYPTO_MUTEX **mutex);
+
+CRYPTO_CONDVAR *ossl_crypto_condvar_new(void);
+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);
+
+typedef uint32_t CRYPTO_THREAD_RETVAL;
+typedef CRYPTO_THREAD_RETVAL (*CRYPTO_THREAD_ROUTINE)(void *);
+typedef CRYPTO_THREAD_RETVAL (*CRYPTO_THREAD_ROUTINE_CB)(void *,
+ void (**)(void *),
+ void **);
+
+# define CRYPTO_THREAD_NO_STATE 0UL
+# define CRYPTO_THREAD_FINISHED (1UL << 1)
+# define CRYPTO_THREAD_JOINED (1UL << 2)
+# define CRYPTO_THREAD_TERMINATED (1UL << 3)
+
+# define CRYPTO_THREAD_GET_STATE(THREAD, FLAG) ((THREAD)->state & (FLAG))
+# define CRYPTO_THREAD_GET_ERROR(THREAD, FLAG) (((THREAD)->state >> 16) & (FLAG))
+
+typedef struct crypto_thread_st {
+ uint32_t state;
+ void *data;
+ CRYPTO_THREAD_ROUTINE routine;
+ CRYPTO_THREAD_RETVAL retval;
+ void *handle;
+ CRYPTO_MUTEX *lock;
+ CRYPTO_MUTEX *statelock;
+ CRYPTO_CONDVAR *condvar;
+ unsigned long thread_id;
+ int joinable;
+ OSSL_LIB_CTX *ctx;
+} CRYPTO_THREAD;
+
+# if defined(OPENSSL_THREADS)
+
+# define CRYPTO_THREAD_UNSET_STATE(THREAD, FLAG) \
+ do { \
+ (THREAD)->state &= ~(FLAG); \
+ } while ((void)0, 0)
+
+# define CRYPTO_THREAD_SET_STATE(THREAD, FLAG) \
+ do { \
+ (THREAD)->state |= (FLAG); \
+ } while ((void)0, 0)
+
+# define CRYPTO_THREAD_SET_ERROR(THREAD, FLAG) \
+ do { \
+ (THREAD)->state |= ((FLAG) << 16); \
+ } while ((void)0, 0)
+
+# define CRYPTO_THREAD_UNSET_ERROR(THREAD, FLAG) \
+ do { \
+ (THREAD)->state &= ~((FLAG) << 16); \
+ } while ((void)0, 0)
+
+# else
+
+# define CRYPTO_THREAD_UNSET_STATE(THREAD, FLAG)
+# define CRYPTO_THREAD_SET_STATE(THREAD, FLAG)
+# define CRYPTO_THREAD_SET_ERROR(THREAD, FLAG)
+# define CRYPTO_THREAD_UNSET_ERROR(THREAD, FLAG)
+
+# endif /* defined(OPENSSL_THREADS) */
+
+CRYPTO_THREAD * ossl_crypto_thread_native_start(CRYPTO_THREAD_ROUTINE routine,
+ void *data, int joinable);
+int ossl_crypto_thread_native_spawn(CRYPTO_THREAD *thread);
+int ossl_crypto_thread_native_join(CRYPTO_THREAD *thread,
+ CRYPTO_THREAD_RETVAL *retval);
+int ossl_crypto_thread_native_terminate(CRYPTO_THREAD *thread);
+int ossl_crypto_thread_native_exit(void);
+int ossl_crypto_thread_native_is_self(CRYPTO_THREAD *thread);
+int ossl_crypto_thread_native_clean(CRYPTO_THREAD *thread);
+
+void ossl_crypto_mem_barrier(void);
+
+#endif /* OSSL_INTERNAL_THREAD_ARCH_H */
--- /dev/null
+/*
+ * Copyright 1995-2022 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright (c) 2002, Oracle and/or its affiliates. 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
+ */
+
+#ifndef OPENSSL_THREAD_H
+# define OPENSSL_THREAD_H
+
+# define OSSL_THREAD_SUPPORT_FLAG_THREAD_POOL (1U<<0)
+# define OSSL_THREAD_SUPPORT_FLAG_DEFAULT_SPAWN (1U<<1)
+
+# include <openssl/types.h>
+
+uint32_t OSSL_get_thread_support_flags(void);
+int OSSL_set_max_threads(OSSL_LIB_CTX *ctx, uint64_t max_threads);
+uint64_t OSSL_get_max_threads(OSSL_LIB_CTX *ctx);
+
+#endif /* OPENSSL_THREAD_H */
DEPEND[ct_test]=../libcrypto libtestutil.a
SOURCE[threadstest]=threadstest.c
- INCLUDE[threadstest]=../include ../apps/include
- DEPEND[threadstest]=../libcrypto libtestutil.a
+ INCLUDE[threadstest]=.. ../include ../apps/include
+ DEPEND[threadstest]=../libcrypto.a libtestutil.a
SOURCE[threadstest_fips]=threadstest_fips.c
INCLUDE[threadstest_fips]=../include ../apps/include
#endif
#include <string.h>
+#include <internal/cryptlib.h>
+#include <internal/thread_arch.h>
+#include <internal/thread.h>
#include <openssl/crypto.h>
#include <openssl/rsa.h>
#include <openssl/aes.h>
#include <openssl/err.h>
#include <openssl/rand.h>
+#include <openssl/thread.h>
#include "internal/tsan_assist.h"
#include "internal/nelem.h"
#include "testutil.h"
}
#endif
+static int test_thread_reported_flags(void)
+{
+ uint32_t flags = OSSL_get_thread_support_flags();
+
+#if !defined(OPENSSL_THREADS)
+ if (!TEST_int_eq(flags, 0))
+ return 0;
+#endif
+
+#if defined(OPENSSL_NO_THREAD_POOL)
+ if (!TEST_int_eq(flags & OSSL_THREAD_SUPPORT_FLAG_THREAD_POOL, 0))
+ return 0;
+#else
+ if (!TEST_int_eq(flags & OSSL_THREAD_SUPPORT_FLAG_THREAD_POOL,
+ OSSL_THREAD_SUPPORT_FLAG_THREAD_POOL))
+ return 0;
+#endif
+
+#if defined(OPENSSL_NO_DEFAULT_THREAD_POOL)
+ if (!TEST_int_eq(flags & OSSL_THREAD_SUPPORT_FLAG_DEFAULT_SPAWN, 0))
+ return 0;
+#else
+ if (!TEST_int_eq(flags & OSSL_THREAD_SUPPORT_FLAG_DEFAULT_SPAWN,
+ OSSL_THREAD_SUPPORT_FLAG_DEFAULT_SPAWN))
+ return 0;
+#endif
+
+ return 1;
+}
+
+#if defined(OPENSSL_THREADS)
+
+# define TEST_THREAD_NATIVE_FN_SET_VALUE 1
+static uint32_t test_thread_native_fn(void *data)
+{
+ uint32_t *ldata = (uint32_t*) data;
+ *ldata = *ldata + 1;
+ return *ldata - 1;
+}
+
+static uint32_t test_thread_noreturn(void *data)
+{
+ CRYPTO_MUTEX *lock = (uint32_t*) data;
+
+ /* lock is assumed to be locked */
+ ossl_crypto_mutex_lock(lock);
+
+ /* unreachable */
+ OPENSSL_die("test_thread_noreturn", __FILE__, __LINE__);
+ return 0;
+}
+
+/* Tests of native threads */
+
+static int test_thread_native(void)
+{
+ int testval = 0;
+ uint32_t retval;
+ uint32_t local;
+ CRYPTO_THREAD *t;
+ CRYPTO_MUTEX *lock;
+
+ /* thread spawn, join and termination */
+
+ local = 1;
+ t = ossl_crypto_thread_native_start(test_thread_native_fn, &local, 1);
+ if (!TEST_ptr(t))
+ return 0;
+
+ /*
+ * pthread_join results in undefined behaviour if called on a joined
+ * thread. We do not impose such restrictions, so it's up to us to
+ * ensure that this does not happen (thread sanitizer will warn us
+ * if we do).
+ */
+ if (!TEST_int_eq(ossl_crypto_thread_native_join(t, &retval), 1))
+ return 0;
+ if (!TEST_int_eq(ossl_crypto_thread_native_join(t, &retval), 1))
+ return 0;
+
+ if (!TEST_int_eq(retval, 1) || !TEST_int_eq(local, 2))
+ return 0;
+
+ if (!TEST_int_eq(ossl_crypto_thread_native_terminate(t), 1))
+ return 0;
+ if (!TEST_int_eq(ossl_crypto_thread_native_terminate(t), 1))
+ return 0;
+
+ if (!TEST_int_eq(ossl_crypto_thread_native_join(t, &retval), 1))
+ return 0;
+
+ if (!TEST_int_eq(ossl_crypto_thread_native_clean(t), 1))
+ return 0;
+ t = NULL;
+
+ if (!TEST_int_eq(ossl_crypto_thread_native_clean(t), 0))
+ return 0;
+
+ /* termination of a long running thread */
+
+ lock = ossl_crypto_mutex_new();
+ if (!TEST_ptr(lock))
+ return 0;
+ ossl_crypto_mutex_lock(lock);
+
+ t = ossl_crypto_thread_native_start(test_thread_noreturn, lock, 1);
+ if (!TEST_ptr(t))
+ goto fail;
+ if (!TEST_int_eq(ossl_crypto_thread_native_terminate(t), 1))
+ goto fail;
+ if (!TEST_int_eq(ossl_crypto_thread_native_clean(t), 1))
+ goto fail;
+
+ testval = 1;
+
+fail:
+ ossl_crypto_mutex_unlock(lock);
+ ossl_crypto_mutex_free(&lock);
+ if (!TEST_ptr_null(lock))
+ return 0;
+
+ return testval;
+}
+
+#if !defined(OPENSSL_NO_DEFAULT_THREAD_POOL)
+static int test_thread_internal(void)
+{
+ uint32_t retval[3];
+ uint32_t local[3] = { 0 };
+ uint32_t threads_supported;
+ size_t i;
+ void *t[3];
+ OSSL_LIB_CTX *cust_ctx = OSSL_LIB_CTX_new();
+
+ threads_supported = OSSL_get_thread_support_flags();
+ threads_supported &= OSSL_THREAD_SUPPORT_FLAG_DEFAULT_SPAWN;
+
+ if (threads_supported == 0) {
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(NULL), 0))
+ return 0;
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(cust_ctx), 0))
+ return 0;
+
+ if (!TEST_int_eq(OSSL_set_max_threads(NULL, 1), 0))
+ return 0;
+ if (!TEST_int_eq(OSSL_set_max_threads(cust_ctx, 1), 0))
+ return 0;
+
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(NULL), 0))
+ return 0;
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(cust_ctx), 0))
+ return 0;
+
+ t[0] = ossl_crypto_thread_start(NULL, test_thread_native_fn, &local[0]);
+ if (!TEST_ptr_null(t[0]))
+ return 0;
+
+ return 1;
+ }
+
+ /* fail when not allowed to use threads */
+
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(NULL), 0))
+ return 0;
+ t[0] = ossl_crypto_thread_start(NULL, test_thread_native_fn, &local[0]);
+ if (!TEST_ptr_null(t[0]))
+ return 0;
+
+ /* fail when enabled on a different context */
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(cust_ctx), 0))
+ return 0;
+ if (!TEST_int_eq(OSSL_set_max_threads(cust_ctx, 1), 1))
+ return 0;
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(NULL), 0))
+ return 0;
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(cust_ctx), 1))
+ return 0;
+ t[0] = ossl_crypto_thread_start(NULL, test_thread_native_fn, &local[0]);
+ if (!TEST_ptr_null(t[0]))
+ return 0;
+ if (!TEST_int_eq(OSSL_set_max_threads(cust_ctx, 0), 1))
+ return 0;
+
+ /* sequential startup */
+
+ if (!TEST_int_eq(OSSL_set_max_threads(NULL, 1), 1))
+ return 0;
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(NULL), 1))
+ return 0;
+ if (!TEST_uint64_t_eq(OSSL_get_max_threads(cust_ctx), 0))
+ return 0;
+
+ for (i = 0; i < OSSL_NELEM(t); ++i) {
+ local[0] = i + 1;
+
+ t[i] = ossl_crypto_thread_start(NULL, test_thread_native_fn, &local[0]);
+ if (!TEST_ptr(t[i]))
+ return 0;
+
+ /*
+ * pthread_join results in undefined behaviour if called on a joined
+ * thread. We do not impose such restrictions, so it's up to us to
+ * ensure that this does not happen (thread sanitizer will warn us
+ * if we do).
+ */
+ if (!TEST_int_eq(ossl_crypto_thread_join(t[i], &retval[0]), 1))
+ return 0;
+ if (!TEST_int_eq(ossl_crypto_thread_join(t[i], &retval[0]), 1))
+ return 0;
+
+ if (!TEST_int_eq(retval[0], i + 1) || !TEST_int_eq(local[0], i + 2))
+ return 0;
+
+ if (!TEST_int_eq(ossl_crypto_thread_clean(t[i]), 1))
+ return 0;
+ t[i] = NULL;
+
+ if (!TEST_int_eq(ossl_crypto_thread_clean(t[i]), 0))
+ return 0;
+ }
+
+ /* parallel startup */
+
+ if (!TEST_int_eq(OSSL_set_max_threads(NULL, OSSL_NELEM(t)), 1))
+ return 0;
+
+ for (i = 0; i < OSSL_NELEM(t); ++i) {
+ local[i] = i + 1;
+ t[i] = ossl_crypto_thread_start(NULL, test_thread_native_fn, &local[i]);
+ if (!TEST_ptr(t[i]))
+ return 0;
+ }
+ for (i = 0; i < OSSL_NELEM(t); ++i) {
+ if (!TEST_int_eq(ossl_crypto_thread_join(t[i], &retval[i]), 1))
+ return 0;
+ }
+ for (i = 0; i < OSSL_NELEM(t); ++i) {
+ if (!TEST_int_eq(retval[i], i + 1) || !TEST_int_eq(local[i], i + 2))
+ return 0;
+ if (!TEST_int_eq(ossl_crypto_thread_clean(t[i]), 1))
+ return 0;
+ }
+
+ /* parallel startup, bottleneck */
+
+ if (!TEST_int_eq(OSSL_set_max_threads(NULL, OSSL_NELEM(t) - 1), 1))
+ return 0;
+
+ for (i = 0; i < OSSL_NELEM(t); ++i) {
+ local[i] = i + 1;
+ t[i] = ossl_crypto_thread_start(NULL, test_thread_native_fn, &local[i]);
+ if (!TEST_ptr(t[i]))
+ return 0;
+ }
+ for (i = 0; i < OSSL_NELEM(t); ++i) {
+ if (!TEST_int_eq(ossl_crypto_thread_join(t[i], &retval[i]), 1))
+ return 0;
+ }
+ for (i = 0; i < OSSL_NELEM(t); ++i) {
+ if (!TEST_int_eq(retval[i], i + 1) || !TEST_int_eq(local[i], i + 2))
+ return 0;
+ if (!TEST_int_eq(ossl_crypto_thread_clean(t[i]), 1))
+ return 0;
+ }
+
+ if (!TEST_int_eq(OSSL_set_max_threads(NULL, 0), 1))
+ return 0;
+
+ OSSL_LIB_CTX_free(cust_ctx);
+ return 1;
+}
+#endif
+
+static uint32_t test_thread_native_multiple_joins_fn1(void *data)
+{
+ return 0;
+}
+
+static uint32_t test_thread_native_multiple_joins_fn2(void *data)
+{
+ ossl_crypto_thread_native_join((CRYPTO_THREAD *)data, NULL);
+ return 0;
+}
+
+static uint32_t test_thread_native_multiple_joins_fn3(void *data)
+{
+ ossl_crypto_thread_native_join((CRYPTO_THREAD *)data, NULL);
+ return 0;
+}
+
+static int test_thread_native_multiple_joins(void)
+{
+ CRYPTO_THREAD *t, *t1, *t2;
+
+ t = ossl_crypto_thread_native_start(test_thread_native_multiple_joins_fn1, NULL, 1);
+ t1 = ossl_crypto_thread_native_start(test_thread_native_multiple_joins_fn2, t, 1);
+ t2 = ossl_crypto_thread_native_start(test_thread_native_multiple_joins_fn3, t, 1);
+
+ if (!TEST_ptr(t) || !TEST_ptr(t1) || !TEST_ptr(t2))
+ return 0;
+
+ if (!TEST_int_eq(ossl_crypto_thread_native_join(t2, NULL), 1))
+ return 0;
+ if (!TEST_int_eq(ossl_crypto_thread_native_join(t1, NULL), 1))
+ return 0;
+
+ if (!TEST_int_eq(ossl_crypto_thread_native_clean(t2), 1))
+ return 0;
+
+ if (!TEST_int_eq(ossl_crypto_thread_native_clean(t1), 1))
+ return 0;
+
+ if (!TEST_int_eq(ossl_crypto_thread_native_clean(t), 1))
+ return 0;
+
+ return 1;
+}
+
+#endif
+
typedef enum OPTION_choice {
OPT_ERR = -1,
OPT_EOF = 0,
#if !defined(OPENSSL_NO_DGRAM) && !defined(OPENSSL_NO_SOCK)
ADD_TEST(test_bio_dgram_pair);
#endif
+ ADD_TEST(test_thread_reported_flags);
+#if defined(OPENSSL_THREADS)
+ ADD_TEST(test_thread_native);
+ ADD_TEST(test_thread_native_multiple_joins);
+#if !defined(OPENSSL_NO_DEFAULT_THREAD_POOL)
+ ADD_TEST(test_thread_internal);
+#endif
+#endif
+
return 1;
}
PKCS12_SAFEBAG_set0_attrs ? 3_2_0 EXIST::FUNCTION:
PKCS12_create_ex2 ? 3_2_0 EXIST::FUNCTION:
OSSL_sleep ? 3_2_0 EXIST::FUNCTION:
+OSSL_get_thread_support_flags ? 3_2_0 EXIST::FUNCTION:
+OSSL_set_max_threads ? 3_2_0 EXIST::FUNCTION:
+OSSL_get_max_threads ? 3_2_0 EXIST::FUNCTION: