OSDN Git Service

Fix a bug in dvmQuasiAtomicsShutdown.
[android-x86/dalvik.git] / vm / Atomic.cpp
1 /*
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 "Dalvik.h"
18
19 #include <cutils/atomic.h>
20
21 #if defined(__arm__)
22 #include <machine/cpu-features.h>
23 #endif
24
25 /*****************************************************************************/
26
27 #if defined(HAVE_MACOSX_IPC)
28 #define NEED_MAC_QUASI_ATOMICS 1
29
30 #elif defined(__i386__) || defined(__x86_64__)
31 #define NEED_PTHREADS_QUASI_ATOMICS 1
32
33 #elif defined(__mips__)
34 #define NEED_PTHREADS_QUASI_ATOMICS 1
35
36 #elif defined(__arm__)
37
38 #if defined(__ARM_HAVE_LDREXD)
39 #define NEED_ARM_LDREXD_QUASI_ATOMICS 1
40 #else
41 #define NEED_PTHREADS_QUASI_ATOMICS 1
42 #endif /*__ARM_HAVE_LDREXD*/
43
44 #elif defined(__sh__)
45 #define NEED_PTHREADS_QUASI_ATOMICS 1
46
47 #else
48 #error "Unsupported atomic operations for this platform"
49 #endif
50
51 /*****************************************************************************/
52
53 #if NEED_ARM_LDREXD_QUASI_ATOMICS
54
55 static inline int64_t dvmQuasiAtomicSwap64Body(int64_t newvalue,
56                                                volatile int64_t* addr)
57 {
58     int64_t prev;
59     int status;
60     do {
61         __asm__ __volatile__ ("@ dvmQuasiAtomicSwap64\n"
62             "ldrexd     %0, %H0, [%3]\n"
63             "strexd     %1, %4, %H4, [%3]"
64             : "=&r" (prev), "=&r" (status), "+m"(*addr)
65             : "r" (addr), "r" (newvalue)
66             : "cc");
67     } while (__builtin_expect(status != 0, 0));
68     return prev;
69 }
70
71 int64_t dvmQuasiAtomicSwap64(int64_t newvalue, volatile int64_t* addr)
72 {
73     return dvmQuasiAtomicSwap64Body(newvalue, addr);
74 }
75
76 int64_t dvmQuasiAtomicSwap64Sync(int64_t newvalue, volatile int64_t* addr)
77 {
78     int64_t prev;
79     ANDROID_MEMBAR_STORE();
80     prev = dvmQuasiAtomicSwap64Body(newvalue, addr);
81     ANDROID_MEMBAR_FULL();
82     return prev;
83 }
84
85 int dvmQuasiAtomicCas64(int64_t oldvalue, int64_t newvalue,
86     volatile int64_t* addr)
87 {
88     int64_t prev;
89     int status;
90     do {
91         __asm__ __volatile__ ("@ dvmQuasiAtomicCas64\n"
92             "ldrexd     %0, %H0, [%3]\n"
93             "mov        %1, #0\n"
94             "teq        %0, %4\n"
95             "teqeq      %H0, %H4\n"
96             "strexdeq   %1, %5, %H5, [%3]"
97             : "=&r" (prev), "=&r" (status), "+m"(*addr)
98             : "r" (addr), "Ir" (oldvalue), "r" (newvalue)
99             : "cc");
100     } while (__builtin_expect(status != 0, 0));
101     return prev != oldvalue;
102 }
103
104 int64_t dvmQuasiAtomicRead64(volatile const int64_t* addr)
105 {
106     int64_t value;
107     __asm__ __volatile__ ("@ dvmQuasiAtomicRead64\n"
108         "ldrexd     %0, %H0, [%1]"
109         : "=&r" (value)
110         : "r" (addr));
111     return value;
112 }
113 #endif
114
115 /*****************************************************************************/
116
117 #if NEED_MAC_QUASI_ATOMICS
118
119 #include <libkern/OSAtomic.h>
120
121 int dvmQuasiAtomicCas64(int64_t oldvalue, int64_t newvalue,
122     volatile int64_t* addr)
123 {
124     return OSAtomicCompareAndSwap64Barrier(oldvalue, newvalue,
125             (int64_t*)addr) == 0;
126 }
127
128
129 static inline int64_t dvmQuasiAtomicSwap64Body(int64_t value,
130                                                volatile int64_t* addr)
131 {
132     int64_t oldValue;
133     do {
134         oldValue = *addr;
135     } while (dvmQuasiAtomicCas64(oldValue, value, addr));
136     return oldValue;
137 }
138
139 int64_t dvmQuasiAtomicSwap64(int64_t value, volatile int64_t* addr)
140 {
141     return dvmQuasiAtomicSwap64Body(value, addr);
142 }
143
144 int64_t dvmQuasiAtomicSwap64Sync(int64_t value, volatile int64_t* addr)
145 {
146     int64_t oldValue;
147     ANDROID_MEMBAR_STORE();
148     oldValue = dvmQuasiAtomicSwap64Body(value, addr);
149     /* TUNING: barriers can be avoided on some architectures */
150     ANDROID_MEMBAR_FULL();
151     return oldValue;
152 }
153
154 int64_t dvmQuasiAtomicRead64(volatile const int64_t* addr)
155 {
156     return OSAtomicAdd64Barrier(0, addr);
157 }
158 #endif
159
160 /*****************************************************************************/
161
162 #if NEED_PTHREADS_QUASI_ATOMICS
163
164 // In the absence of a better implementation, we implement the 64-bit atomic
165 // operations through mutex locking.
166
167 // another twist is that we use a small array of mutexes to dispatch
168 // the contention locks from different memory addresses
169
170 #include <pthread.h>
171
172 static const size_t kSwapLockCount = 32;
173 static pthread_mutex_t* gSwapLocks[kSwapLockCount];
174
175 void dvmQuasiAtomicsStartup() {
176     for (size_t i = 0; i < kSwapLockCount; ++i) {
177         pthread_mutex_t* m = new pthread_mutex_t;
178         dvmInitMutex(m);
179         gSwapLocks[i] = m;
180     }
181 }
182
183 void dvmQuasiAtomicsShutdown() {
184     for (size_t i = 0; i < kSwapLockCount; ++i) {
185         pthread_mutex_t* m = gSwapLocks[i];
186         gSwapLocks[i] = NULL;
187         dvmDestroyMutex(m);
188         delete m;
189     }
190 }
191
192 static inline pthread_mutex_t* GetSwapLock(const volatile int64_t* addr) {
193     return gSwapLocks[((unsigned)(void*)(addr) >> 3U) % kSwapLockCount];
194 }
195
196 int64_t dvmQuasiAtomicSwap64(int64_t value, volatile int64_t* addr)
197 {
198     int64_t oldValue;
199     pthread_mutex_t* lock = GetSwapLock(addr);
200
201     pthread_mutex_lock(lock);
202
203     oldValue = *addr;
204     *addr    = value;
205
206     pthread_mutex_unlock(lock);
207     return oldValue;
208 }
209
210 /* Same as dvmQuasiAtomicSwap64 - mutex handles barrier */
211 int64_t dvmQuasiAtomicSwap64Sync(int64_t value, volatile int64_t* addr)
212 {
213     return dvmQuasiAtomicSwap64(value, addr);
214 }
215
216 int dvmQuasiAtomicCas64(int64_t oldvalue, int64_t newvalue,
217     volatile int64_t* addr)
218 {
219     int result;
220     pthread_mutex_t* lock = GetSwapLock(addr);
221
222     pthread_mutex_lock(lock);
223
224     if (*addr == oldvalue) {
225         *addr  = newvalue;
226         result = 0;
227     } else {
228         result = 1;
229     }
230     pthread_mutex_unlock(lock);
231     return result;
232 }
233
234 int64_t dvmQuasiAtomicRead64(volatile const int64_t* addr)
235 {
236     int64_t result;
237     pthread_mutex_t* lock = GetSwapLock(addr);
238
239     pthread_mutex_lock(lock);
240     result = *addr;
241     pthread_mutex_unlock(lock);
242     return result;
243 }
244
245 #else
246
247 // The other implementations don't need any special setup.
248 void dvmQuasiAtomicsStartup() {}
249 void dvmQuasiAtomicsShutdown() {}
250
251 #endif /*NEED_PTHREADS_QUASI_ATOMICS*/