]> git.ipfire.org Git - thirdparty/openssl.git/blame - crypto/thread/arch/thread_win.c
Copyright year updates
[thirdparty/openssl.git] / crypto / thread / arch / thread_win.c
CommitLineData
4574a7fd 1/*
da1c088f 2 * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
4574a7fd
ČK
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
fae5a155 16static unsigned __stdcall thread_start_thunk(LPVOID vthread)
4574a7fd
ČK
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;
1dd04a0f 29 ossl_crypto_condvar_signal(thread->condvar);
4574a7fd
ČK
30 ossl_crypto_mutex_unlock(thread->statelock);
31
32 return 0;
33}
34
35int 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
50fail:
51 thread->handle = NULL;
52 OPENSSL_free(handle);
53 return 0;
54}
55
4e43bc06 56int ossl_crypto_thread_native_perform_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval)
4574a7fd 57{
4574a7fd
ČK
58 DWORD thread_retval;
59 HANDLE *handle;
60
4e43bc06 61 if (thread == NULL || thread->handle == NULL)
4574a7fd
ČK
62 return 0;
63
4574a7fd 64 handle = (HANDLE *) thread->handle;
4574a7fd 65 if (WaitForSingleObject(*handle, INFINITE) != WAIT_OBJECT_0)
4e43bc06 66 return 0;
4574a7fd
ČK
67
68 if (GetExitCodeThread(*handle, &thread_retval) == 0)
4e43bc06 69 return 0;
4574a7fd
ČK
70
71 /*
72 * GetExitCodeThread call followed by this check is to make sure that
eb4129e1
DP
73 * the thread exited properly. In particular, thread_retval may be
74 * non-zero when exited via explicit ExitThread/TerminateThread or
4574a7fd
ČK
75 * if the thread is still active (returns STILL_ACTIVE (259)).
76 */
77 if (thread_retval != 0)
4e43bc06 78 return 0;
4574a7fd
ČK
79
80 if (CloseHandle(*handle) == 0)
4e43bc06 81 return 0;
4574a7fd 82
4574a7fd 83 return 1;
4574a7fd
ČK
84}
85
4574a7fd
ČK
86int ossl_crypto_thread_native_exit(void)
87{
88 _endthreadex(0);
89 return 1;
90}
91
92int ossl_crypto_thread_native_is_self(CRYPTO_THREAD *thread)
93{
94 return thread->thread_id == GetCurrentThreadId();
95}
96
97CRYPTO_MUTEX *ossl_crypto_mutex_new(void)
98{
99 CRITICAL_SECTION *mutex;
100
101 if ((mutex = OPENSSL_zalloc(sizeof(*mutex))) == NULL)
102 return NULL;
103 InitializeCriticalSection(mutex);
104 return (CRYPTO_MUTEX *)mutex;
105}
106
107void ossl_crypto_mutex_lock(CRYPTO_MUTEX *mutex)
108{
109 CRITICAL_SECTION *mutex_p;
110
111 mutex_p = (CRITICAL_SECTION *)mutex;
112 EnterCriticalSection(mutex_p);
113}
114
115int ossl_crypto_mutex_try_lock(CRYPTO_MUTEX *mutex)
116{
117 CRITICAL_SECTION *mutex_p;
118
119 mutex_p = (CRITICAL_SECTION *)mutex;
120 if (TryEnterCriticalSection(mutex_p))
121 return 1;
122
123 return 0;
124}
125
126void ossl_crypto_mutex_unlock(CRYPTO_MUTEX *mutex)
127{
128 CRITICAL_SECTION *mutex_p;
129
130 mutex_p = (CRITICAL_SECTION *)mutex;
131 LeaveCriticalSection(mutex_p);
132}
133
134void ossl_crypto_mutex_free(CRYPTO_MUTEX **mutex)
135{
136 CRITICAL_SECTION **mutex_p;
137
138 mutex_p = (CRITICAL_SECTION **)mutex;
139 if (*mutex_p != NULL)
140 DeleteCriticalSection(*mutex_p);
141 OPENSSL_free(*mutex_p);
142 *mutex = NULL;
143}
144
1dd04a0f
HL
145static int determine_timeout(OSSL_TIME deadline, DWORD *w_timeout_p)
146{
147 OSSL_TIME now, delta;
148 uint64_t ms;
149
150 if (ossl_time_is_infinite(deadline)) {
151 *w_timeout_p = INFINITE;
152 return 1;
153 }
154
155 now = ossl_time_now();
156 delta = ossl_time_subtract(deadline, now);
157
158 if (ossl_time_is_zero(delta))
159 return 0;
160
161 ms = ossl_time2ms(delta);
162
1dd04a0f
HL
163 /*
164 * Amount of time we want to wait is too long for the 32-bit argument to
165 * the Win32 API, so just wait as long as possible.
166 */
167 if (ms > (uint64_t)(INFINITE - 1))
168 *w_timeout_p = INFINITE - 1;
169 else
170 *w_timeout_p = (DWORD)ms;
171
172 return 1;
173}
174
175# if defined(OPENSSL_THREADS_WINNT_LEGACY)
425a7804
HL
176# include <assert.h>
177
178/*
179 * Win32, before Vista, did not have an OS-provided condition variable
180 * construct. This leads to the need to construct our own condition variable
181 * construct in order to support Windows XP.
182 *
183 * It is difficult to construct a condition variable construct using the
184 * OS-provided primitives in a way that is both correct (avoiding race
185 * conditions where broadcasts get lost) and fair.
186 *
187 * CORRECTNESS:
188 * A blocked thread is a thread which is calling wait(), between the
189 * precise instants at which the external mutex passed to wait() is
190 * unlocked and the instant at which it is relocked.
191 *
192 * a)
193 * - If broadcast() is called, ALL blocked threads MUST be unblocked.
194 * - If signal() is called, at least one blocked thread MUST be unblocked.
195 *
196 * (i.e.: a signal or broadcast must never get 'lost')
197 *
198 * b)
199 * - If broadcast() or signal() is called, this must not cause a thread
200 * which is not blocked to return immediately from a subsequent
201 * call to wait().
202 *
203 * FAIRNESS:
204 * If broadcast() is called at time T1, all blocked threads must be unblocked
205 * before any thread which subsequently calls wait() at time T2 > T1 is
206 * unblocked.
207 *
208 * An example of an implementation which lacks fairness is as follows:
209 *
210 * t1 enters wait()
211 * t2 enters wait()
212 *
213 * tZ calls broadcast()
214 *
215 * t1 exits wait()
216 * t1 enters wait()
217 *
218 * tZ calls broadcast()
219 *
220 * t1 exits wait()
221 *
222 * IMPLEMENTATION:
223 *
224 * The most suitable primitives available to us in Windows XP are semaphores,
225 * auto-reset events and manual-reset events. A solution based on semaphores
226 * is chosen.
227 *
228 * PROBLEM. Designing a solution based on semaphores is non-trivial because,
229 * while it is easy to track the number of waiters in an interlocked data
230 * structure and then add that number to the semaphore, this does not
231 * guarantee fairness or correctness. Consider the following situation:
232 *
233 * - t1 enters wait(), adding 1 to the wait counter & blocks on the semaphore
234 * - t2 enters wait(), adding 1 to the wait counter & blocks on the semaphore
235 * - tZ calls broadcast(), finds the wait counter is 2, adds 2 to the semaphore
236 *
237 * - t1 exits wait()
238 * - t1 immediately reenters wait() and blocks on the semaphore
239 * - The semaphore is still positive due to also having been signalled
240 * for t2, therefore it is decremented
241 * - t1 exits wait() immediately; t2 is never woken
242 *
243 * GENERATION COUNTERS. One naive solution to this is to use a generation
244 * counter. Each broadcast() invocation increments a generation counter. If
245 * the generation counter has not changed during a semaphore wait operation
246 * inside wait(), this indicates that no broadcast() call has been made in
247 * the meantime; therefore, the successful semaphore decrement must have
248 * 'stolen' a wakeup from another thread which was waiting to wakeup from the
249 * prior broadcast() call but which had not yet had a chance to do so. The
250 * semaphore can then be reincremented and the wait() operation repeated.
251 *
252 * However, this suffers from the obvious problem that without OS guarantees
253 * as to how semaphore readiness events are distributed amongst threads,
254 * there is no particular guarantee that the semaphore readiness event will
255 * not be immediately redistributed back to the same thread t1.
256 *
257 * SOLUTION. A solution is chosen as follows. In its initial state, a
258 * condition variable can accept waiters, who wait for the semaphore
259 * normally. However, once broadcast() is called, the condition
260 * variable becomes 'closed'. Any existing blocked threads are unblocked,
261 * but any new calls to wait() will instead enter a blocking pre-wait stage.
262 * Pre-wait threads are not considered to be waiting (and the external
263 * mutex remains held). A call to wait() in pre-wait cannot progress
264 * to waiting until all threads due to be unblocked by the prior broadcast()
265 * call have returned and had a chance to execute.
266 *
267 * This pre-wait does not affect a thread if it does not call wait()
268 * again until after all threads have had a chance to execute.
269 *
270 * RESOURCE USAGE. Aside from an allocation for the condition variable
271 * structure, this solution uses two Win32 semaphores.
272 *
273 * FUTURE OPTIMISATIONS:
274 *
275 * An optimised multi-generation implementation is possible at the cost of
276 * higher Win32 resource usage. Multiple 'buckets' could be defined, with
277 * usage rotating between buckets internally as buckets become closed.
278 * This would avoid the need for the prewait in more cases, depending
279 * on intensity of usage.
280 *
281 */
282typedef struct legacy_condvar_st {
283 CRYPTO_MUTEX *int_m; /* internal mutex */
284 HANDLE sema; /* main wait semaphore */
285 HANDLE prewait_sema; /* prewait semaphore */
286 /*
287 * All of the following fields are protected by int_m.
288 *
289 * num_wake only ever increases by virtue of a corresponding decrease in
290 * num_wait. num_wait can decrease for other reasons (for example due to a
291 * wait operation timing out).
292 */
293 size_t num_wait; /* Num. threads currently blocked */
294 size_t num_wake; /* Num. threads due to wake up */
295 size_t num_prewait; /* Num. threads in prewait */
296 size_t gen; /* Prewait generation */
297 int closed; /* Is closed? */
298} LEGACY_CONDVAR;
1dd04a0f
HL
299
300CRYPTO_CONDVAR *ossl_crypto_condvar_new(void)
301{
425a7804
HL
302 LEGACY_CONDVAR *cv;
303
304 if ((cv = OPENSSL_malloc(sizeof(LEGACY_CONDVAR))) == NULL)
305 return NULL;
1dd04a0f 306
425a7804
HL
307 if ((cv->int_m = ossl_crypto_mutex_new()) == NULL) {
308 OPENSSL_free(cv);
1dd04a0f 309 return NULL;
425a7804 310 }
1dd04a0f 311
425a7804
HL
312 if ((cv->sema = CreateSemaphoreA(NULL, 0, LONG_MAX, NULL)) == NULL) {
313 ossl_crypto_mutex_free(&cv->int_m);
314 OPENSSL_free(cv);
315 return NULL;
316 }
317
318 if ((cv->prewait_sema = CreateSemaphoreA(NULL, 0, LONG_MAX, NULL)) == NULL) {
319 CloseHandle(cv->sema);
320 ossl_crypto_mutex_free(&cv->int_m);
321 OPENSSL_free(cv);
322 return NULL;
323 }
324
325 cv->num_wait = 0;
326 cv->num_wake = 0;
327 cv->num_prewait = 0;
328 cv->closed = 0;
329
330 return (CRYPTO_CONDVAR *)cv;
1dd04a0f
HL
331}
332
425a7804 333void ossl_crypto_condvar_free(CRYPTO_CONDVAR **cv_p)
1dd04a0f 334{
425a7804
HL
335 if (*cv_p != NULL) {
336 LEGACY_CONDVAR *cv = *(LEGACY_CONDVAR **)cv_p;
337
338 CloseHandle(cv->sema);
339 CloseHandle(cv->prewait_sema);
340 ossl_crypto_mutex_free(&cv->int_m);
341 OPENSSL_free(cv);
342 }
343
344 *cv_p = NULL;
1dd04a0f
HL
345}
346
425a7804 347static uint32_t obj_wait(HANDLE h, OSSL_TIME deadline)
1dd04a0f
HL
348{
349 DWORD timeout;
350
1dd04a0f
HL
351 if (!determine_timeout(deadline, &timeout))
352 timeout = 1;
353
425a7804
HL
354 return WaitForSingleObject(h, timeout);
355}
356
357void ossl_crypto_condvar_wait_timeout(CRYPTO_CONDVAR *cv_, CRYPTO_MUTEX *ext_m,
358 OSSL_TIME deadline)
359{
360 LEGACY_CONDVAR *cv = (LEGACY_CONDVAR *)cv_;
361 int closed, set_prewait = 0, have_orig_gen = 0;
362 uint32_t rc;
363 size_t orig_gen;
364
365 /* Admission control - prewait until we can enter our actual wait phase. */
366 do {
367 ossl_crypto_mutex_lock(cv->int_m);
368
369 closed = cv->closed;
370
371 /*
372 * Once prewait is over the prewait semaphore is signalled and
373 * num_prewait is set to 0. Use a generation counter to track if we need
374 * to remove a value we added to num_prewait when exiting (e.g. due to
375 * timeout or failure of WaitForSingleObject).
376 */
377 if (!have_orig_gen) {
378 orig_gen = cv->gen;
379 have_orig_gen = 1;
380 } else if (cv->gen != orig_gen) {
381 set_prewait = 0;
382 orig_gen = cv->gen;
383 }
384
385 if (!closed) {
386 /* We can now be admitted. */
387 ++cv->num_wait;
388 if (set_prewait) {
389 --cv->num_prewait;
390 set_prewait = 0;
391 }
392 } else if (!set_prewait) {
393 ++cv->num_prewait;
394 set_prewait = 1;
395 }
396
397 ossl_crypto_mutex_unlock(cv->int_m);
398
399 if (closed)
400 if (obj_wait(cv->prewait_sema, deadline) != WAIT_OBJECT_0) {
401 /*
402 * If we got WAIT_OBJECT_0 we are safe - num_prewait has been
403 * set to 0 and the semaphore has been consumed. On the other
404 * hand if we timed out, there may be a residual posting that
405 * was made just after we timed out. However in the worst case
406 * this will just cause an internal spurious wakeup here in the
407 * future, so we do not care too much about this. We treat
408 * failure and timeout cases as the same, and simply exit in
409 * this case.
410 */
411 ossl_crypto_mutex_lock(cv->int_m);
412 if (set_prewait && cv->gen == orig_gen)
413 --cv->num_prewait;
414 ossl_crypto_mutex_unlock(cv->int_m);
415 return;
416 }
417 } while (closed);
418
419 /*
420 * Unlock external mutex. Do not do this until we have been admitted, as we
421 * must guarantee we wake if broadcast is called at any time after ext_m is
422 * unlocked.
423 */
424 ossl_crypto_mutex_unlock(ext_m);
425
426 for (;;) {
427 /* Wait. */
428 rc = obj_wait(cv->sema, deadline);
429
430 /* Reacquire internal mutex and probe state. */
431 ossl_crypto_mutex_lock(cv->int_m);
432
433 if (cv->num_wake > 0) {
434 /*
435 * A wake token is available, so we can wake up. Consume the token
436 * and get out of here. We don't care what WaitForSingleObject
437 * returned here (e.g. if it timed out coincidentally). In the
438 * latter case a signal might be left in the semaphore which causes
439 * a future WaitForSingleObject call to return immediately, but in
440 * this case we will just loop again.
441 */
442 --cv->num_wake;
443 if (cv->num_wake == 0 && cv->closed) {
444 /*
445 * We consumed the last wake token, so we can now open the
446 * condition variable for new admissions.
447 */
448 cv->closed = 0;
449 if (cv->num_prewait > 0) {
450 ReleaseSemaphore(cv->prewait_sema, (LONG)cv->num_prewait, NULL);
451 cv->num_prewait = 0;
452 ++cv->gen;
453 }
454 }
455 } else if (rc == WAIT_OBJECT_0) {
456 /*
457 * We got a wakeup from the semaphore but we did not have any wake
458 * tokens. This ideally does not happen, but might if during a
459 * previous wait() call the semaphore is posted just after
460 * WaitForSingleObject returns due to a timeout (such that the
461 * num_wake > 0 case is taken above). Just spin again. (It is worth
462 * noting that repeated WaitForSingleObject calls is the only method
463 * documented for decrementing a Win32 semaphore, so this is
464 * basically the best possible strategy.)
465 */
466 ossl_crypto_mutex_unlock(cv->int_m);
467 continue;
468 } else {
469 /*
470 * Assume we timed out. The WaitForSingleObject call may also have
471 * failed for some other reason, which we treat as a timeout.
472 */
473 assert(cv->num_wait > 0);
474 --cv->num_wait;
475 }
476
477 break;
478 }
479
480 ossl_crypto_mutex_unlock(cv->int_m);
481 ossl_crypto_mutex_lock(ext_m);
1dd04a0f
HL
482}
483
425a7804 484void ossl_crypto_condvar_wait(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *ext_m)
1dd04a0f 485{
425a7804 486 ossl_crypto_condvar_wait_timeout(cv, ext_m, ossl_time_infinite());
1dd04a0f
HL
487}
488
425a7804 489void ossl_crypto_condvar_broadcast(CRYPTO_CONDVAR *cv_)
1dd04a0f 490{
425a7804
HL
491 LEGACY_CONDVAR *cv = (LEGACY_CONDVAR *)cv_;
492 size_t num_wake;
493
494 ossl_crypto_mutex_lock(cv->int_m);
495
496 num_wake = cv->num_wait;
497 if (num_wake == 0) {
498 ossl_crypto_mutex_unlock(cv->int_m);
499 return;
500 }
501
502 cv->num_wake += num_wake;
503 cv->num_wait -= num_wake;
504 cv->closed = 1;
1dd04a0f 505
425a7804
HL
506 ossl_crypto_mutex_unlock(cv->int_m);
507 ReleaseSemaphore(cv->sema, num_wake, NULL);
1dd04a0f
HL
508}
509
425a7804 510void ossl_crypto_condvar_signal(CRYPTO_CONDVAR *cv_)
1dd04a0f 511{
425a7804 512 LEGACY_CONDVAR *cv = (LEGACY_CONDVAR *)cv_;
1dd04a0f 513
425a7804 514 ossl_crypto_mutex_lock(cv->int_m);
1dd04a0f 515
425a7804
HL
516 if (cv->num_wait == 0) {
517 ossl_crypto_mutex_unlock(cv->int_m);
518 return;
519 }
520
521 /*
522 * We do not close the condition variable when merely signalling, as there
523 * are no guaranteed fairness semantics here, unlike for a broadcast.
524 */
525 --cv->num_wait;
526 ++cv->num_wake;
527
528 ossl_crypto_mutex_unlock(cv->int_m);
529 ReleaseSemaphore(cv->sema, 1, NULL);
1dd04a0f
HL
530}
531
532# else
533
4574a7fd
ČK
534CRYPTO_CONDVAR *ossl_crypto_condvar_new(void)
535{
536 CONDITION_VARIABLE *cv_p;
537
538 if ((cv_p = OPENSSL_zalloc(sizeof(*cv_p))) == NULL)
539 return NULL;
540 InitializeConditionVariable(cv_p);
541 return (CRYPTO_CONDVAR *)cv_p;
542}
543
544void ossl_crypto_condvar_wait(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex)
545{
546 CONDITION_VARIABLE *cv_p;
547 CRITICAL_SECTION *mutex_p;
548
549 cv_p = (CONDITION_VARIABLE *)cv;
550 mutex_p = (CRITICAL_SECTION *)mutex;
551 SleepConditionVariableCS(cv_p, mutex_p, INFINITE);
552}
553
2b2b2678 554void ossl_crypto_condvar_wait_timeout(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex,
1dd04a0f 555 OSSL_TIME deadline)
2b2b2678
HL
556{
557 DWORD timeout;
558 CONDITION_VARIABLE *cv_p = (CONDITION_VARIABLE *)cv;
559 CRITICAL_SECTION *mutex_p = (CRITICAL_SECTION *)mutex;
560
1dd04a0f
HL
561 if (!determine_timeout(deadline, &timeout))
562 timeout = 1;
2b2b2678 563
1dd04a0f 564 SleepConditionVariableCS(cv_p, mutex_p, timeout);
2b2b2678
HL
565}
566
4574a7fd
ČK
567void ossl_crypto_condvar_broadcast(CRYPTO_CONDVAR *cv)
568{
569 CONDITION_VARIABLE *cv_p;
570
571 cv_p = (CONDITION_VARIABLE *)cv;
572 WakeAllConditionVariable(cv_p);
573}
574
1dd04a0f
HL
575void ossl_crypto_condvar_signal(CRYPTO_CONDVAR *cv)
576{
577 CONDITION_VARIABLE *cv_p;
578
579 cv_p = (CONDITION_VARIABLE *)cv;
580 WakeConditionVariable(cv_p);
581}
582
4574a7fd
ČK
583void ossl_crypto_condvar_free(CRYPTO_CONDVAR **cv)
584{
585 CONDITION_VARIABLE **cv_p;
586
587 cv_p = (CONDITION_VARIABLE **)cv;
588 OPENSSL_free(*cv_p);
589 *cv_p = NULL;
590}
591
9cf091a3 592# endif
1dd04a0f
HL
593
594void ossl_crypto_mem_barrier(void)
595{
596 MemoryBarrier();
597}
598
599#endif