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