Home | History | Annotate | Line # | Download | only in thread
      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