michael@0: // Copyright (c) 2009 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: // This file is an internal atomic implementation, use base/atomicops.h instead. michael@0: // michael@0: // LinuxKernelCmpxchg and Barrier_AtomicIncrement are from Google Gears. michael@0: michael@0: #ifndef BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_ michael@0: #define BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_ michael@0: michael@0: namespace base { michael@0: namespace subtle { michael@0: michael@0: // 0xffff0fc0 is the hard coded address of a function provided by michael@0: // the kernel which implements an atomic compare-exchange. On older michael@0: // ARM architecture revisions (pre-v6) this may be implemented using michael@0: // a syscall. This address is stable, and in active use (hard coded) michael@0: // by at least glibc-2.7 and the Android C library. michael@0: typedef Atomic32 (*LinuxKernelCmpxchgFunc)(Atomic32 old_value, michael@0: Atomic32 new_value, michael@0: volatile Atomic32* ptr); michael@0: LinuxKernelCmpxchgFunc pLinuxKernelCmpxchg __attribute__((weak)) = michael@0: (LinuxKernelCmpxchgFunc) 0xffff0fc0; michael@0: michael@0: typedef void (*LinuxKernelMemoryBarrierFunc)(void); michael@0: LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) = michael@0: (LinuxKernelMemoryBarrierFunc) 0xffff0fa0; michael@0: michael@0: michael@0: inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, michael@0: Atomic32 old_value, michael@0: Atomic32 new_value) { michael@0: Atomic32 prev_value = *ptr; michael@0: do { michael@0: if (!pLinuxKernelCmpxchg(old_value, new_value, michael@0: const_cast(ptr))) { michael@0: return old_value; michael@0: } michael@0: prev_value = *ptr; michael@0: } while (prev_value == old_value); michael@0: return prev_value; michael@0: } michael@0: michael@0: inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, michael@0: Atomic32 new_value) { michael@0: Atomic32 old_value; michael@0: do { michael@0: old_value = *ptr; michael@0: } while (pLinuxKernelCmpxchg(old_value, new_value, michael@0: const_cast(ptr))); michael@0: return old_value; michael@0: } michael@0: michael@0: inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, michael@0: Atomic32 increment) { michael@0: return Barrier_AtomicIncrement(ptr, increment); michael@0: } michael@0: michael@0: inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, michael@0: Atomic32 increment) { michael@0: for (;;) { michael@0: // Atomic exchange the old value with an incremented one. michael@0: Atomic32 old_value = *ptr; michael@0: Atomic32 new_value = old_value + increment; michael@0: if (pLinuxKernelCmpxchg(old_value, new_value, michael@0: const_cast(ptr)) == 0) { michael@0: // The exchange took place as expected. michael@0: return new_value; michael@0: } michael@0: // Otherwise, *ptr changed mid-loop and we need to retry. michael@0: } michael@0: michael@0: } michael@0: michael@0: inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, michael@0: Atomic32 old_value, michael@0: Atomic32 new_value) { michael@0: return NoBarrier_CompareAndSwap(ptr, old_value, new_value); michael@0: } michael@0: michael@0: inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, michael@0: Atomic32 old_value, michael@0: Atomic32 new_value) { michael@0: return NoBarrier_CompareAndSwap(ptr, old_value, new_value); michael@0: } michael@0: michael@0: inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { michael@0: *ptr = value; michael@0: } michael@0: michael@0: inline void MemoryBarrier() { michael@0: pLinuxKernelMemoryBarrier(); michael@0: } michael@0: michael@0: inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { michael@0: *ptr = value; michael@0: MemoryBarrier(); michael@0: } michael@0: michael@0: inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { michael@0: MemoryBarrier(); michael@0: *ptr = value; michael@0: } michael@0: michael@0: inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { michael@0: return *ptr; michael@0: } michael@0: michael@0: inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { michael@0: Atomic32 value = *ptr; michael@0: MemoryBarrier(); michael@0: return value; michael@0: } michael@0: michael@0: inline Atomic32 Release_Load(volatile const Atomic32* ptr) { michael@0: MemoryBarrier(); michael@0: return *ptr; michael@0: } michael@0: michael@0: } // namespace base::subtle michael@0: } // namespace base michael@0: michael@0: #endif // BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_