// Copyright 2015 the V8 project authors. All rights reserved.
|
// Use of this source code is governed by a BSD-style license that can be
|
// found in the LICENSE file.
|
|
#ifndef V8_BASE_ATOMIC_UTILS_H_
|
#define V8_BASE_ATOMIC_UTILS_H_
|
|
#include <limits.h>
|
#include <type_traits>
|
|
#include "src/base/atomicops.h"
|
#include "src/base/macros.h"
|
|
namespace v8 {
|
namespace base {
|
|
// Deprecated. Use std::atomic<T> for new code.
|
// Flag using T atomically. Also accepts void* as T.
|
template <typename T>
|
class AtomicValue {
|
public:
|
AtomicValue() : value_(0) {}
|
|
explicit AtomicValue(T initial)
|
: value_(cast_helper<T>::to_storage_type(initial)) {}
|
|
V8_INLINE T Value() const {
|
return cast_helper<T>::to_return_type(base::Acquire_Load(&value_));
|
}
|
|
V8_INLINE bool TrySetValue(T old_value, T new_value) {
|
return base::Release_CompareAndSwap(
|
&value_, cast_helper<T>::to_storage_type(old_value),
|
cast_helper<T>::to_storage_type(new_value)) ==
|
cast_helper<T>::to_storage_type(old_value);
|
}
|
|
V8_INLINE void SetBits(T bits, T mask) {
|
DCHECK_EQ(bits & ~mask, static_cast<T>(0));
|
T old_value;
|
T new_value;
|
do {
|
old_value = Value();
|
new_value = (old_value & ~mask) | bits;
|
} while (!TrySetValue(old_value, new_value));
|
}
|
|
V8_INLINE void SetBit(int bit) {
|
SetBits(static_cast<T>(1) << bit, static_cast<T>(1) << bit);
|
}
|
|
V8_INLINE void ClearBit(int bit) { SetBits(0, 1 << bit); }
|
|
V8_INLINE void SetValue(T new_value) {
|
base::Release_Store(&value_, cast_helper<T>::to_storage_type(new_value));
|
}
|
|
private:
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
|
|
template <typename S>
|
struct cast_helper {
|
static base::AtomicWord to_storage_type(S value) {
|
return static_cast<base::AtomicWord>(value);
|
}
|
static S to_return_type(base::AtomicWord value) {
|
return static_cast<S>(value);
|
}
|
};
|
|
template <typename S>
|
struct cast_helper<S*> {
|
static base::AtomicWord to_storage_type(S* value) {
|
return reinterpret_cast<base::AtomicWord>(value);
|
}
|
static S* to_return_type(base::AtomicWord value) {
|
return reinterpret_cast<S*>(value);
|
}
|
};
|
|
base::AtomicWord value_;
|
};
|
|
class AsAtomic32 {
|
public:
|
template <typename T>
|
static T Acquire_Load(T* addr) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic32));
|
return to_return_type<T>(base::Acquire_Load(to_storage_addr(addr)));
|
}
|
|
template <typename T>
|
static T Relaxed_Load(T* addr) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic32));
|
return to_return_type<T>(base::Relaxed_Load(to_storage_addr(addr)));
|
}
|
|
template <typename T>
|
static void Release_Store(T* addr,
|
typename std::remove_reference<T>::type new_value) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic32));
|
base::Release_Store(to_storage_addr(addr), to_storage_type(new_value));
|
}
|
|
template <typename T>
|
static void Relaxed_Store(T* addr,
|
typename std::remove_reference<T>::type new_value) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic32));
|
base::Relaxed_Store(to_storage_addr(addr), to_storage_type(new_value));
|
}
|
|
template <typename T>
|
static T Release_CompareAndSwap(
|
T* addr, typename std::remove_reference<T>::type old_value,
|
typename std::remove_reference<T>::type new_value) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic32));
|
return to_return_type<T>(base::Release_CompareAndSwap(
|
to_storage_addr(addr), to_storage_type(old_value),
|
to_storage_type(new_value)));
|
}
|
|
// Atomically sets bits selected by the mask to the given value.
|
// Returns false if the bits are already set as needed.
|
template <typename T>
|
static bool SetBits(T* addr, T bits, T mask) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic32));
|
DCHECK_EQ(bits & ~mask, static_cast<T>(0));
|
T old_value;
|
T new_value;
|
do {
|
old_value = Relaxed_Load(addr);
|
if ((old_value & mask) == bits) return false;
|
new_value = (old_value & ~mask) | bits;
|
} while (Release_CompareAndSwap(addr, old_value, new_value) != old_value);
|
return true;
|
}
|
|
private:
|
template <typename T>
|
static base::Atomic32 to_storage_type(T value) {
|
return static_cast<base::Atomic32>(value);
|
}
|
template <typename T>
|
static T to_return_type(base::Atomic32 value) {
|
return static_cast<T>(value);
|
}
|
template <typename T>
|
static base::Atomic32* to_storage_addr(T* value) {
|
return reinterpret_cast<base::Atomic32*>(value);
|
}
|
template <typename T>
|
static const base::Atomic32* to_storage_addr(const T* value) {
|
return reinterpret_cast<const base::Atomic32*>(value);
|
}
|
};
|
|
class AsAtomicWord {
|
public:
|
template <typename T>
|
static T Acquire_Load(T* addr) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
|
return to_return_type<T>(base::Acquire_Load(to_storage_addr(addr)));
|
}
|
|
template <typename T>
|
static T Relaxed_Load(T* addr) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
|
return to_return_type<T>(base::Relaxed_Load(to_storage_addr(addr)));
|
}
|
|
template <typename T>
|
static void Release_Store(T* addr,
|
typename std::remove_reference<T>::type new_value) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
|
base::Release_Store(to_storage_addr(addr), to_storage_type(new_value));
|
}
|
|
template <typename T>
|
static void Relaxed_Store(T* addr,
|
typename std::remove_reference<T>::type new_value) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
|
base::Relaxed_Store(to_storage_addr(addr), to_storage_type(new_value));
|
}
|
|
template <typename T>
|
static T Release_CompareAndSwap(
|
T* addr, typename std::remove_reference<T>::type old_value,
|
typename std::remove_reference<T>::type new_value) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
|
return to_return_type<T>(base::Release_CompareAndSwap(
|
to_storage_addr(addr), to_storage_type(old_value),
|
to_storage_type(new_value)));
|
}
|
|
// Atomically sets bits selected by the mask to the given value.
|
// Returns false if the bits are already set as needed.
|
template <typename T>
|
static bool SetBits(T* addr, T bits, T mask) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
|
DCHECK_EQ(bits & ~mask, static_cast<T>(0));
|
T old_value;
|
T new_value;
|
do {
|
old_value = Relaxed_Load(addr);
|
if ((old_value & mask) == bits) return false;
|
new_value = (old_value & ~mask) | bits;
|
} while (Release_CompareAndSwap(addr, old_value, new_value) != old_value);
|
return true;
|
}
|
|
private:
|
template <typename T>
|
static base::AtomicWord to_storage_type(T value) {
|
return static_cast<base::AtomicWord>(value);
|
}
|
template <typename T>
|
static T to_return_type(base::AtomicWord value) {
|
return static_cast<T>(value);
|
}
|
template <typename T>
|
static base::AtomicWord* to_storage_addr(T* value) {
|
return reinterpret_cast<base::AtomicWord*>(value);
|
}
|
template <typename T>
|
static const base::AtomicWord* to_storage_addr(const T* value) {
|
return reinterpret_cast<const base::AtomicWord*>(value);
|
}
|
};
|
|
class AsAtomic8 {
|
public:
|
template <typename T>
|
static T Acquire_Load(T* addr) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic8));
|
return to_return_type<T>(base::Acquire_Load(to_storage_addr(addr)));
|
}
|
|
template <typename T>
|
static T Relaxed_Load(T* addr) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic8));
|
return to_return_type<T>(base::Relaxed_Load(to_storage_addr(addr)));
|
}
|
|
template <typename T>
|
static void Release_Store(T* addr,
|
typename std::remove_reference<T>::type new_value) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic8));
|
base::Release_Store(to_storage_addr(addr), to_storage_type(new_value));
|
}
|
|
template <typename T>
|
static void Relaxed_Store(T* addr,
|
typename std::remove_reference<T>::type new_value) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic8));
|
base::Relaxed_Store(to_storage_addr(addr), to_storage_type(new_value));
|
}
|
|
template <typename T>
|
static T Release_CompareAndSwap(
|
T* addr, typename std::remove_reference<T>::type old_value,
|
typename std::remove_reference<T>::type new_value) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::Atomic8));
|
return to_return_type<T>(base::Release_CompareAndSwap(
|
to_storage_addr(addr), to_storage_type(old_value),
|
to_storage_type(new_value)));
|
}
|
|
private:
|
template <typename T>
|
static base::Atomic8 to_storage_type(T value) {
|
return static_cast<base::Atomic8>(value);
|
}
|
template <typename T>
|
static T to_return_type(base::Atomic8 value) {
|
return static_cast<T>(value);
|
}
|
template <typename T>
|
static base::Atomic8* to_storage_addr(T* value) {
|
return reinterpret_cast<base::Atomic8*>(value);
|
}
|
template <typename T>
|
static const base::Atomic8* to_storage_addr(const T* value) {
|
return reinterpret_cast<const base::Atomic8*>(value);
|
}
|
};
|
|
class AsAtomicPointer {
|
public:
|
template <typename T>
|
static T Acquire_Load(T* addr) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
|
return to_return_type<T>(base::Acquire_Load(to_storage_addr(addr)));
|
}
|
|
template <typename T>
|
static T Relaxed_Load(T* addr) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
|
return to_return_type<T>(base::Relaxed_Load(to_storage_addr(addr)));
|
}
|
|
template <typename T>
|
static void Release_Store(T* addr,
|
typename std::remove_reference<T>::type new_value) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
|
base::Release_Store(to_storage_addr(addr), to_storage_type(new_value));
|
}
|
|
template <typename T>
|
static void Relaxed_Store(T* addr,
|
typename std::remove_reference<T>::type new_value) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
|
base::Relaxed_Store(to_storage_addr(addr), to_storage_type(new_value));
|
}
|
|
template <typename T>
|
static T Release_CompareAndSwap(
|
T* addr, typename std::remove_reference<T>::type old_value,
|
typename std::remove_reference<T>::type new_value) {
|
STATIC_ASSERT(sizeof(T) <= sizeof(base::AtomicWord));
|
return to_return_type<T>(base::Release_CompareAndSwap(
|
to_storage_addr(addr), to_storage_type(old_value),
|
to_storage_type(new_value)));
|
}
|
|
private:
|
template <typename T>
|
static base::AtomicWord to_storage_type(T value) {
|
return reinterpret_cast<base::AtomicWord>(value);
|
}
|
template <typename T>
|
static T to_return_type(base::AtomicWord value) {
|
return reinterpret_cast<T>(value);
|
}
|
template <typename T>
|
static base::AtomicWord* to_storage_addr(T* value) {
|
return reinterpret_cast<base::AtomicWord*>(value);
|
}
|
template <typename T>
|
static const base::AtomicWord* to_storage_addr(const T* value) {
|
return reinterpret_cast<const base::AtomicWord*>(value);
|
}
|
};
|
|
// This class is intended to be used as a wrapper for elements of an array
|
// that is passed in to STL functions such as std::sort. It ensures that
|
// elements accesses are atomic.
|
// Usage example:
|
// Object** given_array;
|
// AtomicElement<Object*>* wrapped =
|
// reinterpret_cast<AtomicElement<Object*>(given_array);
|
// std::sort(wrapped, wrapped + given_length, cmp);
|
// where the cmp function uses the value() accessor to compare the elements.
|
template <typename T>
|
class AtomicElement {
|
public:
|
AtomicElement(const AtomicElement<T>& other) {
|
AsAtomicPointer::Relaxed_Store(
|
&value_, AsAtomicPointer::Relaxed_Load(&other.value_));
|
}
|
|
void operator=(const AtomicElement<T>& other) {
|
AsAtomicPointer::Relaxed_Store(
|
&value_, AsAtomicPointer::Relaxed_Load(&other.value_));
|
}
|
|
T value() const { return AsAtomicPointer::Relaxed_Load(&value_); }
|
|
bool operator<(const AtomicElement<T>& other) const {
|
return value() < other.value();
|
}
|
|
bool operator==(const AtomicElement<T>& other) const {
|
return value() == other.value();
|
}
|
|
private:
|
T value_;
|
};
|
|
} // namespace base
|
} // namespace v8
|
|
#endif // V8_BASE_ATOMIC_UTILS_H_
|