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