atomics: Move atomics/recounting support to separate files
[strongswan.git] / src / libstrongswan / utils / utils / atomics.h
1 /*
2 * Copyright (C) 2008-2014 Tobias Brunner
3 * Copyright (C) 2008 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 /**
18 * @defgroup atomics_i atomics
19 * @{ @ingroup utils_i
20 */
21
22 #ifndef ATOMICS_H_
23 #define ATOMICS_H_
24
25 /**
26 * Special type to count references
27 */
28 typedef u_int refcount_t;
29
30 /* use __atomic* built-ins with GCC 4.7 and newer */
31 #ifdef __GNUC__
32 # if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 6))
33 # define HAVE_GCC_ATOMIC_OPERATIONS
34 # endif
35 #endif
36
37 #ifdef HAVE_GCC_ATOMIC_OPERATIONS
38
39 #define ref_get(ref) __atomic_add_fetch(ref, 1, __ATOMIC_RELAXED)
40 /* The relaxed memory model works fine for increments as these (usually) don't
41 * change the state of refcounted objects. But here we have to ensure that we
42 * free the right stuff if ref counted objects are mutable. So we have to sync
43 * with other threads that call ref_put(). It would be sufficient to use
44 * __ATOMIC_RELEASE here and then call __atomic_thread_fence() with
45 * __ATOMIC_ACQUIRE if we reach 0, but since we don't have control over the use
46 * of ref_put() we have to make sure. */
47 #define ref_put(ref) (!__atomic_sub_fetch(ref, 1, __ATOMIC_ACQ_REL))
48 #define ref_cur(ref) __atomic_load_n(ref, __ATOMIC_RELAXED)
49
50 #define _cas_impl(ptr, oldval, newval) ({ typeof(oldval) _old = oldval; \
51 __atomic_compare_exchange_n(ptr, &_old, newval, FALSE, \
52 __ATOMIC_SEQ_CST, __ATOMIC_RELAXED); })
53 #define cas_bool(ptr, oldval, newval) _cas_impl(ptr, oldval, newval)
54 #define cas_ptr(ptr, oldval, newval) _cas_impl(ptr, oldval, newval)
55
56 #elif defined(HAVE_GCC_SYNC_OPERATIONS)
57
58 #define ref_get(ref) __sync_add_and_fetch(ref, 1)
59 #define ref_put(ref) (!__sync_sub_and_fetch(ref, 1))
60 #define ref_cur(ref) __sync_fetch_and_add(ref, 0)
61
62 #define cas_bool(ptr, oldval, newval) \
63 (__sync_bool_compare_and_swap(ptr, oldval, newval))
64 #define cas_ptr(ptr, oldval, newval) \
65 (__sync_bool_compare_and_swap(ptr, oldval, newval))
66
67 #else /* !HAVE_GCC_ATOMIC_OPERATIONS && !HAVE_GCC_SYNC_OPERATIONS */
68
69 /**
70 * Get a new reference.
71 *
72 * Increments the reference counter atomically.
73 *
74 * @param ref pointer to ref counter
75 * @return new value of ref
76 */
77 refcount_t ref_get(refcount_t *ref);
78
79 /**
80 * Put back a unused reference.
81 *
82 * Decrements the reference counter atomically and
83 * says if more references available.
84 *
85 * @param ref pointer to ref counter
86 * @return TRUE if no more references counted
87 */
88 bool ref_put(refcount_t *ref);
89
90 /**
91 * Get the current value of the reference counter.
92 *
93 * @param ref pointer to ref counter
94 * @return current value of ref
95 */
96 refcount_t ref_cur(refcount_t *ref);
97
98 /**
99 * Atomically replace value of ptr with newval if it currently equals oldval.
100 *
101 * @param ptr pointer to variable
102 * @param oldval old value of the variable
103 * @param newval new value set if possible
104 * @return TRUE if value equaled oldval and newval was written
105 */
106 bool cas_bool(bool *ptr, bool oldval, bool newval);
107
108 /**
109 * Atomically replace value of ptr with newval if it currently equals oldval.
110 *
111 * @param ptr pointer to variable
112 * @param oldval old value of the variable
113 * @param newval new value set if possible
114 * @return TRUE if value equaled oldval and newval was written
115 */
116 bool cas_ptr(void **ptr, void *oldval, void *newval);
117
118 #endif /* HAVE_GCC_ATOMIC_OPERATIONS */
119
120 /**
121 * Initialize atomics utility functions
122 */
123 void atomics_init();
124
125 /**
126 * Clean up atomics utility functions
127 */
128 void atomics_deinit();
129
130 #endif /** ATOMICS_H_ @} */