1 // Copyright (c) 2003, 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 // Author: Lei Zhang, Sasha Levitskiy 32 // 33 // This file is an internal atomic implementation, use base/atomicops.h instead. 34 // 35 // LinuxKernelCmpxchg and Barrier_AtomicIncrement are from Google Gears. 36 37 #ifndef BASE_ATOMICOPS_INTERNALS_ARM_GENERIC_H_ 38 #define BASE_ATOMICOPS_INTERNALS_ARM_GENERIC_H_ 39 40 #include <stdio.h> 41 #include "base/abort.h" 42 #include "base/basictypes.h" 43 44 typedef int32_t Atomic32; 45 46 namespace base { 47 namespace subtle { 48 49 typedef int64_t Atomic64; 50 51 // 0xffff0fc0 is the hard coded address of a function provided by 52 // the kernel which implements an atomic compare-exchange. On older 53 // ARM architecture revisions (pre-v6) this may be implemented using 54 // a syscall. This address is stable, and in active use (hard coded) 55 // by at least glibc-2.7 and the Android C library. 56 // pLinuxKernelCmpxchg has both acquire and release barrier sematincs. 57 typedef Atomic32 (*LinuxKernelCmpxchgFunc)(Atomic32 old_value, 58 Atomic32 new_value, 59 volatile Atomic32* ptr); 60 LinuxKernelCmpxchgFunc pLinuxKernelCmpxchg ATTRIBUTE_WEAK = 61 (LinuxKernelCmpxchgFunc) 0xffff0fc0; 62 63 typedef void (*LinuxKernelMemoryBarrierFunc)(void); 64 LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier ATTRIBUTE_WEAK = 65 (LinuxKernelMemoryBarrierFunc) 0xffff0fa0; 66 67 68 inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, 69 Atomic32 old_value, 70 Atomic32 new_value) { 71 Atomic32 prev_value = *ptr; 72 do { 73 if (!pLinuxKernelCmpxchg(old_value, new_value, 74 const_cast<Atomic32*>(ptr))) { 75 return old_value; 76 } 77 prev_value = *ptr; 78 } while (prev_value == old_value); 79 return prev_value; 80 } 81 82 inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, 83 Atomic32 new_value) { 84 Atomic32 old_value; 85 do { 86 old_value = *ptr; 87 } while (pLinuxKernelCmpxchg(old_value, new_value, 88 const_cast<Atomic32*>(ptr))); 89 return old_value; 90 } 91 92 inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, 93 Atomic32 increment) { 94 for (;;) { 95 // Atomic exchange the old value with an incremented one. 96 Atomic32 old_value = *ptr; 97 Atomic32 new_value = old_value + increment; 98 if (pLinuxKernelCmpxchg(old_value, new_value, 99 const_cast<Atomic32*>(ptr)) == 0) { 100 // The exchange took place as expected. 101 return new_value; 102 } 103 // Otherwise, *ptr changed mid-loop and we need to retry. 104 } 105 } 106 107 inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, 108 Atomic32 increment) { 109 return Barrier_AtomicIncrement(ptr, increment); 110 } 111 112 inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, 113 Atomic32 old_value, 114 Atomic32 new_value) { 115 return NoBarrier_CompareAndSwap(ptr, old_value, new_value); 116 } 117 118 inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, 119 Atomic32 old_value, 120 Atomic32 new_value) { 121 return NoBarrier_CompareAndSwap(ptr, old_value, new_value); 122 } 123 124 inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { 125 *ptr = value; 126 } 127 128 inline void MemoryBarrier() { 129 pLinuxKernelMemoryBarrier(); 130 } 131 132 inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { 133 *ptr = value; 134 MemoryBarrier(); 135 } 136 137 inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { 138 MemoryBarrier(); 139 *ptr = value; 140 } 141 142 inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { 143 return *ptr; 144 } 145 146 inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { 147 Atomic32 value = *ptr; 148 MemoryBarrier(); 149 return value; 150 } 151 152 inline Atomic32 Release_Load(volatile const Atomic32* ptr) { 153 MemoryBarrier(); 154 return *ptr; 155 } 156 157 158 // 64-bit versions are not implemented yet. 159 160 inline void NotImplementedFatalError(const char *function_name) { 161 fprintf(stderr, "64-bit %s() not implemented on this platform\n", 162 function_name); 163 tcmalloc::Abort(); 164 } 165 166 inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, 167 Atomic64 old_value, 168 Atomic64 new_value) { 169 NotImplementedFatalError("NoBarrier_CompareAndSwap"); 170 return 0; 171 } 172 173 inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, 174 Atomic64 new_value) { 175 NotImplementedFatalError("NoBarrier_AtomicExchange"); 176 return 0; 177 } 178 179 inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, 180 Atomic64 increment) { 181 NotImplementedFatalError("NoBarrier_AtomicIncrement"); 182 return 0; 183 } 184 185 inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, 186 Atomic64 increment) { 187 NotImplementedFatalError("Barrier_AtomicIncrement"); 188 return 0; 189 } 190 191 inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { 192 NotImplementedFatalError("NoBarrier_Store"); 193 } 194 195 inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { 196 NotImplementedFatalError("Acquire_Store64"); 197 } 198 199 inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { 200 NotImplementedFatalError("Release_Store"); 201 } 202 203 inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { 204 NotImplementedFatalError("NoBarrier_Load"); 205 return 0; 206 } 207 208 inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { 209 NotImplementedFatalError("Atomic64 Acquire_Load"); 210 return 0; 211 } 212 213 inline Atomic64 Release_Load(volatile const Atomic64* ptr) { 214 NotImplementedFatalError("Atomic64 Release_Load"); 215 return 0; 216 } 217 218 inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, 219 Atomic64 old_value, 220 Atomic64 new_value) { 221 NotImplementedFatalError("Atomic64 Acquire_CompareAndSwap"); 222 return 0; 223 } 224 225 inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, 226 Atomic64 old_value, 227 Atomic64 new_value) { 228 NotImplementedFatalError("Atomic64 Release_CompareAndSwap"); 229 return 0; 230 } 231 232 } // namespace base::subtle 233 } // namespace base 234 235 #endif // BASE_ATOMICOPS_INTERNALS_ARM_GENERIC_H_ 236