1 /*
2 * Copyright 2016-2026 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 #if defined(_WIN32)
11 #include <windows.h>
12 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600
13 #define USE_RWLOCK
14 #endif
15 #endif
16 #include <assert.h>
17
18 /*
19 * VC++ 2008 or earlier x86 compilers do not have an inline implementation
20 * of InterlockedOr64 for 32bit and will fail to run on Windows XP 32bit.
21 * https://docs.microsoft.com/en-us/cpp/intrinsics/interlockedor-intrinsic-functions#requirements
22 * To work around this problem, we implement a manual locking mechanism for
23 * only VC++ 2008 or earlier x86 compilers.
24 */
25
26 #if ((defined(_MSC_VER) && defined(_M_IX86) && _MSC_VER <= 1600) || (defined(__MINGW32__) && !defined(__MINGW64__)))
27 #define NO_INTERLOCKEDOR64
28 #endif
29
30 #include <openssl/crypto.h>
31 #include <crypto/cryptlib.h>
32 #include "internal/common.h"
33 #include "internal/thread_arch.h"
34 #include "internal/rcu.h"
35 #include "rcu_internal.h"
36
37 #if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG) && defined(OPENSSL_SYS_WINDOWS)
38
39 #ifdef USE_RWLOCK
40 typedef struct {
41 SRWLOCK lock;
42 int exclusive;
43 } CRYPTO_win_rwlock;
44 #endif
45
46 /*
47 * This defines a quescent point (qp)
48 * This is the barrier beyond which a writer
49 * must wait before freeing data that was
50 * atomically updated
51 */
52 struct rcu_qp {
53 uint64_t users;
54 };
55
56 struct thread_qp {
57 struct rcu_qp *qp;
58 unsigned int depth;
59 CRYPTO_RCU_LOCK *lock;
60 };
61
62 #define MAX_QPS 10
63 /*
64 * This is the per thread tracking data
65 * that is assigned to each thread participating
66 * in an rcu qp
67 *
68 * qp points to the qp that it last acquired
69 *
70 */
71 struct rcu_thr_data {
72 struct thread_qp thread_qps[MAX_QPS];
73 };
74
75 /*
76 * This is the internal version of a CRYPTO_RCU_LOCK
77 * it is cast from CRYPTO_RCU_LOCK
78 */
79 struct rcu_lock_st {
80 /* Callbacks to call for next ossl_synchronize_rcu */
81 struct rcu_cb_item *cb_items;
82
83 /* The context we are being created against */
84 OSSL_LIB_CTX *ctx;
85
86 /* Array of quiescent points for synchronization */
87 struct rcu_qp *qp_group;
88
89 /* rcu generation counter for in-order retirement */
90 uint32_t id_ctr;
91
92 /* Number of elements in qp_group array */
93 uint32_t group_count;
94
95 /* Index of the current qp in the qp_group array */
96 uint32_t reader_idx;
97
98 /* value of the next id_ctr value to be retired */
99 uint32_t next_to_retire;
100
101 /* index of the next free rcu_qp in the qp_group */
102 uint32_t current_alloc_idx;
103
104 /* number of qp's in qp_group array currently being retired */
105 uint32_t writers_alloced;
106
107 /* lock protecting write side operations */
108 CRYPTO_MUTEX *write_lock;
109
110 /* lock protecting updates to writers_alloced/current_alloc_idx */
111 CRYPTO_MUTEX *alloc_lock;
112
113 /* signal to wake threads waiting on alloc_lock */
114 CRYPTO_CONDVAR *alloc_signal;
115
116 /* lock to enforce in-order retirement */
117 CRYPTO_MUTEX *prior_lock;
118
119 /* signal to wake threads waiting on prior_lock */
120 CRYPTO_CONDVAR *prior_signal;
121
122 /* lock used with NO_INTERLOCKEDOR64: VS2010 x86 */
123 CRYPTO_RWLOCK *rw_lock;
124 };
125
allocate_new_qp_group(struct rcu_lock_st * lock,uint32_t count)126 static struct rcu_qp *allocate_new_qp_group(struct rcu_lock_st *lock,
127 uint32_t count)
128 {
129 struct rcu_qp *new = OPENSSL_zalloc(sizeof(*new) * count);
130
131 lock->group_count = count;
132 return new;
133 }
134
ossl_rcu_lock_new(int num_writers,OSSL_LIB_CTX * ctx)135 CRYPTO_RCU_LOCK *ossl_rcu_lock_new(int num_writers, OSSL_LIB_CTX *ctx)
136 {
137 struct rcu_lock_st *new;
138
139 /*
140 * We need a minimum of 2 qps
141 */
142 if (num_writers < 2)
143 num_writers = 2;
144
145 ctx = ossl_lib_ctx_get_concrete(ctx);
146 if (ctx == NULL)
147 return 0;
148
149 new = OPENSSL_zalloc(sizeof(*new));
150
151 if (new == NULL)
152 return NULL;
153
154 new->ctx = ctx;
155 new->rw_lock = CRYPTO_THREAD_lock_new();
156 new->write_lock = ossl_crypto_mutex_new();
157 new->alloc_signal = ossl_crypto_condvar_new();
158 new->prior_signal = ossl_crypto_condvar_new();
159 new->alloc_lock = ossl_crypto_mutex_new();
160 new->prior_lock = ossl_crypto_mutex_new();
161 new->qp_group = allocate_new_qp_group(new, num_writers);
162 if (new->qp_group == NULL
163 || new->alloc_signal == NULL
164 || new->prior_signal == NULL
165 || new->write_lock == NULL
166 || new->alloc_lock == NULL
167 || new->prior_lock == NULL
168 || new->rw_lock == NULL) {
169 CRYPTO_THREAD_lock_free(new->rw_lock);
170 OPENSSL_free(new->qp_group);
171 ossl_crypto_condvar_free(&new->alloc_signal);
172 ossl_crypto_condvar_free(&new->prior_signal);
173 ossl_crypto_mutex_free(&new->alloc_lock);
174 ossl_crypto_mutex_free(&new->prior_lock);
175 ossl_crypto_mutex_free(&new->write_lock);
176 OPENSSL_free(new);
177 new = NULL;
178 }
179
180 return new;
181 }
182
ossl_rcu_lock_free(CRYPTO_RCU_LOCK * lock)183 void ossl_rcu_lock_free(CRYPTO_RCU_LOCK *lock)
184 {
185 CRYPTO_THREAD_lock_free(lock->rw_lock);
186 OPENSSL_free(lock->qp_group);
187 ossl_crypto_condvar_free(&lock->alloc_signal);
188 ossl_crypto_condvar_free(&lock->prior_signal);
189 ossl_crypto_mutex_free(&lock->alloc_lock);
190 ossl_crypto_mutex_free(&lock->prior_lock);
191 ossl_crypto_mutex_free(&lock->write_lock);
192 OPENSSL_free(lock);
193 }
194
195 /* Read side acquisition of the current qp */
get_hold_current_qp(CRYPTO_RCU_LOCK * lock)196 static ossl_inline struct rcu_qp *get_hold_current_qp(CRYPTO_RCU_LOCK *lock)
197 {
198 uint32_t qp_idx;
199 uint32_t tmp;
200 uint64_t tmp64;
201
202 /* get the current qp index */
203 for (;;) {
204 CRYPTO_atomic_load_int((int *)&lock->reader_idx, (int *)&qp_idx,
205 lock->rw_lock);
206 CRYPTO_atomic_add64(&lock->qp_group[qp_idx].users, (uint64_t)1, &tmp64,
207 lock->rw_lock);
208 CRYPTO_atomic_load_int((int *)&lock->reader_idx, (int *)&tmp,
209 lock->rw_lock);
210 if (qp_idx == tmp)
211 break;
212 CRYPTO_atomic_add64(&lock->qp_group[qp_idx].users, (uint64_t)-1, &tmp64,
213 lock->rw_lock);
214 }
215
216 return &lock->qp_group[qp_idx];
217 }
218
ossl_rcu_free_local_data(void * arg)219 static void ossl_rcu_free_local_data(void *arg)
220 {
221 OSSL_LIB_CTX *ctx = arg;
222 CRYPTO_THREAD_LOCAL *lkey = ossl_lib_ctx_get_rcukey(ctx);
223 struct rcu_thr_data *data = CRYPTO_THREAD_get_local(lkey);
224 OPENSSL_free(data);
225 CRYPTO_THREAD_set_local(lkey, NULL);
226 }
227
ossl_rcu_read_lock(CRYPTO_RCU_LOCK * lock)228 void ossl_rcu_read_lock(CRYPTO_RCU_LOCK *lock)
229 {
230 struct rcu_thr_data *data;
231 int i;
232 int available_qp = -1;
233 CRYPTO_THREAD_LOCAL *lkey = ossl_lib_ctx_get_rcukey(lock->ctx);
234
235 /*
236 * we're going to access current_qp here so ask the
237 * processor to fetch it
238 */
239 data = CRYPTO_THREAD_get_local(lkey);
240
241 if (data == NULL) {
242 data = OPENSSL_zalloc(sizeof(*data));
243 OPENSSL_assert(data != NULL);
244 CRYPTO_THREAD_set_local(lkey, data);
245 ossl_init_thread_start(NULL, lock->ctx, ossl_rcu_free_local_data);
246 }
247
248 for (i = 0; i < MAX_QPS; i++) {
249 if (data->thread_qps[i].qp == NULL && available_qp == -1)
250 available_qp = i;
251 /* If we have a hold on this lock already, we're good */
252 if (data->thread_qps[i].lock == lock)
253 return;
254 }
255
256 /*
257 * if we get here, then we don't have a hold on this lock yet
258 */
259 assert(available_qp != -1);
260
261 data->thread_qps[available_qp].qp = get_hold_current_qp(lock);
262 data->thread_qps[available_qp].depth = 1;
263 data->thread_qps[available_qp].lock = lock;
264 }
265
ossl_rcu_write_lock(CRYPTO_RCU_LOCK * lock)266 void ossl_rcu_write_lock(CRYPTO_RCU_LOCK *lock)
267 {
268 ossl_crypto_mutex_lock(lock->write_lock);
269 }
270
ossl_rcu_write_unlock(CRYPTO_RCU_LOCK * lock)271 void ossl_rcu_write_unlock(CRYPTO_RCU_LOCK *lock)
272 {
273 ossl_crypto_mutex_unlock(lock->write_lock);
274 }
275
ossl_rcu_read_unlock(CRYPTO_RCU_LOCK * lock)276 void ossl_rcu_read_unlock(CRYPTO_RCU_LOCK *lock)
277 {
278 CRYPTO_THREAD_LOCAL *lkey = ossl_lib_ctx_get_rcukey(lock->ctx);
279 struct rcu_thr_data *data = CRYPTO_THREAD_get_local(lkey);
280 int i;
281 LONG64 ret;
282
283 assert(data != NULL);
284
285 for (i = 0; i < MAX_QPS; i++) {
286 if (data->thread_qps[i].lock == lock) {
287 data->thread_qps[i].depth--;
288 if (data->thread_qps[i].depth == 0) {
289 CRYPTO_atomic_add64(&data->thread_qps[i].qp->users,
290 (uint64_t)-1, (uint64_t *)&ret,
291 lock->rw_lock);
292 OPENSSL_assert(ret >= 0);
293 data->thread_qps[i].qp = NULL;
294 data->thread_qps[i].lock = NULL;
295 }
296 return;
297 }
298 }
299 }
300
301 /*
302 * Write side allocation routine to get the current qp
303 * and replace it with a new one
304 */
update_qp(CRYPTO_RCU_LOCK * lock,uint32_t * curr_id)305 static struct rcu_qp *update_qp(CRYPTO_RCU_LOCK *lock, uint32_t *curr_id)
306 {
307 uint32_t current_idx;
308 uint32_t tmp;
309
310 ossl_crypto_mutex_lock(lock->alloc_lock);
311 /*
312 * we need at least one qp to be available with one
313 * left over, so that readers can start working on
314 * one that isn't yet being waited on
315 */
316 while (lock->group_count - lock->writers_alloced < 2)
317 /* we have to wait for one to be free */
318 ossl_crypto_condvar_wait(lock->alloc_signal, lock->alloc_lock);
319
320 current_idx = lock->current_alloc_idx;
321
322 /* Allocate the qp */
323 lock->writers_alloced++;
324
325 /* increment the allocation index */
326 lock->current_alloc_idx = (lock->current_alloc_idx + 1) % lock->group_count;
327
328 /* get and insert a new id */
329 *curr_id = lock->id_ctr;
330 lock->id_ctr++;
331
332 /* update the reader index to be the prior qp */
333 tmp = lock->current_alloc_idx;
334 #if (defined(NO_INTERLOCKEDOR64))
335 CRYPTO_THREAD_write_lock(lock->rw_lock);
336 lock->reader_idx = tmp;
337 CRYPTO_THREAD_unlock(lock->rw_lock);
338 #else
339 InterlockedExchange((LONG volatile *)&lock->reader_idx, tmp);
340 #endif
341
342 /* wake up any waiters */
343 ossl_crypto_condvar_broadcast(lock->alloc_signal);
344 ossl_crypto_mutex_unlock(lock->alloc_lock);
345 return &lock->qp_group[current_idx];
346 }
347
retire_qp(CRYPTO_RCU_LOCK * lock,struct rcu_qp * qp)348 static void retire_qp(CRYPTO_RCU_LOCK *lock,
349 struct rcu_qp *qp)
350 {
351 ossl_crypto_mutex_lock(lock->alloc_lock);
352 lock->writers_alloced--;
353 ossl_crypto_condvar_broadcast(lock->alloc_signal);
354 ossl_crypto_mutex_unlock(lock->alloc_lock);
355 }
356
ossl_synchronize_rcu(CRYPTO_RCU_LOCK * lock)357 void ossl_synchronize_rcu(CRYPTO_RCU_LOCK *lock)
358 {
359 struct rcu_qp *qp;
360 uint64_t count;
361 uint32_t curr_id;
362 struct rcu_cb_item *cb_items, *tmpcb;
363
364 /* before we do anything else, lets grab the cb list */
365 ossl_crypto_mutex_lock(lock->write_lock);
366 cb_items = lock->cb_items;
367 lock->cb_items = NULL;
368 ossl_crypto_mutex_unlock(lock->write_lock);
369
370 qp = update_qp(lock, &curr_id);
371
372 /* retire in order */
373 ossl_crypto_mutex_lock(lock->prior_lock);
374 while (lock->next_to_retire != curr_id)
375 ossl_crypto_condvar_wait(lock->prior_signal, lock->prior_lock);
376
377 /* wait for the reader count to reach zero */
378 do {
379 CRYPTO_atomic_load(&qp->users, &count, lock->rw_lock);
380 } while (count != (uint64_t)0);
381
382 lock->next_to_retire++;
383 ossl_crypto_condvar_broadcast(lock->prior_signal);
384 ossl_crypto_mutex_unlock(lock->prior_lock);
385
386 retire_qp(lock, qp);
387
388 /* handle any callbacks that we have */
389 while (cb_items != NULL) {
390 tmpcb = cb_items;
391 cb_items = cb_items->next;
392 tmpcb->fn(tmpcb->data);
393 OPENSSL_free(tmpcb);
394 }
395
396 /* and we're done */
397 return;
398 }
399
ossl_rcu_cb_item_new(void)400 CRYPTO_RCU_CB_ITEM *ossl_rcu_cb_item_new(void)
401 {
402 return OPENSSL_zalloc(sizeof(CRYPTO_RCU_CB_ITEM));
403 }
404
ossl_rcu_cb_item_free(CRYPTO_RCU_CB_ITEM * item)405 void ossl_rcu_cb_item_free(CRYPTO_RCU_CB_ITEM *item)
406 {
407 OPENSSL_free(item);
408 }
409
410 /*
411 * Note, must be called under the protection of ossl_rcu_write_lock
412 */
ossl_rcu_call(CRYPTO_RCU_LOCK * lock,CRYPTO_RCU_CB_ITEM * item,rcu_cb_fn cb,void * data)413 void ossl_rcu_call(CRYPTO_RCU_LOCK *lock, CRYPTO_RCU_CB_ITEM *item,
414 rcu_cb_fn cb, void *data)
415 {
416 item->fn = cb;
417 item->data = data;
418 item->next = lock->cb_items;
419 lock->cb_items = item;
420 }
421
ossl_rcu_uptr_deref(void ** p)422 void *ossl_rcu_uptr_deref(void **p)
423 {
424 return (void *)*p;
425 }
426
ossl_rcu_assign_uptr(void ** p,void ** v)427 void ossl_rcu_assign_uptr(void **p, void **v)
428 {
429 InterlockedExchangePointer((void *volatile *)p, (void *)*v);
430 }
431
CRYPTO_THREAD_lock_new(void)432 CRYPTO_RWLOCK *CRYPTO_THREAD_lock_new(void)
433 {
434 CRYPTO_RWLOCK *lock;
435 #ifdef USE_RWLOCK
436 CRYPTO_win_rwlock *rwlock;
437
438 if ((lock = OPENSSL_zalloc(sizeof(CRYPTO_win_rwlock))) == NULL)
439 /* Don't set error, to avoid recursion blowup. */
440 return NULL;
441 rwlock = lock;
442 InitializeSRWLock(&rwlock->lock);
443 #else
444
445 if ((lock = OPENSSL_zalloc(sizeof(CRITICAL_SECTION))) == NULL)
446 /* Don't set error, to avoid recursion blowup. */
447 return NULL;
448
449 #if !defined(_WIN32_WCE)
450 /* 0x400 is the spin count value suggested in the documentation */
451 if (!InitializeCriticalSectionAndSpinCount(lock, 0x400)) {
452 OPENSSL_free(lock);
453 return NULL;
454 }
455 #else
456 InitializeCriticalSection(lock);
457 #endif
458 #endif
459
460 return lock;
461 }
462
CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK * lock)463 __owur int CRYPTO_THREAD_read_lock(CRYPTO_RWLOCK *lock)
464 {
465 #ifdef USE_RWLOCK
466 CRYPTO_win_rwlock *rwlock = lock;
467
468 AcquireSRWLockShared(&rwlock->lock);
469 #else
470 EnterCriticalSection(lock);
471 #endif
472 return 1;
473 }
474
CRYPTO_THREAD_write_lock(CRYPTO_RWLOCK * lock)475 __owur int CRYPTO_THREAD_write_lock(CRYPTO_RWLOCK *lock)
476 {
477 #ifdef USE_RWLOCK
478 CRYPTO_win_rwlock *rwlock = lock;
479
480 AcquireSRWLockExclusive(&rwlock->lock);
481 rwlock->exclusive = 1;
482 #else
483 EnterCriticalSection(lock);
484 #endif
485 return 1;
486 }
487
CRYPTO_THREAD_unlock(CRYPTO_RWLOCK * lock)488 int CRYPTO_THREAD_unlock(CRYPTO_RWLOCK *lock)
489 {
490 #ifdef USE_RWLOCK
491 CRYPTO_win_rwlock *rwlock = lock;
492
493 if (rwlock->exclusive) {
494 rwlock->exclusive = 0;
495 ReleaseSRWLockExclusive(&rwlock->lock);
496 } else {
497 ReleaseSRWLockShared(&rwlock->lock);
498 }
499 #else
500 LeaveCriticalSection(lock);
501 #endif
502 return 1;
503 }
504
CRYPTO_THREAD_lock_free(CRYPTO_RWLOCK * lock)505 void CRYPTO_THREAD_lock_free(CRYPTO_RWLOCK *lock)
506 {
507 if (lock == NULL)
508 return;
509
510 #ifndef USE_RWLOCK
511 DeleteCriticalSection(lock);
512 #endif
513 OPENSSL_free(lock);
514
515 return;
516 }
517
518 #define ONCE_UNINITED 0
519 #define ONCE_ININIT 1
520 #define ONCE_DONE 2
521
522 /*
523 * We don't use InitOnceExecuteOnce because that isn't available in WinXP which
524 * we still have to support.
525 */
CRYPTO_THREAD_run_once(CRYPTO_ONCE * once,void (* init)(void))526 int CRYPTO_THREAD_run_once(CRYPTO_ONCE *once, void (*init)(void))
527 {
528 LONG volatile *lock = (LONG *)once;
529 LONG result;
530
531 if (*lock == ONCE_DONE)
532 return 1;
533
534 do {
535 result = InterlockedCompareExchange(lock, ONCE_ININIT, ONCE_UNINITED);
536 if (result == ONCE_UNINITED) {
537 init();
538 /*
539 * On weakly ordered systems, it may happen that the write to *lock
540 * below completes prior to some writes in whatever the init()
541 * callback routine above may do. In this case, other threads
542 * entering here may see unsynchronized data in whatever the init
543 * routine initializes, leading to erroneous behavior.
544 *
545 * We should use InitOnceExecuteOnce here to implement this, but
546 * doing so requires that we modify the definition of the
547 * CRYPTO_ONCE type, which is an ABI breakage. So instead
548 * just insert a memory barrier here to ensure that any pending
549 * writes are flushed to memory prior to setting ONCE_DONE below
550 */
551 MemoryBarrier();
552 *lock = ONCE_DONE;
553 return 1;
554 }
555 } while (result == ONCE_ININIT);
556
557 return (*lock == ONCE_DONE);
558 }
559
ossl_thread_init_local(CRYPTO_THREAD_LOCAL * key,void (* cleanup)(void *))560 int ossl_thread_init_local(CRYPTO_THREAD_LOCAL *key, void (*cleanup)(void *))
561 {
562
563 *key = TlsAlloc();
564 if (*key == TLS_OUT_OF_INDEXES)
565 return 0;
566
567 return 1;
568 }
569
CRYPTO_THREAD_get_local(CRYPTO_THREAD_LOCAL * key)570 void *CRYPTO_THREAD_get_local(CRYPTO_THREAD_LOCAL *key)
571 {
572 DWORD last_error;
573 void *ret;
574
575 /*
576 * TlsGetValue clears the last error even on success, so that callers may
577 * distinguish it successfully returning NULL or failing. It is documented
578 * to never fail if the argument is a valid index from TlsAlloc, so we do
579 * not need to handle this.
580 *
581 * However, this error-mangling behavior interferes with the caller's use of
582 * GetLastError. In particular SSL_get_error queries the error queue to
583 * determine whether the caller should look at the OS's errors. To avoid
584 * destroying state, save and restore the Windows error.
585 *
586 * https://msdn.microsoft.com/en-us/library/windows/desktop/ms686812(v=vs.85).aspx
587 */
588 last_error = GetLastError();
589 ret = TlsGetValue(*key);
590 SetLastError(last_error);
591 return ret;
592 }
593
CRYPTO_THREAD_set_local(CRYPTO_THREAD_LOCAL * key,void * val)594 int CRYPTO_THREAD_set_local(CRYPTO_THREAD_LOCAL *key, void *val)
595 {
596 if (TlsSetValue(*key, val) == 0)
597 return 0;
598
599 return 1;
600 }
601
CRYPTO_THREAD_cleanup_local(CRYPTO_THREAD_LOCAL * key)602 int CRYPTO_THREAD_cleanup_local(CRYPTO_THREAD_LOCAL *key)
603 {
604 if (TlsFree(*key) == 0)
605 return 0;
606
607 return 1;
608 }
609
CRYPTO_THREAD_get_current_id(void)610 CRYPTO_THREAD_ID CRYPTO_THREAD_get_current_id(void)
611 {
612 return GetCurrentThreadId();
613 }
614
CRYPTO_THREAD_compare_id(CRYPTO_THREAD_ID a,CRYPTO_THREAD_ID b)615 int CRYPTO_THREAD_compare_id(CRYPTO_THREAD_ID a, CRYPTO_THREAD_ID b)
616 {
617 return (a == b);
618 }
619
CRYPTO_atomic_add(int * val,int amount,int * ret,CRYPTO_RWLOCK * lock)620 int CRYPTO_atomic_add(int *val, int amount, int *ret, CRYPTO_RWLOCK *lock)
621 {
622 #if (defined(NO_INTERLOCKEDOR64))
623 if (lock == NULL || !CRYPTO_THREAD_write_lock(lock))
624 return 0;
625 *val += amount;
626 *ret = *val;
627
628 if (!CRYPTO_THREAD_unlock(lock))
629 return 0;
630
631 return 1;
632 #else
633 *ret = (int)InterlockedExchangeAdd((LONG volatile *)val, (LONG)amount)
634 + amount;
635 return 1;
636 #endif
637 }
638
CRYPTO_atomic_add64(uint64_t * val,uint64_t op,uint64_t * ret,CRYPTO_RWLOCK * lock)639 int CRYPTO_atomic_add64(uint64_t *val, uint64_t op, uint64_t *ret,
640 CRYPTO_RWLOCK *lock)
641 {
642 #if (defined(NO_INTERLOCKEDOR64))
643 if (lock == NULL || !CRYPTO_THREAD_write_lock(lock))
644 return 0;
645 *val += op;
646 *ret = *val;
647
648 if (!CRYPTO_THREAD_unlock(lock))
649 return 0;
650
651 return 1;
652 #else
653 *ret = (uint64_t)InterlockedAdd64((LONG64 volatile *)val, (LONG64)op);
654 return 1;
655 #endif
656 }
657
CRYPTO_atomic_and(uint64_t * val,uint64_t op,uint64_t * ret,CRYPTO_RWLOCK * lock)658 int CRYPTO_atomic_and(uint64_t *val, uint64_t op, uint64_t *ret,
659 CRYPTO_RWLOCK *lock)
660 {
661 #if (defined(NO_INTERLOCKEDOR64))
662 if (lock == NULL || !CRYPTO_THREAD_write_lock(lock))
663 return 0;
664 *val &= op;
665 *ret = *val;
666
667 if (!CRYPTO_THREAD_unlock(lock))
668 return 0;
669
670 return 1;
671 #else
672 *ret = (uint64_t)InterlockedAnd64((LONG64 volatile *)val, (LONG64)op) & op;
673 return 1;
674 #endif
675 }
676
CRYPTO_atomic_or(uint64_t * val,uint64_t op,uint64_t * ret,CRYPTO_RWLOCK * lock)677 int CRYPTO_atomic_or(uint64_t *val, uint64_t op, uint64_t *ret,
678 CRYPTO_RWLOCK *lock)
679 {
680 #if (defined(NO_INTERLOCKEDOR64))
681 if (lock == NULL || !CRYPTO_THREAD_write_lock(lock))
682 return 0;
683 *val |= op;
684 *ret = *val;
685
686 if (!CRYPTO_THREAD_unlock(lock))
687 return 0;
688
689 return 1;
690 #else
691 *ret = (uint64_t)InterlockedOr64((LONG64 volatile *)val, (LONG64)op) | op;
692 return 1;
693 #endif
694 }
695
CRYPTO_atomic_load(uint64_t * val,uint64_t * ret,CRYPTO_RWLOCK * lock)696 int CRYPTO_atomic_load(uint64_t *val, uint64_t *ret, CRYPTO_RWLOCK *lock)
697 {
698 #if (defined(NO_INTERLOCKEDOR64))
699 if (lock == NULL || !CRYPTO_THREAD_read_lock(lock))
700 return 0;
701 *ret = *val;
702 if (!CRYPTO_THREAD_unlock(lock))
703 return 0;
704
705 return 1;
706 #else
707 *ret = (uint64_t)InterlockedOr64((LONG64 volatile *)val, 0);
708 return 1;
709 #endif
710 }
711
CRYPTO_atomic_store(uint64_t * dst,uint64_t val,CRYPTO_RWLOCK * lock)712 int CRYPTO_atomic_store(uint64_t *dst, uint64_t val, CRYPTO_RWLOCK *lock)
713 {
714 #if (defined(NO_INTERLOCKEDOR64))
715 if (lock == NULL || !CRYPTO_THREAD_read_lock(lock))
716 return 0;
717 *dst = val;
718 if (!CRYPTO_THREAD_unlock(lock))
719 return 0;
720
721 return 1;
722 #else
723 InterlockedExchange64(dst, val);
724 return 1;
725 #endif
726 }
727
CRYPTO_atomic_load_int(int * val,int * ret,CRYPTO_RWLOCK * lock)728 int CRYPTO_atomic_load_int(int *val, int *ret, CRYPTO_RWLOCK *lock)
729 {
730 #if (defined(NO_INTERLOCKEDOR64))
731 if (lock == NULL || !CRYPTO_THREAD_read_lock(lock))
732 return 0;
733 *ret = *val;
734 if (!CRYPTO_THREAD_unlock(lock))
735 return 0;
736
737 return 1;
738 #else
739 /* On Windows, LONG (but not long) is always the same size as int. */
740 *ret = (int)InterlockedOr((LONG volatile *)val, 0);
741 return 1;
742 #endif
743 }
744
openssl_init_fork_handlers(void)745 int openssl_init_fork_handlers(void)
746 {
747 return 0;
748 }
749
openssl_get_fork_id(void)750 int openssl_get_fork_id(void)
751 {
752 return 0;
753 }
754 #endif
755