blob: c373579ee279bba889471bacd36863e1544736fe [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
19#include <sched.h>
20
21namespace art {
22
23/*
24 * Quasi-atomic 64-bit operations, for platforms that lack the real thing.
25 *
26 * TODO: unify ARMv6/x86/sh implementations using the to-be-written
27 * spin lock implementation. We don't want to rely on mutex innards,
28 * and it would be great if all platforms were running the same code.
29 */
30
31#if defined(HAVE_MACOSX_IPC)
32
33#include <libkern/OSAtomic.h>
34
35#if defined(__ppc__) \
36 || defined(__PPC__) \
37 || defined(__powerpc__) \
38 || defined(__powerpc) \
39 || defined(__POWERPC__) \
40 || defined(_M_PPC) \
41 || defined(__PPC)
42#define NEED_QUASIATOMICS 1
43#else
44
45int QuasiAtomicCas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) {
46 return OSAtomicCompareAndSwap64Barrier(old_value, new_value, (int64_t*)addr) == 0;
47}
48
Elliott Hughes557e0272011-09-29 10:52:22 -070049static inline int64_t QuasiAtomicSwap64Impl(int64_t value, volatile int64_t* addr) {
50 int64_t old_value;
Elliott Hughes5ea047b2011-09-13 14:38:18 -070051 do {
Elliott Hughes557e0272011-09-29 10:52:22 -070052 old_value = *addr;
53 } while (QuasiAtomicCas64(old_value, value, addr));
54 return old_value;
55}
56
57int64_t QuasiAtomicSwap64(int64_t value, volatile int64_t* addr) {
58 return QuasiAtomicSwap64Impl(value, addr);
59}
60
61int64_t QuasiAtomicSwap64Sync(int64_t value, volatile int64_t* addr) {
62 ANDROID_MEMBAR_STORE();
63 int64_t old_value = QuasiAtomicSwap64Impl(value, addr);
64 /* TUNING: barriers can be avoided on some architectures */
65 ANDROID_MEMBAR_FULL();
66 return old_value;
Elliott Hughes5ea047b2011-09-13 14:38:18 -070067}
68
69int64_t QuasiAtomicRead64(volatile const int64_t* addr) {
70 return OSAtomicAdd64Barrier(0, addr);
71}
72#endif
73
74#elif defined(__i386__) || defined(__x86_64__)
75#define NEED_QUASIATOMICS 1
76
77#elif __arm__
78#include <machine/cpu-features.h>
79
80#ifdef __ARM_HAVE_LDREXD
Elliott Hughes557e0272011-09-29 10:52:22 -070081static inline int64_t QuasiAtomicSwap64Impl(int64_t new_value, volatile int64_t* addr) {
Elliott Hughes5ea047b2011-09-13 14:38:18 -070082 int64_t prev;
83 int status;
84 do {
85 __asm__ __volatile__ ("@ QuasiAtomicSwap64\n"
86 "ldrexd %0, %H0, [%3]\n"
87 "strexd %1, %4, %H4, [%3]"
88 : "=&r" (prev), "=&r" (status), "+m"(*addr)
89 : "r" (addr), "r" (new_value)
90 : "cc");
91 } while (__builtin_expect(status != 0, 0));
92 return prev;
93}
94
Elliott Hughes557e0272011-09-29 10:52:22 -070095int64_t QuasiAtomicSwap64(int64_t new_value, volatile int64_t* addr) {
96 return QuasiAtomicSwap64Impl(new_value, addr);
97}
98
99int64_t QuasiAtomicSwap64Sync(int64_t new_value, volatile int64_t* addr) {
100 ANDROID_MEMBAR_STORE();
101 int64_t old_value = QuasiAtomicSwap64Impl(new_value, addr);
102 ANDROID_MEMBAR_FULL();
103 return old_value;
104}
105
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700106int QuasiAtomicCas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) {
107 int64_t prev;
108 int status;
109 do {
110 __asm__ __volatile__ ("@ QuasiAtomicCas64\n"
111 "ldrexd %0, %H0, [%3]\n"
112 "mov %1, #0\n"
113 "teq %0, %4\n"
114 "teqeq %H0, %H4\n"
115 "strexdeq %1, %5, %H5, [%3]"
116 : "=&r" (prev), "=&r" (status), "+m"(*addr)
117 : "r" (addr), "Ir" (old_value), "r" (new_value)
118 : "cc");
119 } while (__builtin_expect(status != 0, 0));
120 return prev != old_value;
121}
122
123int64_t QuasiAtomicRead64(volatile const int64_t* addr) {
124 int64_t value;
125 __asm__ __volatile__ ("@ QuasiAtomicRead64\n"
126 "ldrexd %0, %H0, [%1]"
127 : "=&r" (value)
128 : "r" (addr));
129 return value;
130}
131
132#else
133
134// on the device, we implement the 64-bit atomic operations through
135// mutex locking. normally, this is bad because we must initialize
136// a pthread_mutex_t before being able to use it, and this means
137// having to do an initialization check on each function call, and
138// that's where really ugly things begin...
139//
140// BUT, as a special twist, we take advantage of the fact that in our
141// pthread library, a mutex is simply a volatile word whose value is always
142// initialized to 0. In other words, simply declaring a static mutex
143// object initializes it !
144//
145// another twist is that we use a small array of mutexes to dispatch
146// the contention locks from different memory addresses
147//
148
149#include <pthread.h>
150
151#define SWAP_LOCK_COUNT 32U
152static pthread_mutex_t _swap_locks[SWAP_LOCK_COUNT];
153
154#define SWAP_LOCK(addr) &_swap_locks[((unsigned)(void*)(addr) >> 3U) % SWAP_LOCK_COUNT]
155
156int64_t QuasiAtomicSwap64(int64_t value, volatile int64_t* addr) {
157 pthread_mutex_t* lock = SWAP_LOCK(addr);
158
159 pthread_mutex_lock(lock);
160
Elliott Hughes557e0272011-09-29 10:52:22 -0700161 int64_t old_value = *addr;
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700162 *addr = value;
163
164 pthread_mutex_unlock(lock);
Elliott Hughes557e0272011-09-29 10:52:22 -0700165 return old_value;
166}
167
168int64_t QuasiAtomicSwap64Sync(int64_t value, volatile int64_t* addr) {
169 // Same as QuasiAtomicSwap64 - mutex handles barrier.
170 return QuasiAtomicSwap64(value, addr);
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700171}
172
173int QuasiAtomicCas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) {
174 int result;
175 pthread_mutex_t* lock = SWAP_LOCK(addr);
176
177 pthread_mutex_lock(lock);
178
179 if (*addr == old_value) {
180 *addr = new_value;
181 result = 0;
182 } else {
183 result = 1;
184 }
185 pthread_mutex_unlock(lock);
186 return result;
187}
188
189int64_t QuasiAtomicRead64(volatile const int64_t* addr) {
190 int64_t result;
191 pthread_mutex_t* lock = SWAP_LOCK(addr);
192
193 pthread_mutex_lock(lock);
194 result = *addr;
195 pthread_mutex_unlock(lock);
196 return result;
197}
198
199#endif /*__ARM_HAVE_LDREXD*/
200
201/*****************************************************************************/
202#elif __sh__
203#define NEED_QUASIATOMICS 1
204
205#else
206#error "Unsupported atomic operations for this platform"
207#endif
208
209
210#if NEED_QUASIATOMICS
211
212/* Note that a spinlock is *not* a good idea in general
213 * since they can introduce subtle issues. For example,
214 * a real-time thread trying to acquire a spinlock already
215 * acquired by another thread will never yeld, making the
216 * CPU loop endlessly!
217 *
218 * However, this code is only used on the Linux simulator
219 * so it's probably ok for us.
220 *
221 * The alternative is to use a pthread mutex, but
222 * these must be initialized before being used, and
223 * then you have the problem of lazily initializing
224 * a mutex without any other synchronization primitive.
225 *
226 * TODO: these currently use sched_yield(), which is not guaranteed to
227 * do anything at all. We need to use dvmIterativeSleep or a wait /
228 * notify mechanism if the initial attempt fails.
229 */
230
231/* global spinlock for all 64-bit quasiatomic operations */
232static int32_t quasiatomic_spinlock = 0;
233
234int QuasiAtomicCas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) {
235 int result;
236
237 while (android_atomic_acquire_cas(0, 1, &quasiatomic_spinlock)) {
238#ifdef HAVE_WIN32_THREADS
239 Sleep(0);
240#else
241 sched_yield();
242#endif
243 }
244
245 if (*addr == old_value) {
246 *addr = new_value;
247 result = 0;
248 } else {
249 result = 1;
250 }
251
252 android_atomic_release_store(0, &quasiatomic_spinlock);
253
254 return result;
255}
256
257int64_t QuasiAtomicRead64(volatile const int64_t* addr) {
258 int64_t result;
259
260 while (android_atomic_acquire_cas(0, 1, &quasiatomic_spinlock)) {
261#ifdef HAVE_WIN32_THREADS
262 Sleep(0);
263#else
264 sched_yield();
265#endif
266 }
267
268 result = *addr;
269 android_atomic_release_store(0, &quasiatomic_spinlock);
270
271 return result;
272}
273
274int64_t QuasiAtomicSwap64(int64_t value, volatile int64_t* addr) {
275 int64_t result;
276
277 while (android_atomic_acquire_cas(0, 1, &quasiatomic_spinlock)) {
278#ifdef HAVE_WIN32_THREADS
279 Sleep(0);
280#else
281 sched_yield();
282#endif
283 }
284
285 result = *addr;
286 *addr = value;
287 android_atomic_release_store(0, &quasiatomic_spinlock);
288
289 return result;
290}
291
Elliott Hughes557e0272011-09-29 10:52:22 -0700292int64_t QuasiAtomicSwap64Sync(int64_t value, volatile int64_t* addr) {
293 // Same as QuasiAtomicSwap64 - syscall handles barrier.
294 return QuasiAtomicSwap64(value, addr);
295}
296
Elliott Hughes5ea047b2011-09-13 14:38:18 -0700297#endif /*NEED_QUASIATOMICS*/
298
299} // namespace art