/*
* 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)[(reinterpret_cast<unsigned>(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