blob: fb4de9c9de68fb8c579522c026b4474bbfc7277b [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"
25
26#if defined(__APPLE__)
27#include <libkern/OSAtomic.h>
28#endif
29#if defined(__arm__)
30#include <machine/cpu-features.h>
31#endif
Elliott Hughes5ea047b2011-09-13 14:38:18 -070032
33namespace art {
34
Elliott Hughes5ea047b2011-09-13 14:38:18 -070035#if defined(HAVE_MACOSX_IPC)
Elliott Hughes7c6169d2012-05-02 16:11:48 -070036#define NEED_MAC_QUASI_ATOMICS 1
Elliott Hughes5ea047b2011-09-13 14:38:18 -070037
38#elif defined(__i386__) || defined(__x86_64__)
Elliott Hughes7c6169d2012-05-02 16:11:48 -070039#define NEED_PTHREADS_QUASI_ATOMICS 1
Elliott Hughes5ea047b2011-09-13 14:38:18 -070040
Elliott Hughes7c6169d2012-05-02 16:11:48 -070041#elif defined(__mips__)
42#define NEED_PTHREADS_QUASI_ATOMICS 1
Elliott Hughes5ea047b2011-09-13 14:38:18 -070043
Elliott Hughes7c6169d2012-05-02 16:11:48 -070044#elif defined(__arm__)
45
46#if defined(__ARM_HAVE_LDREXD)
47#define NEED_ARM_LDREXD_QUASI_ATOMICS 1
48#else
49#define NEED_PTHREADS_QUASI_ATOMICS 1
50#endif
51
52#elif defined(__sh__)
53#define NEED_PTHREADS_QUASI_ATOMICS 1
54
55#else
56#error "QuasiAtomic unsupported on this platform"
57#endif
58
59// *****************************************************************************
60
61#if NEED_ARM_LDREXD_QUASI_ATOMICS
62
Elliott Hughes557e0272011-09-29 10:52:22 -070063static inline int64_t QuasiAtomicSwap64Impl(int64_t new_value, volatile int64_t* addr) {
Elliott Hughes5ea047b2011-09-13 14:38:18 -070064 int64_t prev;
65 int status;
66 do {
Elliott Hughes7c6169d2012-05-02 16:11:48 -070067 __asm__ __volatile__("@ QuasiAtomic::Swap64\n"
Elliott Hughes5ea047b2011-09-13 14:38:18 -070068 "ldrexd %0, %H0, [%3]\n"
69 "strexd %1, %4, %H4, [%3]"
70 : "=&r" (prev), "=&r" (status), "+m"(*addr)
71 : "r" (addr), "r" (new_value)
72 : "cc");
73 } while (__builtin_expect(status != 0, 0));
74 return prev;
75}
76
Elliott Hughes7c6169d2012-05-02 16:11:48 -070077int64_t QuasiAtomic::Swap64(int64_t new_value, volatile int64_t* addr) {
Elliott Hughes557e0272011-09-29 10:52:22 -070078 return QuasiAtomicSwap64Impl(new_value, addr);
79}
80
Elliott Hughes7c6169d2012-05-02 16:11:48 -070081int64_t QuasiAtomic::Swap64Sync(int64_t new_value, volatile int64_t* addr) {
Elliott Hughes557e0272011-09-29 10:52:22 -070082 ANDROID_MEMBAR_STORE();
83 int64_t old_value = QuasiAtomicSwap64Impl(new_value, addr);
84 ANDROID_MEMBAR_FULL();
85 return old_value;
86}
87
Elliott Hughes7c6169d2012-05-02 16:11:48 -070088int64_t QuasiAtomic::Read64(volatile const int64_t* addr) {
89 int64_t value;
90 __asm__ __volatile__("@ QuasiAtomic::Read64\n"
91 "ldrexd %0, %H0, [%1]"
92 : "=&r" (value)
93 : "r" (addr));
94 return value;
95}
96
97int QuasiAtomic::Cas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) {
Elliott Hughes5ea047b2011-09-13 14:38:18 -070098 int64_t prev;
99 int status;
100 do {
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700101 __asm__ __volatile__("@ QuasiAtomic::Cas64\n"
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700102 "ldrexd %0, %H0, [%3]\n"
103 "mov %1, #0\n"
104 "teq %0, %4\n"
105 "teqeq %H0, %H4\n"
106 "strexdeq %1, %5, %H5, [%3]"
107 : "=&r" (prev), "=&r" (status), "+m"(*addr)
108 : "r" (addr), "Ir" (old_value), "r" (new_value)
109 : "cc");
110 } while (__builtin_expect(status != 0, 0));
111 return prev != old_value;
112}
113
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700114#endif
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700115
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700116// *****************************************************************************
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700117
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700118#if NEED_MAC_QUASI_ATOMICS
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700119
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700120static inline int64_t QuasiAtomicSwap64Impl(int64_t value, volatile int64_t* addr) {
121 int64_t old_value;
122 do {
123 old_value = *addr;
124 } while (QuasiAtomic::Cas64(old_value, value, addr));
Elliott Hughes557e0272011-09-29 10:52:22 -0700125 return old_value;
126}
127
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700128int64_t QuasiAtomic::Swap64(int64_t value, volatile int64_t* addr) {
129 return QuasiAtomicSwap64Impl(value, addr);
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700130}
131
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700132int64_t QuasiAtomic::Swap64Sync(int64_t value, volatile int64_t* addr) {
133 ANDROID_MEMBAR_STORE();
134 int64_t old_value = QuasiAtomicSwap64Impl(value, addr);
135 // TUNING: barriers can be avoided on some architectures.
136 ANDROID_MEMBAR_FULL();
137 return old_value;
138}
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700139
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700140int64_t QuasiAtomic::Read64(volatile const int64_t* addr) {
141 return OSAtomicAdd64Barrier(0, const_cast<volatile int64_t*>(addr));
142}
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700143
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700144int QuasiAtomic::Cas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) {
145 return OSAtomicCompareAndSwap64Barrier(old_value, new_value, const_cast<int64_t*>(addr)) == 0;
146}
147
148#endif
149
150// *****************************************************************************
151
152#if NEED_PTHREADS_QUASI_ATOMICS
153
154// In the absence of a better implementation, we implement the 64-bit atomic
155// operations through mutex locking.
156
157// We stripe across a bunch of different mutexes to reduce contention.
158static const size_t kSwapLockCount = 32;
159static std::vector<Mutex*>* gSwapLocks;
160
161void QuasiAtomic::Startup() {
162 gSwapLocks = new std::vector<Mutex*>;
163 for (size_t i = 0; i < kSwapLockCount; ++i) {
164 gSwapLocks->push_back(new Mutex(StringPrintf("QuasiAtomic stripe %d", i).c_str()));
165 }
166}
167
168void QuasiAtomic::Shutdown() {
169 STLDeleteElements(gSwapLocks);
170 delete gSwapLocks;
171}
172
173static inline Mutex& GetSwapLock(const volatile int64_t* addr) {
174 return *(*gSwapLocks)[((unsigned)(void*)(addr) >> 3U) % kSwapLockCount];
175}
176
177int64_t QuasiAtomic::Swap64(int64_t value, volatile int64_t* addr) {
178 MutexLock mu(GetSwapLock(addr));
179 int64_t old_value = *addr;
180 *addr = value;
181 return old_value;
182}
183
184int64_t QuasiAtomic::Swap64Sync(int64_t value, volatile int64_t* addr) {
185 // Same as QuasiAtomicSwap64 - mutex handles barrier.
186 return QuasiAtomic::Swap64(value, addr);
187}
188
189int QuasiAtomic::Cas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) {
190 MutexLock mu(GetSwapLock(addr));
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700191 if (*addr == old_value) {
192 *addr = new_value;
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700193 return 0;
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700194 }
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700195 return 1;
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700196}
197
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700198int64_t QuasiAtomic::Read64(volatile const int64_t* addr) {
199 MutexLock mu(GetSwapLock(addr));
200 return *addr;
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700201}
202
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700203#else
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700204
205// The other implementations don't need any special setup.
206void QuasiAtomic::Startup() {}
207void QuasiAtomic::Shutdown() {}
208
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700209#endif
210
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700211} // namespace art