diff options
Diffstat (limited to 'runtime/atomic.cc')
-rw-r--r-- | runtime/atomic.cc | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/runtime/atomic.cc b/runtime/atomic.cc new file mode 100644 index 0000000000..f2a998289c --- /dev/null +++ b/runtime/atomic.cc @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "atomic.h" + +#define NEED_SWAP_MUTEXES !defined(__arm__) && !defined(__i386__) + +#if NEED_SWAP_MUTEXES +#include <vector> +#include "base/mutex.h" +#include "base/stl_util.h" +#include "base/stringprintf.h" +#include "thread.h" +#endif + +namespace art { + +#if NEED_SWAP_MUTEXES +// We stripe across a bunch of different mutexes to reduce contention. +static const size_t kSwapMutexCount = 32; +static std::vector<Mutex*>* gSwapMutexes; + +static Mutex& GetSwapMutex(const volatile int64_t* addr) { + return *(*gSwapMutexes)[((unsigned)(void*)(addr) >> 3U) % kSwapMutexCount]; +} +#endif + +void QuasiAtomic::Startup() { +#if NEED_SWAP_MUTEXES + gSwapMutexes = new std::vector<Mutex*>; + for (size_t i = 0; i < kSwapMutexCount; ++i) { + gSwapMutexes->push_back(new Mutex("QuasiAtomic stripe")); + } +#endif +} + +void QuasiAtomic::Shutdown() { +#if NEED_SWAP_MUTEXES + STLDeleteElements(gSwapMutexes); + delete gSwapMutexes; +#endif +} + +int64_t QuasiAtomic::Read64(volatile const int64_t* addr) { + int64_t value; +#if NEED_SWAP_MUTEXES + MutexLock mu(Thread::Current(), GetSwapMutex(addr)); + value = *addr; +#elif defined(__arm__) + // Exclusive loads are defined not to tear, clearing the exclusive state isn't necessary. If we + // have LPAE (such as Cortex-A15) then ldrd would suffice. + __asm__ __volatile__("@ QuasiAtomic::Read64\n" + "ldrexd %0, %H0, [%1]" + : "=&r" (value) + : "r" (addr)); +#elif defined(__i386__) + __asm__ __volatile__( + "movq %1, %0\n" + : "=x" (value) + : "m" (*addr)); +#else +#error Unexpected architecture +#endif + return value; +} + +void QuasiAtomic::Write64(volatile int64_t* addr, int64_t value) { +#if NEED_SWAP_MUTEXES + MutexLock mu(Thread::Current(), GetSwapMutex(addr)); + *addr = value; +#elif defined(__arm__) + // The write is done as a swap so that the cache-line is in the exclusive state for the store. If + // we know that ARM architecture has LPAE (such as Cortex-A15) this isn't necessary and strd will + // suffice. + int64_t prev; + int status; + do { + __asm__ __volatile__("@ QuasiAtomic::Write64\n" + "ldrexd %0, %H0, [%3]\n" + "strexd %1, %4, %H4, [%3]" + : "=&r" (prev), "=&r" (status), "+m"(*addr) + : "r" (addr), "r" (value) + : "cc"); + } while (__builtin_expect(status != 0, 0)); +#elif defined(__i386__) + __asm__ __volatile__( + "movq %1, %0" + : "=m" (*addr) + : "x" (value)); +#else +#error Unexpected architecture +#endif +} + + +bool QuasiAtomic::Cas64(int64_t old_value, int64_t new_value, volatile int64_t* addr) { +#if NEED_SWAP_MUTEXES + MutexLock mu(Thread::Current(), GetSwapMutex(addr)); + if (*addr == old_value) { + *addr = new_value; + return true; + } + return false; +#elif defined(__arm__) + int64_t prev; + int status; + do { + __asm__ __volatile__("@ QuasiAtomic::Cas64\n" + "ldrexd %0, %H0, [%3]\n" + "mov %1, #0\n" + "teq %0, %4\n" + "teqeq %H0, %H4\n" + "strexdeq %1, %5, %H5, [%3]" + : "=&r" (prev), "=&r" (status), "+m"(*addr) + : "r" (addr), "Ir" (old_value), "r" (new_value) + : "cc"); + } while (__builtin_expect(status != 0, 0)); + return prev == old_value; +#elif defined(__i386__) + // The compiler does the right job and works better than inline assembly, especially with -O0 + // compilation. + return __sync_bool_compare_and_swap(addr, old_value, new_value); +#else +#error Unexpected architecture +#endif +} + +bool QuasiAtomic::LongAtomicsUseMutexes() { +#if NEED_SWAP_MUTEXES + return true; +#else + return false; +#endif +} + +} // namespace art |