1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to you under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  * https://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14  * implied.  See the License for the specific language governing
15  * permissions and limitations under the License.
16  */
17 
18 #ifndef AVRO_REFCOUNT_H
19 #define AVRO_REFCOUNT_H
20 
21 #if defined(_WIN32) && defined(__cplusplus)
22 /* Include the C++ file <intrin.h> outside the scope of extern "C" */
23 #include <intrin.h>
24 #endif
25 
26 #ifdef __cplusplus
27 extern "C" {
28 #define CLOSE_EXTERN }
29 #else
30 #define CLOSE_EXTERN
31 #endif
32 
33 /**
34  * Atomically sets the value of a reference count.
35  */
36 
37 static inline void
38 avro_refcount_set(volatile int *refcount, int value);
39 
40 /**
41  * Increments a reference count, ensuring that its value doesn't
42  * overflow.
43  */
44 
45 static inline void
46 avro_refcount_inc(volatile int *refcount);
47 
48 /**
49  * Decrements a reference count, and returns whether the resulting
50  * (decremented) value is 0.
51  */
52 
53 static inline int
54 avro_refcount_dec(volatile int *refcount);
55 
56 
57 /*-----------------------------------------------------------------------
58  * Non-Atomic Reference Count
59  */
60 #if defined(AVRO_NON_ATOMIC_REFCOUNT)
61 static inline void
avro_refcount_set(volatile int * refcount,int value)62 avro_refcount_set(volatile int *refcount, int value)
63 {
64 	*refcount = value;
65 }
66 
67 static inline void
avro_refcount_inc(volatile int * refcount)68 avro_refcount_inc(volatile int *refcount)
69 {
70 	if (*refcount != (int) -1) {
71 		*refcount += 1;
72 	}
73 }
74 
75 static inline int
avro_refcount_dec(volatile int * refcount)76 avro_refcount_dec(volatile int *refcount)
77 {
78 	if (*refcount != (int) -1) {
79 		*refcount -= 1;
80 		return (*refcount == 0);
81 	}
82 	return 0;
83 }
84 
85 /*-----------------------------------------------------------------------
86  * Mac OS X
87  */
88 
89 #elif __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050
90 
91 #include <libkern/OSAtomic.h>
92 
93 static inline void
avro_refcount_set(volatile int * refcount,int value)94 avro_refcount_set(volatile int *refcount, int value)
95 {
96 	*refcount = value;
97 }
98 
99 static inline void
avro_refcount_inc(volatile int * refcount)100 avro_refcount_inc(volatile int *refcount)
101 {
102 	if (*refcount != (int) -1) {
103 		OSAtomicIncrement32(refcount);
104 	}
105 }
106 
107 static inline int
avro_refcount_dec(volatile int * refcount)108 avro_refcount_dec(volatile int *refcount)
109 {
110 	if (*refcount != (int) -1) {
111 		return (OSAtomicDecrement32(refcount) == 0);
112 	}
113 	return 0;
114 }
115 
116 
117 /*-----------------------------------------------------------------------
118  * GCC intrinsics
119  */
120 
121 #elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) > 40500 \
122 || defined(__clang__)
123 
124 static inline void
avro_refcount_set(volatile int * refcount,int value)125 avro_refcount_set(volatile int *refcount, int value)
126 {
127 	*refcount = value;
128 }
129 
130 static inline void
avro_refcount_inc(volatile int * refcount)131 avro_refcount_inc(volatile int *refcount)
132 {
133 	if (*refcount != (int) -1) {
134 		__sync_add_and_fetch(refcount, 1);
135 	}
136 }
137 
138 static inline int
avro_refcount_dec(volatile int * refcount)139 avro_refcount_dec(volatile int *refcount)
140 {
141 	if (*refcount != (int) -1) {
142 		return (__sync_sub_and_fetch(refcount, 1) == 0);
143 	}
144 	return 0;
145 }
146 
147 
148 /*-----------------------------------------------------------------------
149  * Raw x86 assembly
150  */
151 
152 #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
153 
154 /* determine the size of int */
155 
156 #include <limits.h>
157 #include <avro/platform.h>
158 #if INT_MAX == INT32_MAX
159 #define REFCOUNT_SS "l"
160 #elif INT_MAX == INT64_MAX
161 #define REFCOUNT_SS "q"
162 #else
163 #error "Unknown int size"
164 #endif
165 
166 static inline void
avro_refcount_set(volatile int * refcount,int value)167 avro_refcount_set(volatile int *refcount, int value)
168 {
169 	*refcount = value;
170 }
171 
172 static inline void
avro_refcount_inc(volatile int * refcount)173 avro_refcount_inc(volatile int *refcount)
174 {
175 	if (*refcount != (int) -1) {
176 		__asm__ __volatile__ ("lock ; inc"REFCOUNT_SS" %0"
177 				      :"=m" (*refcount)
178 				      :"m" (*refcount));
179 	}
180 }
181 
182 static inline int
avro_refcount_dec(volatile int * refcount)183 avro_refcount_dec(volatile int *refcount)
184 {
185 	if (*refcount != (int) -1) {
186 		char result;
187 		__asm__ __volatile__ ("lock ; dec"REFCOUNT_SS" %0; setz %1"
188 				      :"=m" (*refcount), "=q" (result)
189 				      :"m" (*refcount));
190 		return result;
191 	}
192 	return 0;
193 }
194 
195 #undef REFCOUNT_SS
196 
197 
198 /*-----------------------------------------------------------------------
199  * Raw PPC assembly
200  */
201 
202 #elif defined(__GNUC__) && defined(__ppc__)
203 
204 static inline int
avro_refcount_LL_int(volatile int * ptr)205 avro_refcount_LL_int(volatile int *ptr)
206 {
207 	int val;
208 	__asm__ __volatile__ ("lwarx %[val],0,%[ptr]"
209 			      : [val] "=r" (val)
210 			      : [ptr] "r" (&ptr)
211 			      : "cc");
212 
213 	return val;
214 }
215 
216 /* Returns non-zero if the store was successful, zero otherwise. */
217 static inline int
avro_refcount_SC_int(volatile int * ptr,int val)218 avro_refcount_SC_int(volatile int *ptr, int val)
219 {
220 	int ret = 1; /* init to non-zero, will be reset to 0 if SC was successful */
221 	__asm__ __volatile__ ("stwcx. %[val],0,%[ptr];\n"
222 			      "beq 1f;\n"
223 			      "li %[ret], 0;\n"
224 			      "1: ;\n"
225 			      : [ret] "=r" (ret)
226 			      : [ptr] "r" (&ptr), [val] "r" (val), "0" (ret)
227 			      : "cc", "memory");
228 	return ret;
229 }
230 
231 static inline void
avro_refcount_set(volatile int * refcount,int value)232 avro_refcount_set(volatile int *refcount, int value)
233 {
234 	*refcount = value;
235 }
236 
237 static inline void
avro_refcount_inc(volatile int * refcount)238 avro_refcount_inc(volatile int *refcount)
239 {
240 	int prev;
241 	do {
242 		prev = avro_refcount_LL_int(refcount);
243 		if (prev == (int) -1) {
244 			return;
245 		}
246 	} while (!avro_refcount_SC_int(refcount, prev + 1));
247 }
248 
249 static inline int
avro_refcount_dec(volatile int * refcount)250 avro_refcount_dec(volatile int *refcount)
251 {
252 	int prev;
253 	do {
254 		prev = avro_refcount_LL_int(refcount);
255 		if (prev == (int) -1) {
256 			return 0;
257 		}
258 	} while (!avro_refcount_SC_int(refcount, prev - 1));
259 	return prev == 1;
260 }
261 
262 
263 /*-----------------------------------------------------------------------
264  * Windows intrinsics
265  */
266 #elif defined(_WIN32)
267 
268 #ifdef __cplusplus
269 // Note: <intrin.h> included outside the extern "C" wrappers above
270 #else
271 #include <windows.h>
272 #include <intrin.h>
273 #endif // __cplusplus
274 
275 static inline void
avro_refcount_set(volatile int * refcount,int value)276 avro_refcount_set(volatile int *refcount, int value)
277 {
278 	*refcount = value;
279 }
280 
281 static inline void
avro_refcount_inc(volatile int * refcount)282 avro_refcount_inc(volatile int *refcount)
283 {
284 	if (*refcount != (int) -1) {
285 		_InterlockedIncrement((volatile long *) refcount);
286 	}
287 }
288 
289 static inline int
avro_refcount_dec(volatile int * refcount)290 avro_refcount_dec(volatile int *refcount)
291 {
292 	if (*refcount != (int) -1) {
293 		return (_InterlockedDecrement((volatile long *) refcount) == 0);
294 	}
295 	return 0;
296 }
297 
298 /*-----------------------------------------------------------------------
299  * Fallback
300  */
301 #else
302 #error "No atomic implementation!"
303 #endif
304 
305 CLOSE_EXTERN
306 #endif
307