Add support for reference counting using C11 atomics
[openssl.git] / include / internal / refcount.h
1 /*
2  * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the OpenSSL license (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 #ifndef HEADER_INTERNAL_REFCOUNT_H
10 # define HEADER_INTERNAL_REFCOUNT_H
11
12 # if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__)
13 # include <stdatomic.h>
14 # define HAVE_C11_ATOMICS
15 # endif
16
17 # if defined(HAVE_C11_ATOMICS) && ATOMIC_INT_LOCK_FREE > 0
18
19 # define HAVE_ATOMICS 1
20
21 typedef _Atomic int CRYPTO_REF_COUNT;
22
23 static ossl_inline int CRYPTO_UP_REF(_Atomic int *val, int *ret, void *lock)
24 {
25     *ret = atomic_fetch_add_explicit(val, 1, memory_order_relaxed) + 1;
26     return 1;
27 }
28
29 static ossl_inline int CRYPTO_DOWN_REF(_Atomic int *val, int *ret, void *lock)
30 {
31     *ret = atomic_fetch_sub_explicit(val, 1, memory_order_release) - 1;
32     if (*ret == 0)
33         atomic_thread_fence(memory_order_acquire);
34     return 1;
35 }
36
37 # elif defined(__GNUC__) && defined(__ATOMIC_RELAXED) && __GCC_ATOMIC_INT_LOCK_FREE > 0
38
39 # define HAVE_ATOMICS 1
40
41 typedef int CRYPTO_REF_COUNT;
42
43 static ossl_inline int CRYPTO_UP_REF(int *val, int *ret, void *lock)
44 {
45     *ret = __atomic_fetch_add(val, 1, __ATOMIC_RELAXED) + 1;
46     return 1;
47 }
48
49 static ossl_inline int CRYPTO_DOWN_REF(int *val, int *ret, void *lock)
50 {
51     *ret = __atomic_fetch_sub(val, 1, __ATOMIC_RELEASE) - 1;
52     if (*ret == 0)
53         __atomic_thread_fence(__ATOMIC_ACQUIRE);
54     return 1;
55 }
56
57 # else
58
59 typedef int CRYPTO_REF_COUNT;
60
61 # define CRYPTO_UP_REF(val, ret, lock) CRYPTO_atomic_add(val, 1, ret, lock)
62 # define CRYPTO_DOWN_REF(val, ret, lock) CRYPTO_atomic_add(val, -1, ret, lock)
63
64 # endif
65 #endif