blob: 008aa871516f6c59adb4a5a8724435397b304c08 [file] [log] [blame]
Elliott Hughes5ea047b2011-09-13 14:38:18 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "atomic.h"
18
Elliott Hughes7c6169d2012-05-02 16:11:48 -070019#include <pthread.h>
Ian Rogers25fd14b2012-09-05 10:56:38 -070020#include <vector>
Elliott Hughes7c6169d2012-05-02 16:11:48 -070021
22#include "mutex.h"
23#include "stl_util.h"
24#include "stringprintf.h"
Ian Rogers50b35e22012-10-04 10:09:15 -070025#include "thread.h"
Elliott Hughes7c6169d2012-05-02 16:11:48 -070026
27#if defined(__APPLE__)
28#include <libkern/OSAtomic.h>
29#endif
30#if defined(__arm__)
31#include <machine/cpu-features.h>
32#endif
Elliott Hughes5ea047b2011-09-13 14:38:18 -070033
34namespace art {
35
Elliott Hughes5ea047b2011-09-13 14:38:18 -070036#if defined(HAVE_MACOSX_IPC)
Elliott Hughes7c6169d2012-05-02 16:11:48 -070037#define NEED_MAC_QUASI_ATOMICS 1
Elliott Hughes5ea047b2011-09-13 14:38:18 -070038
39#elif defined(__i386__) || defined(__x86_64__)
Elliott Hughes7c6169d2012-05-02 16:11:48 -070040#define NEED_PTHREADS_QUASI_ATOMICS 1
Elliott Hughes5ea047b2011-09-13 14:38:18 -070041
Elliott Hughes7c6169d2012-05-02 16:11:48 -070042#elif defined(__mips__)
43#define NEED_PTHREADS_QUASI_ATOMICS 1
Elliott Hughes5ea047b2011-09-13 14:38:18 -070044
Elliott Hughes7c6169d2012-05-02 16:11:48 -070045#elif defined(__arm__)
46
47#if defined(__ARM_HAVE_LDREXD)
48#define NEED_ARM_LDREXD_QUASI_ATOMICS 1
49#else
50#define NEED_PTHREADS_QUASI_ATOMICS 1
51#endif
52
Elliott Hughes7c6169d2012-05-02 16:11:48 -070053#else
54#error "QuasiAtomic unsupported on this platform"
55#endif
56
57// *****************************************************************************
58
59#if NEED_ARM_LDREXD_QUASI_ATOMICS
60
Elliott Hughes557e0272011-09-29 10:52:22 -070061static inline int64_t QuasiAtomicSwap64Impl(int64_t new_value, volatile int64_t* addr) {
Elliott Hughes5ea047b2011-09-13 14:38:18 -070062 int64_t prev;
63 int status;
64 do {
Elliott Hughes7c6169d2012-05-02 16:11:48 -070065 __asm__ __volatile__("@ QuasiAtomic::Swap64\n"
Elliott Hughes5ea047b2011-09-13 14:38:18 -070066 "ldrexd %0, %H0, [%3]\n"
67 "strexd %1, %4, %H4, [%3]"
68 : "=&r" (prev), "=&r" (status), "+m"(*addr)
69 : "r" (addr), "r" (new_value)
70 : "cc");
71 } while (__builtin_expect(status != 0, 0));
72 return prev;
73}
74
Elliott Hughes7c6169d2012-05-02 16:11:48 -070075int64_t QuasiAtomic::Swap64(int64_t new_value, volatile int64_t* addr) {
Elliott Hughes557e0272011-09-29 10:52:22 -070076 return QuasiAtomicSwap64Impl(new_value, addr);
77}
78
Elliott Hughes7c6169d2012-05-02 16:11:48 -070079int64_t QuasiAtomic::Swap64Sync(int64_t new_value, volatile int64_t* addr) {
Elliott Hughes557e0272011-09-29 10:52:22 -070080 ANDROID_MEMBAR_STORE();
81 int64_t old_value = QuasiAtomicSwap64Impl(new_value, addr);
82 ANDROID_MEMBAR_FULL();
83 return old_value;
84}
85
Elliott Hughes7c6169d2012-05-02 16:11:48 -070086int64_t QuasiAtomic::Read64(volatile const int64_t* addr) {
87 int64_t value;
88 __asm__ __volatile__("@ QuasiAtomic::Read64\n"
89 "ldrexd %0, %H0, [%1]"
90 : "=&r" (value)
91 : "r" (addr));
92 return value;
93}
94
95int QuasiAtomic::Cas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) {
Elliott Hughes5ea047b2011-09-13 14:38:18 -070096 int64_t prev;
97 int status;
98 do {
Elliott Hughes7c6169d2012-05-02 16:11:48 -070099 __asm__ __volatile__("@ QuasiAtomic::Cas64\n"
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700100 "ldrexd %0, %H0, [%3]\n"
101 "mov %1, #0\n"
102 "teq %0, %4\n"
103 "teqeq %H0, %H4\n"
104 "strexdeq %1, %5, %H5, [%3]"
105 : "=&r" (prev), "=&r" (status), "+m"(*addr)
106 : "r" (addr), "Ir" (old_value), "r" (new_value)
107 : "cc");
108 } while (__builtin_expect(status != 0, 0));
109 return prev != old_value;
110}
111
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700112#endif
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700113
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700114// *****************************************************************************
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700115
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700116#if NEED_MAC_QUASI_ATOMICS
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700117
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700118static inline int64_t QuasiAtomicSwap64Impl(int64_t value, volatile int64_t* addr) {
119 int64_t old_value;
120 do {
121 old_value = *addr;
122 } while (QuasiAtomic::Cas64(old_value, value, addr));
Elliott Hughes557e0272011-09-29 10:52:22 -0700123 return old_value;
124}
125
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700126int64_t QuasiAtomic::Swap64(int64_t value, volatile int64_t* addr) {
127 return QuasiAtomicSwap64Impl(value, addr);
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700128}
129
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700130int64_t QuasiAtomic::Swap64Sync(int64_t value, volatile int64_t* addr) {
131 ANDROID_MEMBAR_STORE();
132 int64_t old_value = QuasiAtomicSwap64Impl(value, addr);
133 // TUNING: barriers can be avoided on some architectures.
134 ANDROID_MEMBAR_FULL();
135 return old_value;
136}
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700137
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700138int64_t QuasiAtomic::Read64(volatile const int64_t* addr) {
139 return OSAtomicAdd64Barrier(0, const_cast<volatile int64_t*>(addr));
140}
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700141
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700142int QuasiAtomic::Cas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) {
143 return OSAtomicCompareAndSwap64Barrier(old_value, new_value, const_cast<int64_t*>(addr)) == 0;
144}
145
146#endif
147
148// *****************************************************************************
149
150#if NEED_PTHREADS_QUASI_ATOMICS
151
152// In the absence of a better implementation, we implement the 64-bit atomic
153// operations through mutex locking.
154
155// We stripe across a bunch of different mutexes to reduce contention.
156static const size_t kSwapLockCount = 32;
157static std::vector<Mutex*>* gSwapLocks;
158
159void QuasiAtomic::Startup() {
160 gSwapLocks = new std::vector<Mutex*>;
161 for (size_t i = 0; i < kSwapLockCount; ++i) {
162 gSwapLocks->push_back(new Mutex(StringPrintf("QuasiAtomic stripe %d", i).c_str()));
163 }
164}
165
166void QuasiAtomic::Shutdown() {
167 STLDeleteElements(gSwapLocks);
168 delete gSwapLocks;
169}
170
171static inline Mutex& GetSwapLock(const volatile int64_t* addr) {
172 return *(*gSwapLocks)[((unsigned)(void*)(addr) >> 3U) % kSwapLockCount];
173}
174
175int64_t QuasiAtomic::Swap64(int64_t value, volatile int64_t* addr) {
Ian Rogers50b35e22012-10-04 10:09:15 -0700176 MutexLock mu(Thread::Current(), GetSwapLock(addr));
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700177 int64_t old_value = *addr;
178 *addr = value;
179 return old_value;
180}
181
182int64_t QuasiAtomic::Swap64Sync(int64_t value, volatile int64_t* addr) {
183 // Same as QuasiAtomicSwap64 - mutex handles barrier.
184 return QuasiAtomic::Swap64(value, addr);
185}
186
187int QuasiAtomic::Cas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) {
Ian Rogers50b35e22012-10-04 10:09:15 -0700188 MutexLock mu(Thread::Current(), GetSwapLock(addr));
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700189 if (*addr == old_value) {
190 *addr = new_value;
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700191 return 0;
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700192 }
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700193 return 1;
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700194}
195
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700196int64_t QuasiAtomic::Read64(volatile const int64_t* addr) {
Ian Rogers50b35e22012-10-04 10:09:15 -0700197 MutexLock mu(Thread::Current(), GetSwapLock(addr));
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700198 return *addr;
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700199}
200
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700201#else
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700202
203// The other implementations don't need any special setup.
204void QuasiAtomic::Startup() {}
205void QuasiAtomic::Shutdown() {}
206
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700207#endif
208
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700209} // namespace art