1 /* Copyright (c) 2006, Google Inc.
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 // Implementation of atomic operations for Mac OS X. This file should not
32 // be included directly. Clients should instead include
33 // "base/atomicops.h".
34
35 #ifndef BASE_ATOMICOPS_INTERNALS_MACOSX_H_
36 #define BASE_ATOMICOPS_INTERNALS_MACOSX_H_
37
38 typedef int32_t Atomic32;
39
40 // MacOS uses long for intptr_t, AtomicWord and Atomic32 are always different
41 // on the Mac, even when they are the same size. Similarly, on __ppc64__,
42 // AtomicWord and Atomic64 are always different. Thus, we need explicit
43 // casting.
44 #ifdef __LP64__
45 #define AtomicWordCastType base::subtle::Atomic64
46 #else
47 #define AtomicWordCastType Atomic32
48 #endif
49
50 #if defined(__LP64__) || defined(__i386__)
51 #define BASE_HAS_ATOMIC64 1 // Use only in tests and base/atomic*
52 #endif
53
54 #include <libkern/OSAtomic.h>
55
56 namespace base {
57 namespace subtle {
58
59 #if !defined(__LP64__) && defined(__ppc__)
60
61 // The Mac 64-bit OSAtomic implementations are not available for 32-bit PowerPC,
62 // while the underlying assembly instructions are available only some
63 // implementations of PowerPC.
64
65 // The following inline functions will fail with the error message at compile
66 // time ONLY IF they are called. So it is safe to use this header if user
67 // code only calls AtomicWord and Atomic32 operations.
68 //
69 // NOTE(vchen): Implementation notes to implement the atomic ops below may
70 // be found in "PowerPC Virtual Environment Architecture, Book II,
71 // Version 2.02", January 28, 2005, Appendix B, page 46. Unfortunately,
72 // extra care must be taken to ensure data are properly 8-byte aligned, and
73 // that data are returned correctly according to Mac OS X ABI specs.
74
OSAtomicCompareAndSwap64(int64_t oldValue,int64_t newValue,int64_t * theValue)75 inline int64_t OSAtomicCompareAndSwap64(
76 int64_t oldValue, int64_t newValue, int64_t *theValue) {
77 __asm__ __volatile__(
78 "_OSAtomicCompareAndSwap64_not_supported_for_32_bit_ppc\n\t");
79 return 0;
80 }
81
OSAtomicAdd64(int64_t theAmount,int64_t * theValue)82 inline int64_t OSAtomicAdd64(int64_t theAmount, int64_t *theValue) {
83 __asm__ __volatile__(
84 "_OSAtomicAdd64_not_supported_for_32_bit_ppc\n\t");
85 return 0;
86 }
87
OSAtomicCompareAndSwap64Barrier(int64_t oldValue,int64_t newValue,int64_t * theValue)88 inline int64_t OSAtomicCompareAndSwap64Barrier(
89 int64_t oldValue, int64_t newValue, int64_t *theValue) {
90 int64_t prev = OSAtomicCompareAndSwap64(oldValue, newValue, theValue);
91 OSMemoryBarrier();
92 return prev;
93 }
94
OSAtomicAdd64Barrier(int64_t theAmount,int64_t * theValue)95 inline int64_t OSAtomicAdd64Barrier(
96 int64_t theAmount, int64_t *theValue) {
97 int64_t new_val = OSAtomicAdd64(theAmount, theValue);
98 OSMemoryBarrier();
99 return new_val;
100 }
101 #endif
102
103 typedef int64_t Atomic64;
104
MemoryBarrier()105 inline void MemoryBarrier() {
106 OSMemoryBarrier();
107 }
108
109 // 32-bit Versions.
110
NoBarrier_CompareAndSwap(volatile Atomic32 * ptr,Atomic32 old_value,Atomic32 new_value)111 inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr,
112 Atomic32 old_value,
113 Atomic32 new_value) {
114 Atomic32 prev_value;
115 do {
116 if (OSAtomicCompareAndSwap32(old_value, new_value,
117 const_cast<Atomic32*>(ptr))) {
118 return old_value;
119 }
120 prev_value = *ptr;
121 } while (prev_value == old_value);
122 return prev_value;
123 }
124
NoBarrier_AtomicExchange(volatile Atomic32 * ptr,Atomic32 new_value)125 inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32 *ptr,
126 Atomic32 new_value) {
127 Atomic32 old_value;
128 do {
129 old_value = *ptr;
130 } while (!OSAtomicCompareAndSwap32(old_value, new_value,
131 const_cast<Atomic32*>(ptr)));
132 return old_value;
133 }
134
NoBarrier_AtomicIncrement(volatile Atomic32 * ptr,Atomic32 increment)135 inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32 *ptr,
136 Atomic32 increment) {
137 return OSAtomicAdd32(increment, const_cast<Atomic32*>(ptr));
138 }
139
Barrier_AtomicIncrement(volatile Atomic32 * ptr,Atomic32 increment)140 inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32 *ptr,
141 Atomic32 increment) {
142 return OSAtomicAdd32Barrier(increment, const_cast<Atomic32*>(ptr));
143 }
144
Acquire_CompareAndSwap(volatile Atomic32 * ptr,Atomic32 old_value,Atomic32 new_value)145 inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32 *ptr,
146 Atomic32 old_value,
147 Atomic32 new_value) {
148 Atomic32 prev_value;
149 do {
150 if (OSAtomicCompareAndSwap32Barrier(old_value, new_value,
151 const_cast<Atomic32*>(ptr))) {
152 return old_value;
153 }
154 prev_value = *ptr;
155 } while (prev_value == old_value);
156 return prev_value;
157 }
158
Release_CompareAndSwap(volatile Atomic32 * ptr,Atomic32 old_value,Atomic32 new_value)159 inline Atomic32 Release_CompareAndSwap(volatile Atomic32 *ptr,
160 Atomic32 old_value,
161 Atomic32 new_value) {
162 return Acquire_CompareAndSwap(ptr, old_value, new_value);
163 }
164
NoBarrier_Store(volatile Atomic32 * ptr,Atomic32 value)165 inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
166 *ptr = value;
167 }
168
Acquire_Store(volatile Atomic32 * ptr,Atomic32 value)169 inline void Acquire_Store(volatile Atomic32 *ptr, Atomic32 value) {
170 *ptr = value;
171 MemoryBarrier();
172 }
173
Release_Store(volatile Atomic32 * ptr,Atomic32 value)174 inline void Release_Store(volatile Atomic32 *ptr, Atomic32 value) {
175 MemoryBarrier();
176 *ptr = value;
177 }
178
NoBarrier_Load(volatile const Atomic32 * ptr)179 inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
180 return *ptr;
181 }
182
Acquire_Load(volatile const Atomic32 * ptr)183 inline Atomic32 Acquire_Load(volatile const Atomic32 *ptr) {
184 Atomic32 value = *ptr;
185 MemoryBarrier();
186 return value;
187 }
188
Release_Load(volatile const Atomic32 * ptr)189 inline Atomic32 Release_Load(volatile const Atomic32 *ptr) {
190 MemoryBarrier();
191 return *ptr;
192 }
193
194 // 64-bit version
195
NoBarrier_CompareAndSwap(volatile Atomic64 * ptr,Atomic64 old_value,Atomic64 new_value)196 inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64 *ptr,
197 Atomic64 old_value,
198 Atomic64 new_value) {
199 Atomic64 prev_value;
200 do {
201 if (OSAtomicCompareAndSwap64(old_value, new_value,
202 const_cast<Atomic64*>(ptr))) {
203 return old_value;
204 }
205 prev_value = *ptr;
206 } while (prev_value == old_value);
207 return prev_value;
208 }
209
NoBarrier_AtomicExchange(volatile Atomic64 * ptr,Atomic64 new_value)210 inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64 *ptr,
211 Atomic64 new_value) {
212 Atomic64 old_value;
213 do {
214 old_value = *ptr;
215 } while (!OSAtomicCompareAndSwap64(old_value, new_value,
216 const_cast<Atomic64*>(ptr)));
217 return old_value;
218 }
219
NoBarrier_AtomicIncrement(volatile Atomic64 * ptr,Atomic64 increment)220 inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64 *ptr,
221 Atomic64 increment) {
222 return OSAtomicAdd64(increment, const_cast<Atomic64*>(ptr));
223 }
224
Barrier_AtomicIncrement(volatile Atomic64 * ptr,Atomic64 increment)225 inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64 *ptr,
226 Atomic64 increment) {
227 return OSAtomicAdd64Barrier(increment, const_cast<Atomic64*>(ptr));
228 }
229
Acquire_CompareAndSwap(volatile Atomic64 * ptr,Atomic64 old_value,Atomic64 new_value)230 inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr,
231 Atomic64 old_value,
232 Atomic64 new_value) {
233 Atomic64 prev_value;
234 do {
235 if (OSAtomicCompareAndSwap64Barrier(old_value, new_value,
236 const_cast<Atomic64*>(ptr))) {
237 return old_value;
238 }
239 prev_value = *ptr;
240 } while (prev_value == old_value);
241 return prev_value;
242 }
243
Release_CompareAndSwap(volatile Atomic64 * ptr,Atomic64 old_value,Atomic64 new_value)244 inline Atomic64 Release_CompareAndSwap(volatile Atomic64 *ptr,
245 Atomic64 old_value,
246 Atomic64 new_value) {
247 // The lib kern interface does not distinguish between
248 // Acquire and Release memory barriers; they are equivalent.
249 return Acquire_CompareAndSwap(ptr, old_value, new_value);
250 }
251
252 #ifdef __LP64__
253
254 // 64-bit implementation on 64-bit platform
255
NoBarrier_Store(volatile Atomic64 * ptr,Atomic64 value)256 inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
257 *ptr = value;
258 }
259
Acquire_Store(volatile Atomic64 * ptr,Atomic64 value)260 inline void Acquire_Store(volatile Atomic64 *ptr, Atomic64 value) {
261 *ptr = value;
262 MemoryBarrier();
263 }
264
Release_Store(volatile Atomic64 * ptr,Atomic64 value)265 inline void Release_Store(volatile Atomic64 *ptr, Atomic64 value) {
266 MemoryBarrier();
267 *ptr = value;
268 }
269
NoBarrier_Load(volatile const Atomic64 * ptr)270 inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
271 return *ptr;
272 }
273
Acquire_Load(volatile const Atomic64 * ptr)274 inline Atomic64 Acquire_Load(volatile const Atomic64 *ptr) {
275 Atomic64 value = *ptr;
276 MemoryBarrier();
277 return value;
278 }
279
Release_Load(volatile const Atomic64 * ptr)280 inline Atomic64 Release_Load(volatile const Atomic64 *ptr) {
281 MemoryBarrier();
282 return *ptr;
283 }
284
285 #else
286
287 // 64-bit implementation on 32-bit platform
288
289 #if defined(__ppc__)
290
NoBarrier_Store(volatile Atomic64 * ptr,Atomic64 value)291 inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
292 __asm__ __volatile__(
293 "_NoBarrier_Store_not_supported_for_32_bit_ppc\n\t");
294 }
295
NoBarrier_Load(volatile const Atomic64 * ptr)296 inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
297 __asm__ __volatile__(
298 "_NoBarrier_Load_not_supported_for_32_bit_ppc\n\t");
299 return 0;
300 }
301
302 #elif defined(__i386__)
303
NoBarrier_Store(volatile Atomic64 * ptr,Atomic64 value)304 inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
305 __asm__ __volatile__("movq %1, %%mm0\n\t" // Use mmx reg for 64-bit atomic
306 "movq %%mm0, %0\n\t" // moves (ptr could be read-only)
307 "emms\n\t" // Reset FP registers
308 : "=m" (*ptr)
309 : "m" (value)
310 : // mark the FP stack and mmx registers as clobbered
311 "st", "st(1)", "st(2)", "st(3)", "st(4)",
312 "st(5)", "st(6)", "st(7)", "mm0", "mm1",
313 "mm2", "mm3", "mm4", "mm5", "mm6", "mm7");
314
315 }
316
NoBarrier_Load(volatile const Atomic64 * ptr)317 inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
318 Atomic64 value;
319 __asm__ __volatile__("movq %1, %%mm0\n\t" // Use mmx reg for 64-bit atomic
320 "movq %%mm0, %0\n\t" // moves (ptr could be read-only)
321 "emms\n\t" // Reset FP registers
322 : "=m" (value)
323 : "m" (*ptr)
324 : // mark the FP stack and mmx registers as clobbered
325 "st", "st(1)", "st(2)", "st(3)", "st(4)",
326 "st(5)", "st(6)", "st(7)", "mm0", "mm1",
327 "mm2", "mm3", "mm4", "mm5", "mm6", "mm7");
328
329 return value;
330 }
331 #endif
332
333
Acquire_Store(volatile Atomic64 * ptr,Atomic64 value)334 inline void Acquire_Store(volatile Atomic64 *ptr, Atomic64 value) {
335 NoBarrier_Store(ptr, value);
336 MemoryBarrier();
337 }
338
Release_Store(volatile Atomic64 * ptr,Atomic64 value)339 inline void Release_Store(volatile Atomic64 *ptr, Atomic64 value) {
340 MemoryBarrier();
341 NoBarrier_Store(ptr, value);
342 }
343
Acquire_Load(volatile const Atomic64 * ptr)344 inline Atomic64 Acquire_Load(volatile const Atomic64 *ptr) {
345 Atomic64 value = NoBarrier_Load(ptr);
346 MemoryBarrier();
347 return value;
348 }
349
Release_Load(volatile const Atomic64 * ptr)350 inline Atomic64 Release_Load(volatile const Atomic64 *ptr) {
351 MemoryBarrier();
352 return NoBarrier_Load(ptr);
353 }
354 #endif // __LP64__
355
356 } // namespace base::subtle
357 } // namespace base
358
359 #endif // BASE_ATOMICOPS_INTERNALS_MACOSX_H_
360