Split thread intialisation and handling out of init.c
[openssl.git] / crypto / initthread.c
1 /*
2  * Copyright 2019 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/crypto.h>
11 #include "internal/cryptlib_int.h"
12
13 typedef struct thread_event_handler_st THREAD_EVENT_HANDLER;
14 struct thread_event_handler_st {
15     OPENSSL_CTX *ctx;
16     ossl_thread_stop_handler_fn handfn;
17     THREAD_EVENT_HANDLER *next;
18 };
19
20 /*
21  * Since per-thread-specific-data destructors are not universally
22  * available, i.e. not on Windows, only below CRYPTO_THREAD_LOCAL key
23  * is assumed to have destructor associated. And then an effort is made
24  * to call this single destructor on non-pthread platform[s].
25  *
26  * Initial value is "impossible". It is used as guard value to shortcut
27  * destructor for threads terminating before libcrypto is initialized or
28  * after it's de-initialized. Access to the key doesn't have to be
29  * serialized for the said threads, because they didn't use libcrypto
30  * and it doesn't matter if they pick "impossible" or derefernce real
31  * key value and pull NULL past initialization in the first thread that
32  * intends to use libcrypto.
33  */
34 static union {
35     long sane;
36     CRYPTO_THREAD_LOCAL value;
37 } destructor_key = { -1 };
38
39 static void ossl_init_thread_stop(THREAD_EVENT_HANDLER **hands);
40
41 static void ossl_init_thread_destructor(void *hands)
42 {
43     ossl_init_thread_stop((THREAD_EVENT_HANDLER **)hands);
44 }
45
46 int init_thread(void)
47 {
48
49     if (!CRYPTO_THREAD_init_local(&destructor_key.value,
50                                   ossl_init_thread_destructor))
51         return 0;
52
53     return 1;
54 }
55
56 void cleanup_thread(void)
57 {
58     CRYPTO_THREAD_cleanup_local(&destructor_key.value);
59     destructor_key.sane = -1;
60 }
61
62 static THREAD_EVENT_HANDLER **ossl_init_get_thread_local(int alloc)
63 {
64     THREAD_EVENT_HANDLER **hands =
65         CRYPTO_THREAD_get_local(&destructor_key.value);
66
67     if (alloc) {
68         if (hands == NULL
69             && (hands = OPENSSL_zalloc(sizeof(*hands))) != NULL
70             && !CRYPTO_THREAD_set_local(&destructor_key.value, hands)) {
71             OPENSSL_free(hands);
72             return NULL;
73         }
74     } else {
75         CRYPTO_THREAD_set_local(&destructor_key.value, NULL);
76     }
77
78     return hands;
79 }
80
81 static void ossl_init_thread_stop(THREAD_EVENT_HANDLER **hands)
82 {
83     THREAD_EVENT_HANDLER *curr, *prev = NULL;
84
85     /* Can't do much about this */
86     if (hands == NULL)
87         return;
88
89     curr = *hands;
90     while (curr != NULL) {
91         curr->handfn(curr->ctx);
92         prev = curr;
93         curr = curr->next;
94         OPENSSL_free(prev);
95     }
96
97     OPENSSL_free(hands);
98 }
99
100 void OPENSSL_thread_stop(void)
101 {
102     if (destructor_key.sane != -1)
103         ossl_init_thread_stop(ossl_init_get_thread_local(0));
104 }
105
106 int ossl_init_thread_start(OPENSSL_CTX *ctx, ossl_thread_stop_handler_fn handfn)
107 {
108     THREAD_EVENT_HANDLER **hands;
109     THREAD_EVENT_HANDLER *hand;
110
111     if (!OPENSSL_init_crypto(0, NULL))
112         return 0;
113
114     hands = ossl_init_get_thread_local(1);
115
116     if (hands == NULL)
117         return 0;
118
119     hand = OPENSSL_malloc(sizeof(*hand));
120     if (hand == NULL)
121         return 0;
122
123     hand->handfn = handfn;
124     hand->ctx = ctx;
125     hand->next = *hands;
126     *hands = hand;
127
128     return 1;
129 }