1// Copyright (c) 2009 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// This file is an internal atomic implementation, use base/atomicops.h instead. 6// 7// LinuxKernelCmpxchg and Barrier_AtomicIncrement are from Google Gears. 8 9#ifndef BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_ 10#define BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_ 11 12namespace base { 13namespace subtle { 14 15// 0xffff0fc0 is the hard coded address of a function provided by 16// the kernel which implements an atomic compare-exchange. On older 17// ARM architecture revisions (pre-v6) this may be implemented using 18// a syscall. This address is stable, and in active use (hard coded) 19// by at least glibc-2.7 and the Android C library. 20typedef Atomic32 (*LinuxKernelCmpxchgFunc)(Atomic32 old_value, 21 Atomic32 new_value, 22 volatile Atomic32* ptr); 23LinuxKernelCmpxchgFunc pLinuxKernelCmpxchg __attribute__((weak)) = 24 (LinuxKernelCmpxchgFunc) 0xffff0fc0; 25 26typedef void (*LinuxKernelMemoryBarrierFunc)(void); 27LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) = 28 (LinuxKernelMemoryBarrierFunc) 0xffff0fa0; 29 30 31inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, 32 Atomic32 old_value, 33 Atomic32 new_value) { 34 Atomic32 prev_value = *ptr; 35 do { 36 if (!pLinuxKernelCmpxchg(old_value, new_value, 37 const_cast<Atomic32*>(ptr))) { 38 return old_value; 39 } 40 prev_value = *ptr; 41 } while (prev_value == old_value); 42 return prev_value; 43} 44 45inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, 46 Atomic32 new_value) { 47 Atomic32 old_value; 48 do { 49 old_value = *ptr; 50 } while (pLinuxKernelCmpxchg(old_value, new_value, 51 const_cast<Atomic32*>(ptr))); 52 return old_value; 53} 54 55inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, 56 Atomic32 increment) { 57 return Barrier_AtomicIncrement(ptr, increment); 58} 59 60inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, 61 Atomic32 increment) { 62 for (;;) { 63 // Atomic exchange the old value with an incremented one. 64 Atomic32 old_value = *ptr; 65 Atomic32 new_value = old_value + increment; 66 if (pLinuxKernelCmpxchg(old_value, new_value, 67 const_cast<Atomic32*>(ptr)) == 0) { 68 // The exchange took place as expected. 69 return new_value; 70 } 71 // Otherwise, *ptr changed mid-loop and we need to retry. 72 } 73 74} 75 76inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, 77 Atomic32 old_value, 78 Atomic32 new_value) { 79 return NoBarrier_CompareAndSwap(ptr, old_value, new_value); 80} 81 82inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, 83 Atomic32 old_value, 84 Atomic32 new_value) { 85 return NoBarrier_CompareAndSwap(ptr, old_value, new_value); 86} 87 88inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { 89 *ptr = value; 90} 91 92inline void MemoryBarrier() { 93 pLinuxKernelMemoryBarrier(); 94} 95 96inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { 97 *ptr = value; 98 MemoryBarrier(); 99} 100 101inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { 102 MemoryBarrier(); 103 *ptr = value; 104} 105 106inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { 107 return *ptr; 108} 109 110inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { 111 Atomic32 value = *ptr; 112 MemoryBarrier(); 113 return value; 114} 115 116inline Atomic32 Release_Load(volatile const Atomic32* ptr) { 117 MemoryBarrier(); 118 return *ptr; 119} 120 121} // namespace base::subtle 122} // namespace base 123 124#endif // BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_ 125