1 /* 2 * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the Apache License 2.0 (the "License"). You may not use 5 * this file except in compliance with the License. You can obtain a copy 6 * in the file LICENSE in the source distribution or at 7 * https://www.openssl.org/source/license.html 8 */ 9 10 #include <openssl/configuration.h> 11 #include <internal/thread_arch.h> 12 13 CRYPTO_THREAD *ossl_crypto_thread_native_start(CRYPTO_THREAD_ROUTINE routine, 14 void *data, int joinable) 15 { 16 CRYPTO_THREAD *handle; 17 18 if (routine == NULL) 19 return NULL; 20 21 handle = OPENSSL_zalloc(sizeof(*handle)); 22 if (handle == NULL) 23 return NULL; 24 25 if ((handle->lock = ossl_crypto_mutex_new()) == NULL) 26 goto fail; 27 if ((handle->statelock = ossl_crypto_mutex_new()) == NULL) 28 goto fail; 29 if ((handle->condvar = ossl_crypto_condvar_new()) == NULL) 30 goto fail; 31 32 handle->data = data; 33 handle->routine = routine; 34 handle->joinable = joinable; 35 36 if (ossl_crypto_thread_native_spawn(handle) == 1) 37 return handle; 38 39 fail: 40 ossl_crypto_condvar_free(&handle->condvar); 41 ossl_crypto_mutex_free(&handle->statelock); 42 ossl_crypto_mutex_free(&handle->lock); 43 OPENSSL_free(handle); 44 return NULL; 45 } 46 47 int ossl_crypto_thread_native_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval) 48 { 49 uint64_t req_state_mask; 50 51 if (thread == NULL) 52 return 0; 53 54 ossl_crypto_mutex_lock(thread->statelock); 55 req_state_mask = CRYPTO_THREAD_FINISHED | CRYPTO_THREAD_JOINED; 56 while (!CRYPTO_THREAD_GET_STATE(thread, req_state_mask)) 57 ossl_crypto_condvar_wait(thread->condvar, thread->statelock); 58 59 if (CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_JOINED)) 60 goto pass; 61 62 /* Await concurrent join completion, if any. */ 63 while (CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_JOIN_AWAIT)) { 64 if (!CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_JOINED)) 65 ossl_crypto_condvar_wait(thread->condvar, thread->statelock); 66 if (CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_JOINED)) 67 goto pass; 68 } 69 CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_JOIN_AWAIT); 70 ossl_crypto_mutex_unlock(thread->statelock); 71 72 if (ossl_crypto_thread_native_perform_join(thread, retval) == 0) 73 goto fail; 74 75 ossl_crypto_mutex_lock(thread->statelock); 76 pass: 77 CRYPTO_THREAD_UNSET_ERROR(thread, CRYPTO_THREAD_JOINED); 78 CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_JOINED); 79 80 /* 81 * Signal join completion. It is important to signal even if we haven't 82 * performed an actual join. Multiple threads could be awaiting the 83 * CRYPTO_THREAD_JOIN_AWAIT -> CRYPTO_THREAD_JOINED transition, but signal 84 * on actual join would wake only one. Signalling here will always wake one. 85 */ 86 ossl_crypto_condvar_signal(thread->condvar); 87 ossl_crypto_mutex_unlock(thread->statelock); 88 89 if (retval != NULL) 90 *retval = thread->retval; 91 return 1; 92 93 fail: 94 ossl_crypto_mutex_lock(thread->statelock); 95 CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_JOINED); 96 97 /* Have another thread that's awaiting join retry to avoid that 98 * thread deadlock. */ 99 CRYPTO_THREAD_UNSET_STATE(thread, CRYPTO_THREAD_JOIN_AWAIT); 100 ossl_crypto_condvar_signal(thread->condvar); 101 102 ossl_crypto_mutex_unlock(thread->statelock); 103 return 0; 104 } 105 106 int ossl_crypto_thread_native_clean(CRYPTO_THREAD *handle) 107 { 108 uint64_t req_state_mask; 109 110 if (handle == NULL) 111 return 0; 112 113 req_state_mask = 0; 114 req_state_mask |= CRYPTO_THREAD_FINISHED; 115 req_state_mask |= CRYPTO_THREAD_JOINED; 116 117 ossl_crypto_mutex_lock(handle->statelock); 118 if (CRYPTO_THREAD_GET_STATE(handle, req_state_mask) == 0) { 119 ossl_crypto_mutex_unlock(handle->statelock); 120 return 0; 121 } 122 ossl_crypto_mutex_unlock(handle->statelock); 123 124 ossl_crypto_mutex_free(&handle->lock); 125 ossl_crypto_mutex_free(&handle->statelock); 126 ossl_crypto_condvar_free(&handle->condvar); 127 128 OPENSSL_free(handle->handle); 129 OPENSSL_free(handle); 130 131 return 1; 132 } 133