// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- /* Copyright (c) 2006, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // Implementation of atomic operations for Mac OS X. This file should not // be included directly. Clients should instead include // "base/atomicops.h". #ifndef BASE_ATOMICOPS_INTERNALS_MACOSX_H_ #define BASE_ATOMICOPS_INTERNALS_MACOSX_H_ typedef int32_t Atomic32; // MacOS uses long for intptr_t, AtomicWord and Atomic32 are always different // on the Mac, even when they are the same size. Similarly, on __ppc64__, // AtomicWord and Atomic64 are always different. Thus, we need explicit // casting. #ifdef __LP64__ #define AtomicWordCastType base::subtle::Atomic64 #else #define AtomicWordCastType Atomic32 #endif #if defined(__LP64__) || defined(__i386__) #define BASE_HAS_ATOMIC64 1 // Use only in tests and base/atomic* #endif #include namespace base { namespace subtle { #if !defined(__LP64__) && defined(__ppc__) // The Mac 64-bit OSAtomic implementations are not available for 32-bit PowerPC, // while the underlying assembly instructions are available only some // implementations of PowerPC. // The following inline functions will fail with the error message at compile // time ONLY IF they are called. So it is safe to use this header if user // code only calls AtomicWord and Atomic32 operations. // // NOTE(vchen): Implementation notes to implement the atomic ops below may // be found in "PowerPC Virtual Environment Architecture, Book II, // Version 2.02", January 28, 2005, Appendix B, page 46. Unfortunately, // extra care must be taken to ensure data are properly 8-byte aligned, and // that data are returned correctly according to Mac OS X ABI specs. inline int64_t OSAtomicCompareAndSwap64( int64_t oldValue, int64_t newValue, int64_t *theValue) { __asm__ __volatile__( "_OSAtomicCompareAndSwap64_not_supported_for_32_bit_ppc\n\t"); return 0; } inline int64_t OSAtomicAdd64(int64_t theAmount, int64_t *theValue) { __asm__ __volatile__( "_OSAtomicAdd64_not_supported_for_32_bit_ppc\n\t"); return 0; } inline int64_t OSAtomicCompareAndSwap64Barrier( int64_t oldValue, int64_t newValue, int64_t *theValue) { int64_t prev = OSAtomicCompareAndSwap64(oldValue, newValue, theValue); OSMemoryBarrier(); return prev; } inline int64_t OSAtomicAdd64Barrier( int64_t theAmount, int64_t *theValue) { int64_t new_val = OSAtomicAdd64(theAmount, theValue); OSMemoryBarrier(); return new_val; } #endif typedef int64_t Atomic64; inline void MemoryBarrier() { OSMemoryBarrier(); } // 32-bit Versions. inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr, Atomic32 old_value, Atomic32 new_value) { Atomic32 prev_value; do { if (OSAtomicCompareAndSwap32(old_value, new_value, const_cast(ptr))) { return old_value; } prev_value = *ptr; } while (prev_value == old_value); return prev_value; } inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32 *ptr, Atomic32 new_value) { Atomic32 old_value; do { old_value = *ptr; } while (!OSAtomicCompareAndSwap32(old_value, new_value, const_cast(ptr))); return old_value; } inline Atomic32 Acquire_AtomicExchange(volatile Atomic32 *ptr, Atomic32 new_value) { Atomic32 old_value; do { old_value = *ptr; } while (!OSAtomicCompareAndSwap32Barrier(old_value, new_value, const_cast(ptr))); return old_value; } inline Atomic32 Release_AtomicExchange(volatile Atomic32 *ptr, Atomic32 new_value) { return Acquire_AtomicExchange(ptr, new_value); } inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32 *ptr, Atomic32 old_value, Atomic32 new_value) { Atomic32 prev_value; do { if (OSAtomicCompareAndSwap32Barrier(old_value, new_value, const_cast(ptr))) { return old_value; } prev_value = *ptr; } while (prev_value == old_value); return prev_value; } inline Atomic32 Release_CompareAndSwap(volatile Atomic32 *ptr, Atomic32 old_value, Atomic32 new_value) { return Acquire_CompareAndSwap(ptr, old_value, new_value); } inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { *ptr = value; } inline void Acquire_Store(volatile Atomic32 *ptr, Atomic32 value) { *ptr = value; MemoryBarrier(); } inline void Release_Store(volatile Atomic32 *ptr, Atomic32 value) { MemoryBarrier(); *ptr = value; } inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { return *ptr; } inline Atomic32 Acquire_Load(volatile const Atomic32 *ptr) { Atomic32 value = *ptr; MemoryBarrier(); return value; } inline Atomic32 Release_Load(volatile const Atomic32 *ptr) { MemoryBarrier(); return *ptr; } // 64-bit version inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64 *ptr, Atomic64 old_value, Atomic64 new_value) { Atomic64 prev_value; do { if (OSAtomicCompareAndSwap64(old_value, new_value, const_cast(ptr))) { return old_value; } prev_value = *ptr; } while (prev_value == old_value); return prev_value; } inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64 *ptr, Atomic64 new_value) { Atomic64 old_value; do { old_value = *ptr; } while (!OSAtomicCompareAndSwap64(old_value, new_value, const_cast(ptr))); return old_value; } inline Atomic64 Acquire_AtomicExchange(volatile Atomic64 *ptr, Atomic64 new_value) { Atomic64 old_value; do { old_value = *ptr; } while (!OSAtomicCompareAndSwap64Barrier(old_value, new_value, const_cast(ptr))); return old_value; } inline Atomic64 Release_AtomicExchange(volatile Atomic64 *ptr, Atomic64 new_value) { return Acquire_AtomicExchange(ptr, new_value); } inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr, Atomic64 old_value, Atomic64 new_value) { Atomic64 prev_value; do { if (OSAtomicCompareAndSwap64Barrier(old_value, new_value, const_cast(ptr))) { return old_value; } prev_value = *ptr; } while (prev_value == old_value); return prev_value; } inline Atomic64 Release_CompareAndSwap(volatile Atomic64 *ptr, Atomic64 old_value, Atomic64 new_value) { // The lib kern interface does not distinguish between // Acquire and Release memory barriers; they are equivalent. return Acquire_CompareAndSwap(ptr, old_value, new_value); } #ifdef __LP64__ // 64-bit implementation on 64-bit platform inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { *ptr = value; } inline void Acquire_Store(volatile Atomic64 *ptr, Atomic64 value) { *ptr = value; MemoryBarrier(); } inline void Release_Store(volatile Atomic64 *ptr, Atomic64 value) { MemoryBarrier(); *ptr = value; } inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { return *ptr; } inline Atomic64 Acquire_Load(volatile const Atomic64 *ptr) { Atomic64 value = *ptr; MemoryBarrier(); return value; } inline Atomic64 Release_Load(volatile const Atomic64 *ptr) { MemoryBarrier(); return *ptr; } #else // 64-bit implementation on 32-bit platform #if defined(__ppc__) inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { __asm__ __volatile__( "_NoBarrier_Store_not_supported_for_32_bit_ppc\n\t"); } inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { __asm__ __volatile__( "_NoBarrier_Load_not_supported_for_32_bit_ppc\n\t"); return 0; } #elif defined(__i386__) inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { __asm__ __volatile__("movq %1, %%mm0\n\t" // Use mmx reg for 64-bit atomic "movq %%mm0, %0\n\t" // moves (ptr could be read-only) "emms\n\t" // Reset FP registers : "=m" (*ptr) : "m" (value) : // mark the FP stack and mmx registers as clobbered "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)", "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7"); } inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { Atomic64 value; __asm__ __volatile__("movq %1, %%mm0\n\t" // Use mmx reg for 64-bit atomic "movq %%mm0, %0\n\t" // moves (ptr could be read-only) "emms\n\t" // Reset FP registers : "=m" (value) : "m" (*ptr) : // mark the FP stack and mmx registers as clobbered "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)", "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7"); return value; } #endif inline void Acquire_Store(volatile Atomic64 *ptr, Atomic64 value) { NoBarrier_Store(ptr, value); MemoryBarrier(); } inline void Release_Store(volatile Atomic64 *ptr, Atomic64 value) { MemoryBarrier(); NoBarrier_Store(ptr, value); } inline Atomic64 Acquire_Load(volatile const Atomic64 *ptr) { Atomic64 value = NoBarrier_Load(ptr); MemoryBarrier(); return value; } inline Atomic64 Release_Load(volatile const Atomic64 *ptr) { MemoryBarrier(); return NoBarrier_Load(ptr); } #endif // __LP64__ } // namespace base::subtle } // namespace base #endif // BASE_ATOMICOPS_INTERNALS_MACOSX_H_