1e71b7053SJung-uk Kim /*
2610a21fdSJung-uk Kim  * Copyright 2016-2019 The OpenSSL Project Authors. All Rights Reserved.
3e71b7053SJung-uk Kim  *
4e71b7053SJung-uk Kim  * Licensed under the OpenSSL license (the "License").  You may not use
5e71b7053SJung-uk Kim  * this file except in compliance with the License.  You can obtain a copy
6e71b7053SJung-uk Kim  * in the file LICENSE in the source distribution or at
7e71b7053SJung-uk Kim  * https://www.openssl.org/source/license.html
8e71b7053SJung-uk Kim  */
9*17f01e99SJung-uk Kim #ifndef OSSL_INTERNAL_REFCOUNT_H
10*17f01e99SJung-uk Kim # define OSSL_INTERNAL_REFCOUNT_H
11e71b7053SJung-uk Kim 
12e71b7053SJung-uk Kim /* Used to checking reference counts, most while doing perl5 stuff :-) */
13e71b7053SJung-uk Kim # if defined(OPENSSL_NO_STDIO)
14e71b7053SJung-uk Kim #  if defined(REF_PRINT)
15e71b7053SJung-uk Kim #   error "REF_PRINT requires stdio"
16e71b7053SJung-uk Kim #  endif
17e71b7053SJung-uk Kim # endif
18e71b7053SJung-uk Kim 
19e71b7053SJung-uk Kim # if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L \
20e71b7053SJung-uk Kim      && !defined(__STDC_NO_ATOMICS__)
21e71b7053SJung-uk Kim #  include <stdatomic.h>
22e71b7053SJung-uk Kim #  define HAVE_C11_ATOMICS
23e71b7053SJung-uk Kim # endif
24e71b7053SJung-uk Kim 
25e71b7053SJung-uk Kim # if defined(HAVE_C11_ATOMICS) && defined(ATOMIC_INT_LOCK_FREE) \
26e71b7053SJung-uk Kim      && ATOMIC_INT_LOCK_FREE > 0
27e71b7053SJung-uk Kim 
28e71b7053SJung-uk Kim #  define HAVE_ATOMICS 1
29e71b7053SJung-uk Kim 
30e71b7053SJung-uk Kim typedef _Atomic int CRYPTO_REF_COUNT;
31e71b7053SJung-uk Kim 
32e71b7053SJung-uk Kim static inline int CRYPTO_UP_REF(_Atomic int *val, int *ret, void *lock)
33e71b7053SJung-uk Kim {
34e71b7053SJung-uk Kim     *ret = atomic_fetch_add_explicit(val, 1, memory_order_relaxed) + 1;
35e71b7053SJung-uk Kim     return 1;
36e71b7053SJung-uk Kim }
37e71b7053SJung-uk Kim 
38e71b7053SJung-uk Kim /*
39e71b7053SJung-uk Kim  * Changes to shared structure other than reference counter have to be
40e71b7053SJung-uk Kim  * serialized. And any kind of serialization implies a release fence. This
41e71b7053SJung-uk Kim  * means that by the time reference counter is decremented all other
42e71b7053SJung-uk Kim  * changes are visible on all processors. Hence decrement itself can be
43e71b7053SJung-uk Kim  * relaxed. In case it hits zero, object will be destructed. Since it's
44e71b7053SJung-uk Kim  * last use of the object, destructor programmer might reason that access
45e71b7053SJung-uk Kim  * to mutable members doesn't have to be serialized anymore, which would
46e71b7053SJung-uk Kim  * otherwise imply an acquire fence. Hence conditional acquire fence...
47e71b7053SJung-uk Kim  */
48e71b7053SJung-uk Kim static inline int CRYPTO_DOWN_REF(_Atomic int *val, int *ret, void *lock)
49e71b7053SJung-uk Kim {
50e71b7053SJung-uk Kim     *ret = atomic_fetch_sub_explicit(val, 1, memory_order_relaxed) - 1;
51e71b7053SJung-uk Kim     if (*ret == 0)
52e71b7053SJung-uk Kim         atomic_thread_fence(memory_order_acquire);
53e71b7053SJung-uk Kim     return 1;
54e71b7053SJung-uk Kim }
55e71b7053SJung-uk Kim 
56e71b7053SJung-uk Kim # elif defined(__GNUC__) && defined(__ATOMIC_RELAXED) && __GCC_ATOMIC_INT_LOCK_FREE > 0
57e71b7053SJung-uk Kim 
58e71b7053SJung-uk Kim #  define HAVE_ATOMICS 1
59e71b7053SJung-uk Kim 
60e71b7053SJung-uk Kim typedef int CRYPTO_REF_COUNT;
61e71b7053SJung-uk Kim 
62e71b7053SJung-uk Kim static __inline__ int CRYPTO_UP_REF(int *val, int *ret, void *lock)
63e71b7053SJung-uk Kim {
64e71b7053SJung-uk Kim     *ret = __atomic_fetch_add(val, 1, __ATOMIC_RELAXED) + 1;
65e71b7053SJung-uk Kim     return 1;
66e71b7053SJung-uk Kim }
67e71b7053SJung-uk Kim 
68e71b7053SJung-uk Kim static __inline__ int CRYPTO_DOWN_REF(int *val, int *ret, void *lock)
69e71b7053SJung-uk Kim {
70e71b7053SJung-uk Kim     *ret = __atomic_fetch_sub(val, 1, __ATOMIC_RELAXED) - 1;
71e71b7053SJung-uk Kim     if (*ret == 0)
72e71b7053SJung-uk Kim         __atomic_thread_fence(__ATOMIC_ACQUIRE);
73e71b7053SJung-uk Kim     return 1;
74e71b7053SJung-uk Kim }
75e71b7053SJung-uk Kim 
76e71b7053SJung-uk Kim # elif defined(_MSC_VER) && _MSC_VER>=1200
77e71b7053SJung-uk Kim 
78e71b7053SJung-uk Kim #  define HAVE_ATOMICS 1
79e71b7053SJung-uk Kim 
80e71b7053SJung-uk Kim typedef volatile int CRYPTO_REF_COUNT;
81e71b7053SJung-uk Kim 
82610a21fdSJung-uk Kim #  if (defined(_M_ARM) && _M_ARM>=7 && !defined(_WIN32_WCE)) || defined(_M_ARM64)
83e71b7053SJung-uk Kim #   include <intrin.h>
84e71b7053SJung-uk Kim #   if defined(_M_ARM64) && !defined(_ARM_BARRIER_ISH)
85e71b7053SJung-uk Kim #    define _ARM_BARRIER_ISH _ARM64_BARRIER_ISH
86e71b7053SJung-uk Kim #   endif
87e71b7053SJung-uk Kim 
88e71b7053SJung-uk Kim static __inline int CRYPTO_UP_REF(volatile int *val, int *ret, void *lock)
89e71b7053SJung-uk Kim {
90e71b7053SJung-uk Kim     *ret = _InterlockedExchangeAdd_nf(val, 1) + 1;
91e71b7053SJung-uk Kim     return 1;
92e71b7053SJung-uk Kim }
93e71b7053SJung-uk Kim 
94e71b7053SJung-uk Kim static __inline int CRYPTO_DOWN_REF(volatile int *val, int *ret, void *lock)
95e71b7053SJung-uk Kim {
96e71b7053SJung-uk Kim     *ret = _InterlockedExchangeAdd_nf(val, -1) - 1;
97e71b7053SJung-uk Kim     if (*ret == 0)
98e71b7053SJung-uk Kim         __dmb(_ARM_BARRIER_ISH);
99e71b7053SJung-uk Kim     return 1;
100e71b7053SJung-uk Kim }
101e71b7053SJung-uk Kim #  else
102610a21fdSJung-uk Kim #   if !defined(_WIN32_WCE)
103e71b7053SJung-uk Kim #    pragma intrinsic(_InterlockedExchangeAdd)
104610a21fdSJung-uk Kim #   else
105610a21fdSJung-uk Kim #    if _WIN32_WCE >= 0x600
106610a21fdSJung-uk Kim       extern long __cdecl _InterlockedExchangeAdd(long volatile*, long);
107610a21fdSJung-uk Kim #    else
108da327cd2SJung-uk Kim       /* under Windows CE we still have old-style Interlocked* functions */
109610a21fdSJung-uk Kim       extern long __cdecl InterlockedExchangeAdd(long volatile*, long);
110610a21fdSJung-uk Kim #     define _InterlockedExchangeAdd InterlockedExchangeAdd
111610a21fdSJung-uk Kim #    endif
112610a21fdSJung-uk Kim #   endif
113e71b7053SJung-uk Kim 
114e71b7053SJung-uk Kim static __inline int CRYPTO_UP_REF(volatile int *val, int *ret, void *lock)
115e71b7053SJung-uk Kim {
116e71b7053SJung-uk Kim     *ret = _InterlockedExchangeAdd(val, 1) + 1;
117e71b7053SJung-uk Kim     return 1;
118e71b7053SJung-uk Kim }
119e71b7053SJung-uk Kim 
120e71b7053SJung-uk Kim static __inline int CRYPTO_DOWN_REF(volatile int *val, int *ret, void *lock)
121e71b7053SJung-uk Kim {
122e71b7053SJung-uk Kim     *ret = _InterlockedExchangeAdd(val, -1) - 1;
123e71b7053SJung-uk Kim     return 1;
124e71b7053SJung-uk Kim }
125e71b7053SJung-uk Kim #  endif
126e71b7053SJung-uk Kim 
127e71b7053SJung-uk Kim # else
128e71b7053SJung-uk Kim 
129e71b7053SJung-uk Kim typedef int CRYPTO_REF_COUNT;
130e71b7053SJung-uk Kim 
131e71b7053SJung-uk Kim # define CRYPTO_UP_REF(val, ret, lock) CRYPTO_atomic_add(val, 1, ret, lock)
132e71b7053SJung-uk Kim # define CRYPTO_DOWN_REF(val, ret, lock) CRYPTO_atomic_add(val, -1, ret, lock)
133e71b7053SJung-uk Kim 
134e71b7053SJung-uk Kim # endif
135e71b7053SJung-uk Kim 
136e71b7053SJung-uk Kim # if !defined(NDEBUG) && !defined(OPENSSL_NO_STDIO)
137e71b7053SJung-uk Kim #  define REF_ASSERT_ISNT(test) \
138e71b7053SJung-uk Kim     (void)((test) ? (OPENSSL_die("refcount error", __FILE__, __LINE__), 1) : 0)
139e71b7053SJung-uk Kim # else
140e71b7053SJung-uk Kim #  define REF_ASSERT_ISNT(i)
141e71b7053SJung-uk Kim # endif
142e71b7053SJung-uk Kim 
143e71b7053SJung-uk Kim # ifdef REF_PRINT
144e71b7053SJung-uk Kim #  define REF_PRINT_COUNT(a, b) \
145e71b7053SJung-uk Kim         fprintf(stderr, "%p:%4d:%s\n", b, b->references, a)
146e71b7053SJung-uk Kim # else
147e71b7053SJung-uk Kim #  define REF_PRINT_COUNT(a, b)
148e71b7053SJung-uk Kim # endif
149e71b7053SJung-uk Kim 
150e71b7053SJung-uk Kim #endif
151