crypto: add preemptive threading support
[openssl.git] / crypto / thread / arch / thread_win.c
1 /*
2  * Copyright 2019-2021 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 <internal/thread_arch.h>
11
12 #if defined(OPENSSL_THREADS_WINNT)
13 # include <process.h>
14 # include <windows.h>
15
16 static DWORD __stdcall thread_start_thunk(LPVOID vthread)
17 {
18     CRYPTO_THREAD *thread;
19     CRYPTO_THREAD_RETVAL ret;
20
21     thread = (CRYPTO_THREAD *)vthread;
22
23     thread->thread_id = GetCurrentThreadId();
24
25     ret = thread->routine(thread->data);
26     ossl_crypto_mutex_lock(thread->statelock);
27     CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_FINISHED);
28     thread->retval = ret;
29     ossl_crypto_condvar_broadcast(thread->condvar);
30     ossl_crypto_mutex_unlock(thread->statelock);
31
32     return 0;
33 }
34
35 int ossl_crypto_thread_native_spawn(CRYPTO_THREAD *thread)
36 {
37     HANDLE *handle;
38
39     handle = OPENSSL_zalloc(sizeof(*handle));
40     if (handle == NULL)
41         goto fail;
42
43     *handle = (HANDLE)_beginthreadex(NULL, 0, &thread_start_thunk, thread, 0, NULL);
44     if (*handle == NULL)
45         goto fail;
46
47     thread->handle = handle;
48     return 1;
49
50 fail:
51     thread->handle = NULL;
52     OPENSSL_free(handle);
53     return 0;
54 }
55
56 int ossl_crypto_thread_native_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval)
57 {
58     int req_state_mask;
59     DWORD thread_retval;
60     HANDLE *handle;
61
62     if (thread == NULL)
63         return 0;
64
65     req_state_mask = CRYPTO_THREAD_TERMINATED | CRYPTO_THREAD_JOINED;
66
67     ossl_crypto_mutex_lock(thread->statelock);
68     if (CRYPTO_THREAD_GET_STATE(thread, req_state_mask))
69         goto pass;
70     while (!CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_FINISHED))
71         ossl_crypto_condvar_wait(thread->condvar, thread->statelock);
72
73     handle = (HANDLE *) thread->handle;
74     if (handle == NULL)
75         goto fail;
76
77     if (WaitForSingleObject(*handle, INFINITE) != WAIT_OBJECT_0)
78         goto fail;
79
80     if (GetExitCodeThread(*handle, &thread_retval) == 0)
81         goto fail;
82
83     /*
84      * GetExitCodeThread call followed by this check is to make sure that
85      * the thread exitted properly. In particular, thread_retval may be
86      * non-zero when exitted via explicit ExitThread/TerminateThread or
87      * if the thread is still active (returns STILL_ACTIVE (259)).
88      */
89     if (thread_retval != 0)
90         goto fail;
91
92     if (CloseHandle(*handle) == 0)
93         goto fail;
94
95 pass:
96     if (retval != NULL)
97         *retval = thread->retval;
98
99     CRYPTO_THREAD_UNSET_ERROR(thread, CRYPTO_THREAD_JOINED);
100     CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_JOINED);
101     ossl_crypto_mutex_unlock(thread->statelock);
102     return 1;
103
104 fail:
105     CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_JOINED);
106     ossl_crypto_mutex_unlock(thread->statelock);
107     return 0;
108 }
109
110 int ossl_crypto_thread_native_terminate(CRYPTO_THREAD *thread)
111 {
112     uint64_t mask;
113     HANDLE *handle;
114
115     mask = CRYPTO_THREAD_FINISHED;
116     mask |= CRYPTO_THREAD_TERMINATED;
117     mask |= CRYPTO_THREAD_JOINED;
118
119     if (thread == NULL)
120         return 1;
121
122     ossl_crypto_mutex_lock(thread->statelock);
123     if (thread->handle == NULL || CRYPTO_THREAD_GET_STATE(thread, mask))
124         goto terminated;
125     ossl_crypto_mutex_unlock(thread->statelock);
126
127     handle = thread->handle;
128     if (WaitForSingleObject(*handle, 0) != WAIT_OBJECT_0) {
129         if (TerminateThread(*handle, STILL_ACTIVE) == 0) {
130             ossl_crypto_mutex_lock(thread->statelock);
131             CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_TERMINATED);
132             ossl_crypto_mutex_unlock(thread->statelock);
133             return 0;
134         }
135     }
136
137     if (CloseHandle(*handle) == 0) {
138         CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_TERMINATED);
139         return 0;
140     }
141
142     thread->handle = NULL;
143     OPENSSL_free(handle);
144
145     ossl_crypto_mutex_lock(thread->statelock);
146 terminated:
147     CRYPTO_THREAD_UNSET_ERROR(thread, CRYPTO_THREAD_TERMINATED);
148     CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_TERMINATED);
149     ossl_crypto_mutex_unlock(thread->statelock);
150     return 1;
151 }
152
153 int ossl_crypto_thread_native_exit(void)
154 {
155     _endthreadex(0);
156     return 1;
157 }
158
159 int ossl_crypto_thread_native_is_self(CRYPTO_THREAD *thread)
160 {
161     return thread->thread_id == GetCurrentThreadId();
162 }
163
164 CRYPTO_MUTEX *ossl_crypto_mutex_new(void)
165 {
166     CRITICAL_SECTION *mutex;
167
168     if ((mutex = OPENSSL_zalloc(sizeof(*mutex))) == NULL)
169         return NULL;
170     InitializeCriticalSection(mutex);
171     return (CRYPTO_MUTEX *)mutex;
172 }
173
174 void ossl_crypto_mutex_lock(CRYPTO_MUTEX *mutex)
175 {
176     CRITICAL_SECTION *mutex_p;
177
178     mutex_p = (CRITICAL_SECTION *)mutex;
179     EnterCriticalSection(mutex_p);
180 }
181
182 int ossl_crypto_mutex_try_lock(CRYPTO_MUTEX *mutex)
183 {
184     CRITICAL_SECTION *mutex_p;
185
186     mutex_p = (CRITICAL_SECTION *)mutex;
187     if (TryEnterCriticalSection(mutex_p))
188         return 1;
189
190     return 0;
191 }
192
193 void ossl_crypto_mutex_unlock(CRYPTO_MUTEX *mutex)
194 {
195     CRITICAL_SECTION *mutex_p;
196
197     mutex_p = (CRITICAL_SECTION *)mutex;
198     LeaveCriticalSection(mutex_p);
199 }
200
201 void ossl_crypto_mutex_free(CRYPTO_MUTEX **mutex)
202 {
203     CRITICAL_SECTION **mutex_p;
204
205     mutex_p = (CRITICAL_SECTION **)mutex;
206     if (*mutex_p != NULL)
207         DeleteCriticalSection(*mutex_p);
208     OPENSSL_free(*mutex_p);
209     *mutex = NULL;
210 }
211
212 CRYPTO_CONDVAR *ossl_crypto_condvar_new(void)
213 {
214     CONDITION_VARIABLE *cv_p;
215
216     if ((cv_p = OPENSSL_zalloc(sizeof(*cv_p))) == NULL)
217         return NULL;
218     InitializeConditionVariable(cv_p);
219     return (CRYPTO_CONDVAR *)cv_p;
220 }
221
222 void ossl_crypto_condvar_wait(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex)
223 {
224     CONDITION_VARIABLE *cv_p;
225     CRITICAL_SECTION *mutex_p;
226
227     cv_p = (CONDITION_VARIABLE *)cv;
228     mutex_p = (CRITICAL_SECTION *)mutex;
229     SleepConditionVariableCS(cv_p, mutex_p, INFINITE);
230 }
231
232 void ossl_crypto_condvar_broadcast(CRYPTO_CONDVAR *cv)
233 {
234     CONDITION_VARIABLE *cv_p;
235
236     cv_p = (CONDITION_VARIABLE *)cv;
237     WakeAllConditionVariable(cv_p);
238 }
239
240 void ossl_crypto_condvar_free(CRYPTO_CONDVAR **cv)
241 {
242     CONDITION_VARIABLE **cv_p;
243
244     cv_p = (CONDITION_VARIABLE **)cv;
245     OPENSSL_free(*cv_p);
246     *cv_p = NULL;
247 }
248
249 void ossl_crypto_mem_barrier(void)
250 {
251     MemoryBarrier();
252 }
253
254 #endif