1 /*************************************************************************/
2 /*  safe_refcount.cpp                                                    */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
10 /*                                                                       */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the       */
13 /* "Software"), to deal in the Software without restriction, including   */
14 /* without limitation the rights to use, copy, modify, merge, publish,   */
15 /* distribute, sublicense, and/or sell copies of the Software, and to    */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions:                                             */
18 /*                                                                       */
19 /* The above copyright notice and this permission notice shall be        */
20 /* included in all copies or substantial portions of the Software.       */
21 /*                                                                       */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
29 /*************************************************************************/
30 #include "safe_refcount.h"
31 
32 // Atomic functions, these are used for multithread safe reference counters!
33 
34 #ifdef NO_THREADS
35 
36 /* Bogus implementation unaware of multiprocessing */
37 
38 template <class T>
_atomic_conditional_increment_impl(register T * pw)39 static _ALWAYS_INLINE_ T _atomic_conditional_increment_impl(register T *pw) {
40 
41 	if (*pw == 0)
42 		return 0;
43 
44 	(*pw)++;
45 
46 	return *pw;
47 }
48 
49 template <class T>
_atomic_decrement_impl(register T * pw)50 static _ALWAYS_INLINE_ T _atomic_decrement_impl(register T *pw) {
51 
52 	(*pw)--;
53 
54 	return *pw;
55 }
56 
57 template <class T>
_atomic_increment_impl(register T * pw)58 static _ALWAYS_INLINE_ T _atomic_increment_impl(register T *pw) {
59 
60 	(*pw)++;
61 
62 	return *pw;
63 }
64 
65 template <class T>
_atomic_sub_impl(register T * pw,register T val)66 static _ALWAYS_INLINE_ T _atomic_sub_impl(register T *pw, register T val) {
67 
68 	(*pw) -= val;
69 
70 	return *pw;
71 }
72 
73 template <class T>
_atomic_add_impl(register T * pw,register T val)74 static _ALWAYS_INLINE_ T _atomic_add_impl(register T *pw, register T val) {
75 
76 	(*pw) += val;
77 
78 	return *pw;
79 }
80 
81 template <class T>
_atomic_exchange_if_greater_impl(register T * pw,register T val)82 static _ALWAYS_INLINE_ T _atomic_exchange_if_greater_impl(register T *pw, register T val) {
83 
84 	if (val > *pw)
85 		*pw = val;
86 
87 	return *pw;
88 }
89 
90 #elif defined(__GNUC__)
91 
92 /* Implementation for GCC & Clang */
93 
94 // GCC guarantees atomic intrinsics for sizes of 1, 2, 4 and 8 bytes.
95 // Clang states it supports GCC atomic builtins.
96 
97 template <class T>
_atomic_conditional_increment_impl(register T * pw)98 static _ALWAYS_INLINE_ T _atomic_conditional_increment_impl(register T *pw) {
99 
100 	while (true) {
101 		T tmp = static_cast<T const volatile &>(*pw);
102 		if (tmp == 0)
103 			return 0; // if zero, can't add to it anymore
104 		if (__sync_val_compare_and_swap(pw, tmp, tmp + 1) == tmp)
105 			return tmp + 1;
106 	}
107 }
108 
109 template <class T>
_atomic_decrement_impl(register T * pw)110 static _ALWAYS_INLINE_ T _atomic_decrement_impl(register T *pw) {
111 
112 	return __sync_sub_and_fetch(pw, 1);
113 }
114 
115 template <class T>
_atomic_increment_impl(register T * pw)116 static _ALWAYS_INLINE_ T _atomic_increment_impl(register T *pw) {
117 
118 	return __sync_add_and_fetch(pw, 1);
119 }
120 
121 template <class T>
_atomic_sub_impl(register T * pw,register T val)122 static _ALWAYS_INLINE_ T _atomic_sub_impl(register T *pw, register T val) {
123 
124 	return __sync_sub_and_fetch(pw, val);
125 }
126 
127 template <class T>
_atomic_add_impl(register T * pw,register T val)128 static _ALWAYS_INLINE_ T _atomic_add_impl(register T *pw, register T val) {
129 
130 	return __sync_add_and_fetch(pw, val);
131 }
132 
133 template <class T>
_atomic_exchange_if_greater_impl(register T * pw,register T val)134 static _ALWAYS_INLINE_ T _atomic_exchange_if_greater_impl(register T *pw, register T val) {
135 
136 	while (true) {
137 		T tmp = static_cast<T const volatile &>(*pw);
138 		if (tmp >= val)
139 			return tmp; // already greater, or equal
140 		if (__sync_val_compare_and_swap(pw, tmp, val) == tmp)
141 			return val;
142 	}
143 }
144 
145 #elif defined(_MSC_VER)
146 
147 /* Implementation for MSVC-Windows */
148 
149 // don't pollute my namespace!
150 #include <windows.h>
151 
152 #define ATOMIC_CONDITIONAL_INCREMENT_BODY(m_pw, m_win_type, m_win_cmpxchg, m_cpp_type) \
153 	/* try to increment until it actually works */                                     \
154 	/* taken from boost */                                                             \
155 	while (true) {                                                                     \
156 		m_cpp_type tmp = static_cast<m_cpp_type const volatile &>(*(m_pw));            \
157 		if (tmp == 0)                                                                  \
158 			return 0; /* if zero, can't add to it anymore */                           \
159 		if (m_win_cmpxchg((m_win_type volatile *)(m_pw), tmp + 1, tmp) == tmp)         \
160 			return tmp + 1;                                                            \
161 	}
162 
163 #define ATOMIC_EXCHANGE_IF_GREATER_BODY(m_pw, m_val, m_win_type, m_win_cmpxchg, m_cpp_type) \
164 	while (true) {                                                                          \
165 		m_cpp_type tmp = static_cast<m_cpp_type const volatile &>(*(m_pw));                 \
166 		if (tmp >= m_val)                                                                   \
167 			return tmp; /* already greater, or equal */                                     \
168 		if (m_win_cmpxchg((m_win_type volatile *)(m_pw), m_val, tmp) == tmp)                \
169 			return m_val;                                                                   \
170 	}
171 
_atomic_conditional_increment_impl(register uint32_t * pw)172 static _ALWAYS_INLINE_ uint32_t _atomic_conditional_increment_impl(register uint32_t *pw) {
173 
174 	ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONG, InterlockedCompareExchange, uint32_t)
175 }
176 
_atomic_decrement_impl(register uint32_t * pw)177 static _ALWAYS_INLINE_ uint32_t _atomic_decrement_impl(register uint32_t *pw) {
178 
179 	return InterlockedDecrement((LONG volatile *)pw);
180 }
181 
_atomic_increment_impl(register uint32_t * pw)182 static _ALWAYS_INLINE_ uint32_t _atomic_increment_impl(register uint32_t *pw) {
183 
184 	return InterlockedIncrement((LONG volatile *)pw);
185 }
186 
_atomic_sub_impl(register uint32_t * pw,register uint32_t val)187 static _ALWAYS_INLINE_ uint32_t _atomic_sub_impl(register uint32_t *pw, register uint32_t val) {
188 
189 	return InterlockedExchangeAdd((LONG volatile *)pw, -(int32_t)val) - val;
190 }
191 
_atomic_add_impl(register uint32_t * pw,register uint32_t val)192 static _ALWAYS_INLINE_ uint32_t _atomic_add_impl(register uint32_t *pw, register uint32_t val) {
193 
194 	return InterlockedAdd((LONG volatile *)pw, val);
195 }
196 
_atomic_exchange_if_greater_impl(register uint32_t * pw,register uint32_t val)197 static _ALWAYS_INLINE_ uint32_t _atomic_exchange_if_greater_impl(register uint32_t *pw, register uint32_t val) {
198 
199 	ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONG, InterlockedCompareExchange, uint32_t)
200 }
201 
_atomic_conditional_increment_impl(register uint64_t * pw)202 static _ALWAYS_INLINE_ uint64_t _atomic_conditional_increment_impl(register uint64_t *pw) {
203 
204 	ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONGLONG, InterlockedCompareExchange64, uint64_t)
205 }
206 
_atomic_decrement_impl(register uint64_t * pw)207 static _ALWAYS_INLINE_ uint64_t _atomic_decrement_impl(register uint64_t *pw) {
208 
209 	return InterlockedDecrement64((LONGLONG volatile *)pw);
210 }
211 
_atomic_increment_impl(register uint64_t * pw)212 static _ALWAYS_INLINE_ uint64_t _atomic_increment_impl(register uint64_t *pw) {
213 
214 	return InterlockedIncrement64((LONGLONG volatile *)pw);
215 }
216 
_atomic_sub_impl(register uint64_t * pw,register uint64_t val)217 static _ALWAYS_INLINE_ uint64_t _atomic_sub_impl(register uint64_t *pw, register uint64_t val) {
218 
219 	return InterlockedExchangeAdd64((LONGLONG volatile *)pw, -(int64_t)val) - val;
220 }
221 
_atomic_add_impl(register uint64_t * pw,register uint64_t val)222 static _ALWAYS_INLINE_ uint64_t _atomic_add_impl(register uint64_t *pw, register uint64_t val) {
223 
224 	return InterlockedAdd64((LONGLONG volatile *)pw, val);
225 }
226 
_atomic_exchange_if_greater_impl(register uint64_t * pw,register uint64_t val)227 static _ALWAYS_INLINE_ uint64_t _atomic_exchange_if_greater_impl(register uint64_t *pw, register uint64_t val) {
228 
229 	ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONGLONG, InterlockedCompareExchange64, uint64_t)
230 }
231 
232 #else
233 
234 //no threads supported?
235 #error Must provide atomic functions for this platform or compiler!
236 
237 #endif
238 
239 // The actual advertised functions; they'll call the right implementation
240 
atomic_conditional_increment(register uint32_t * counter)241 uint32_t atomic_conditional_increment(register uint32_t *counter) {
242 	return _atomic_conditional_increment_impl(counter);
243 }
244 
atomic_decrement(register uint32_t * pw)245 uint32_t atomic_decrement(register uint32_t *pw) {
246 	return _atomic_decrement_impl(pw);
247 }
248 
atomic_increment(register uint32_t * pw)249 uint32_t atomic_increment(register uint32_t *pw) {
250 	return _atomic_increment_impl(pw);
251 }
252 
atomic_sub(register uint32_t * pw,register uint32_t val)253 uint32_t atomic_sub(register uint32_t *pw, register uint32_t val) {
254 	return _atomic_sub_impl(pw, val);
255 }
256 
atomic_add(register uint32_t * pw,register uint32_t val)257 uint32_t atomic_add(register uint32_t *pw, register uint32_t val) {
258 	return _atomic_add_impl(pw, val);
259 }
260 
atomic_exchange_if_greater(register uint32_t * pw,register uint32_t val)261 uint32_t atomic_exchange_if_greater(register uint32_t *pw, register uint32_t val) {
262 	return _atomic_exchange_if_greater_impl(pw, val);
263 }
264 
atomic_conditional_increment(register uint64_t * counter)265 uint64_t atomic_conditional_increment(register uint64_t *counter) {
266 	return _atomic_conditional_increment_impl(counter);
267 }
268 
atomic_decrement(register uint64_t * pw)269 uint64_t atomic_decrement(register uint64_t *pw) {
270 	return _atomic_decrement_impl(pw);
271 }
272 
atomic_increment(register uint64_t * pw)273 uint64_t atomic_increment(register uint64_t *pw) {
274 	return _atomic_increment_impl(pw);
275 }
276 
atomic_sub(register uint64_t * pw,register uint64_t val)277 uint64_t atomic_sub(register uint64_t *pw, register uint64_t val) {
278 	return _atomic_sub_impl(pw, val);
279 }
280 
atomic_add(register uint64_t * pw,register uint64_t val)281 uint64_t atomic_add(register uint64_t *pw, register uint64_t val) {
282 	return _atomic_add_impl(pw, val);
283 }
284 
atomic_exchange_if_greater(register uint64_t * pw,register uint64_t val)285 uint64_t atomic_exchange_if_greater(register uint64_t *pw, register uint64_t val) {
286 	return _atomic_exchange_if_greater_impl(pw, val);
287 }
288