blob: b923f91ebbdf308f46914d74d0421232e94944c5 [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>
20
21#include "mutex.h"
22#include "stl_util.h"
23#include "stringprintf.h"
24
25#if defined(__APPLE__)
26#include <libkern/OSAtomic.h>
27#endif
28#if defined(__arm__)
29#include <machine/cpu-features.h>
30#endif
Elliott Hughes5ea047b2011-09-13 14:38:18 -070031
32namespace art {
33
Elliott Hughes5ea047b2011-09-13 14:38:18 -070034#if defined(HAVE_MACOSX_IPC)
Elliott Hughes7c6169d2012-05-02 16:11:48 -070035#define NEED_MAC_QUASI_ATOMICS 1
Elliott Hughes5ea047b2011-09-13 14:38:18 -070036
37#elif defined(__i386__) || defined(__x86_64__)
Elliott Hughes7c6169d2012-05-02 16:11:48 -070038#define NEED_PTHREADS_QUASI_ATOMICS 1
Elliott Hughes5ea047b2011-09-13 14:38:18 -070039
Elliott Hughes7c6169d2012-05-02 16:11:48 -070040#elif defined(__mips__)
41#define NEED_PTHREADS_QUASI_ATOMICS 1
Elliott Hughes5ea047b2011-09-13 14:38:18 -070042
Elliott Hughes7c6169d2012-05-02 16:11:48 -070043#elif defined(__arm__)
44
45#if defined(__ARM_HAVE_LDREXD)
46#define NEED_ARM_LDREXD_QUASI_ATOMICS 1
47#else
48#define NEED_PTHREADS_QUASI_ATOMICS 1
49#endif
50
51#elif defined(__sh__)
52#define NEED_PTHREADS_QUASI_ATOMICS 1
53
54#else
55#error "QuasiAtomic unsupported on this platform"
56#endif
57
58// *****************************************************************************
59
60#if NEED_ARM_LDREXD_QUASI_ATOMICS
61
Elliott Hughes557e0272011-09-29 10:52:22 -070062static inline int64_t QuasiAtomicSwap64Impl(int64_t new_value, volatile int64_t* addr) {
Elliott Hughes5ea047b2011-09-13 14:38:18 -070063 int64_t prev;
64 int status;
65 do {
Elliott Hughes7c6169d2012-05-02 16:11:48 -070066 __asm__ __volatile__("@ QuasiAtomic::Swap64\n"
Elliott Hughes5ea047b2011-09-13 14:38:18 -070067 "ldrexd %0, %H0, [%3]\n"
68 "strexd %1, %4, %H4, [%3]"
69 : "=&r" (prev), "=&r" (status), "+m"(*addr)
70 : "r" (addr), "r" (new_value)
71 : "cc");
72 } while (__builtin_expect(status != 0, 0));
73 return prev;
74}
75
Elliott Hughes7c6169d2012-05-02 16:11:48 -070076int64_t QuasiAtomic::Swap64(int64_t new_value, volatile int64_t* addr) {
Elliott Hughes557e0272011-09-29 10:52:22 -070077 return QuasiAtomicSwap64Impl(new_value, addr);
78}
79
Elliott Hughes7c6169d2012-05-02 16:11:48 -070080int64_t QuasiAtomic::Swap64Sync(int64_t new_value, volatile int64_t* addr) {
Elliott Hughes557e0272011-09-29 10:52:22 -070081 ANDROID_MEMBAR_STORE();
82 int64_t old_value = QuasiAtomicSwap64Impl(new_value, addr);
83 ANDROID_MEMBAR_FULL();
84 return old_value;
85}
86
Elliott Hughes7c6169d2012-05-02 16:11:48 -070087int64_t QuasiAtomic::Read64(volatile const int64_t* addr) {
88 int64_t value;
89 __asm__ __volatile__("@ QuasiAtomic::Read64\n"
90 "ldrexd %0, %H0, [%1]"
91 : "=&r" (value)
92 : "r" (addr));
93 return value;
94}
95
96int QuasiAtomic::Cas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) {
Elliott Hughes5ea047b2011-09-13 14:38:18 -070097 int64_t prev;
98 int status;
99 do {
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700100 __asm__ __volatile__("@ QuasiAtomic::Cas64\n"
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700101 "ldrexd %0, %H0, [%3]\n"
102 "mov %1, #0\n"
103 "teq %0, %4\n"
104 "teqeq %H0, %H4\n"
105 "strexdeq %1, %5, %H5, [%3]"
106 : "=&r" (prev), "=&r" (status), "+m"(*addr)
107 : "r" (addr), "Ir" (old_value), "r" (new_value)
108 : "cc");
109 } while (__builtin_expect(status != 0, 0));
110 return prev != old_value;
111}
112
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700113#endif
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700114
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700115// *****************************************************************************
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700116
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700117#if NEED_MAC_QUASI_ATOMICS
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700118
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700119static inline int64_t QuasiAtomicSwap64Impl(int64_t value, volatile int64_t* addr) {
120 int64_t old_value;
121 do {
122 old_value = *addr;
123 } while (QuasiAtomic::Cas64(old_value, value, addr));
Elliott Hughes557e0272011-09-29 10:52:22 -0700124 return old_value;
125}
126
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700127int64_t QuasiAtomic::Swap64(int64_t value, volatile int64_t* addr) {
128 return QuasiAtomicSwap64Impl(value, addr);
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700129}
130
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700131int64_t QuasiAtomic::Swap64Sync(int64_t value, volatile int64_t* addr) {
132 ANDROID_MEMBAR_STORE();
133 int64_t old_value = QuasiAtomicSwap64Impl(value, addr);
134 // TUNING: barriers can be avoided on some architectures.
135 ANDROID_MEMBAR_FULL();
136 return old_value;
137}
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700138
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700139int64_t QuasiAtomic::Read64(volatile const int64_t* addr) {
140 return OSAtomicAdd64Barrier(0, const_cast<volatile int64_t*>(addr));
141}
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700142
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700143int QuasiAtomic::Cas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) {
144 return OSAtomicCompareAndSwap64Barrier(old_value, new_value, const_cast<int64_t*>(addr)) == 0;
145}
146
147#endif
148
149// *****************************************************************************
150
151#if NEED_PTHREADS_QUASI_ATOMICS
152
153// In the absence of a better implementation, we implement the 64-bit atomic
154// operations through mutex locking.
155
156// We stripe across a bunch of different mutexes to reduce contention.
157static const size_t kSwapLockCount = 32;
158static std::vector<Mutex*>* gSwapLocks;
159
160void QuasiAtomic::Startup() {
161 gSwapLocks = new std::vector<Mutex*>;
162 for (size_t i = 0; i < kSwapLockCount; ++i) {
163 gSwapLocks->push_back(new Mutex(StringPrintf("QuasiAtomic stripe %d", i).c_str()));
164 }
165}
166
167void QuasiAtomic::Shutdown() {
168 STLDeleteElements(gSwapLocks);
169 delete gSwapLocks;
170}
171
172static inline Mutex& GetSwapLock(const volatile int64_t* addr) {
173 return *(*gSwapLocks)[((unsigned)(void*)(addr) >> 3U) % kSwapLockCount];
174}
175
176int64_t QuasiAtomic::Swap64(int64_t value, volatile int64_t* addr) {
177 MutexLock mu(GetSwapLock(addr));
178 int64_t old_value = *addr;
179 *addr = value;
180 return old_value;
181}
182
183int64_t QuasiAtomic::Swap64Sync(int64_t value, volatile int64_t* addr) {
184 // Same as QuasiAtomicSwap64 - mutex handles barrier.
185 return QuasiAtomic::Swap64(value, addr);
186}
187
188int QuasiAtomic::Cas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) {
189 MutexLock mu(GetSwapLock(addr));
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700190 if (*addr == old_value) {
191 *addr = new_value;
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700192 return 0;
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700193 }
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700194 return 1;
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700195}
196
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700197int64_t QuasiAtomic::Read64(volatile const int64_t* addr) {
198 MutexLock mu(GetSwapLock(addr));
199 return *addr;
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700200}
201
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700202#else
Elliott Hughes7c6169d2012-05-02 16:11:48 -0700203
204// The other implementations don't need any special setup.
205void QuasiAtomic::Startup() {}
206void QuasiAtomic::Shutdown() {}
207
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700208#endif
209
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700210} // namespace art