8f1bc55817
TBR: mlippautz@chromium.org Change-Id: I1d0c3c84e4483287aa599c7d3a0c0d1c5a4d154a Reviewed-on: https://chromium-review.googlesource.com/612177 Commit-Queue: Ulan Degenbaev <ulan@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Reviewed-by: Michael Lippautz <mlippautz@chromium.org> Cr-Commit-Position: refs/heads/master@{#47379}
287 lines
7.0 KiB
C++
287 lines
7.0 KiB
C++
// Copyright 2014 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.
|
|
|
|
#include <limits.h>
|
|
|
|
#include "src/base/atomic-utils.h"
|
|
#include "src/base/platform/platform.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace v8 {
|
|
namespace base {
|
|
|
|
TEST(AtomicNumber, Constructor) {
|
|
// Test some common types.
|
|
AtomicNumber<int> zero_int;
|
|
AtomicNumber<size_t> zero_size_t;
|
|
AtomicNumber<intptr_t> zero_intptr_t;
|
|
EXPECT_EQ(0, zero_int.Value());
|
|
EXPECT_EQ(0u, zero_size_t.Value());
|
|
EXPECT_EQ(0, zero_intptr_t.Value());
|
|
}
|
|
|
|
|
|
TEST(AtomicNumber, Value) {
|
|
AtomicNumber<int> a(1);
|
|
EXPECT_EQ(1, a.Value());
|
|
AtomicNumber<int> b(-1);
|
|
EXPECT_EQ(-1, b.Value());
|
|
AtomicNumber<size_t> c(1);
|
|
EXPECT_EQ(1u, c.Value());
|
|
AtomicNumber<size_t> d(static_cast<size_t>(-1));
|
|
EXPECT_EQ(std::numeric_limits<size_t>::max(), d.Value());
|
|
}
|
|
|
|
|
|
TEST(AtomicNumber, SetValue) {
|
|
AtomicNumber<int> a(1);
|
|
a.SetValue(-1);
|
|
EXPECT_EQ(-1, a.Value());
|
|
}
|
|
|
|
|
|
TEST(AtomicNumber, Increment) {
|
|
AtomicNumber<int> a(std::numeric_limits<int>::max());
|
|
a.Increment(1);
|
|
EXPECT_EQ(std::numeric_limits<int>::min(), a.Value());
|
|
// Check that potential signed-ness of the underlying storage has no impact
|
|
// on unsigned types.
|
|
AtomicNumber<size_t> b(std::numeric_limits<intptr_t>::max());
|
|
b.Increment(1);
|
|
EXPECT_EQ(static_cast<size_t>(std::numeric_limits<intptr_t>::max()) + 1,
|
|
b.Value());
|
|
// Should work as decrement as well.
|
|
AtomicNumber<size_t> c(1);
|
|
c.Increment(-1);
|
|
EXPECT_EQ(0u, c.Value());
|
|
c.Increment(-1);
|
|
EXPECT_EQ(std::numeric_limits<size_t>::max(), c.Value());
|
|
}
|
|
|
|
TEST(AtomicNumber, Decrement) {
|
|
AtomicNumber<size_t> a(std::numeric_limits<size_t>::max());
|
|
a.Increment(1);
|
|
EXPECT_EQ(0u, a.Value());
|
|
a.Decrement(1);
|
|
EXPECT_EQ(std::numeric_limits<size_t>::max(), a.Value());
|
|
}
|
|
|
|
TEST(AtomicNumber, OperatorAdditionAssignment) {
|
|
AtomicNumber<size_t> a(0u);
|
|
AtomicNumber<size_t> b(std::numeric_limits<size_t>::max());
|
|
a += b.Value();
|
|
EXPECT_EQ(a.Value(), b.Value());
|
|
EXPECT_EQ(b.Value(), std::numeric_limits<size_t>::max());
|
|
}
|
|
|
|
TEST(AtomicNumber, OperatorSubtractionAssignment) {
|
|
AtomicNumber<size_t> a(std::numeric_limits<size_t>::max());
|
|
AtomicNumber<size_t> b(std::numeric_limits<size_t>::max());
|
|
a -= b.Value();
|
|
EXPECT_EQ(a.Value(), 0u);
|
|
EXPECT_EQ(b.Value(), std::numeric_limits<size_t>::max());
|
|
}
|
|
|
|
namespace {
|
|
|
|
enum TestFlag : base::AtomicWord {
|
|
kA,
|
|
kB,
|
|
kC,
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
|
TEST(AtomicValue, Initial) {
|
|
AtomicValue<TestFlag> a(kA);
|
|
EXPECT_EQ(TestFlag::kA, a.Value());
|
|
}
|
|
|
|
|
|
TEST(AtomicValue, TrySetValue) {
|
|
AtomicValue<TestFlag> a(kA);
|
|
EXPECT_FALSE(a.TrySetValue(kB, kC));
|
|
EXPECT_TRUE(a.TrySetValue(kA, kC));
|
|
EXPECT_EQ(TestFlag::kC, a.Value());
|
|
}
|
|
|
|
|
|
TEST(AtomicValue, SetValue) {
|
|
AtomicValue<TestFlag> a(kB);
|
|
a.SetValue(kC);
|
|
EXPECT_EQ(TestFlag::kC, a.Value());
|
|
}
|
|
|
|
|
|
TEST(AtomicValue, WithVoidStar) {
|
|
AtomicValue<void*> a(nullptr);
|
|
AtomicValue<void*> dummy(nullptr);
|
|
EXPECT_EQ(nullptr, a.Value());
|
|
a.SetValue(&a);
|
|
EXPECT_EQ(&a, a.Value());
|
|
EXPECT_FALSE(a.TrySetValue(nullptr, &dummy));
|
|
EXPECT_TRUE(a.TrySetValue(&a, &dummy));
|
|
EXPECT_EQ(&dummy, a.Value());
|
|
}
|
|
|
|
TEST(AsAtomic8, CompareAndSwap_Sequential) {
|
|
uint8_t bytes[8];
|
|
for (int i = 0; i < 8; i++) {
|
|
bytes[i] = 0xF0 + i;
|
|
}
|
|
for (int i = 0; i < 8; i++) {
|
|
EXPECT_EQ(0xF0 + i,
|
|
AsAtomic8::Release_CompareAndSwap(&bytes[i], i, 0xF7 + i));
|
|
}
|
|
for (int i = 0; i < 8; i++) {
|
|
EXPECT_EQ(0xF0 + i,
|
|
AsAtomic8::Release_CompareAndSwap(&bytes[i], 0xF0 + i, 0xF7 + i));
|
|
}
|
|
for (int i = 0; i < 8; i++) {
|
|
EXPECT_EQ(0xF7 + i, bytes[i]);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
class ByteIncrementingThread final : public Thread {
|
|
public:
|
|
ByteIncrementingThread()
|
|
: Thread(Options("ByteIncrementingThread")),
|
|
byte_addr_(nullptr),
|
|
increments_(0) {}
|
|
|
|
void Initialize(uint8_t* byte_addr, int increments) {
|
|
byte_addr_ = byte_addr;
|
|
increments_ = increments;
|
|
}
|
|
|
|
void Run() override {
|
|
for (int i = 0; i < increments_; i++) {
|
|
Increment();
|
|
}
|
|
}
|
|
|
|
void Increment() {
|
|
uint8_t byte;
|
|
do {
|
|
byte = AsAtomic8::Relaxed_Load(byte_addr_);
|
|
} while (AsAtomic8::Release_CompareAndSwap(byte_addr_, byte, byte + 1) !=
|
|
byte);
|
|
}
|
|
|
|
private:
|
|
uint8_t* byte_addr_;
|
|
int increments_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
TEST(AsAtomic8, CompareAndSwap_Concurrent) {
|
|
const int kIncrements = 10;
|
|
const int kByteCount = 8;
|
|
uint8_t bytes[kByteCount];
|
|
const int kThreadsPerByte = 4;
|
|
const int kThreadCount = kByteCount * kThreadsPerByte;
|
|
ByteIncrementingThread threads[kThreadCount];
|
|
|
|
for (int i = 0; i < kByteCount; i++) {
|
|
AsAtomic8::Relaxed_Store(&bytes[i], i);
|
|
for (int j = 0; j < kThreadsPerByte; j++) {
|
|
threads[i * kThreadsPerByte + j].Initialize(&bytes[i], kIncrements);
|
|
}
|
|
}
|
|
for (int i = 0; i < kThreadCount; i++) {
|
|
threads[i].Start();
|
|
}
|
|
|
|
for (int i = 0; i < kThreadCount; i++) {
|
|
threads[i].Join();
|
|
}
|
|
|
|
for (int i = 0; i < kByteCount; i++) {
|
|
EXPECT_EQ(i + kIncrements * kThreadsPerByte,
|
|
AsAtomic8::Relaxed_Load(&bytes[i]));
|
|
}
|
|
}
|
|
|
|
TEST(AsAtomicWord, SetBits_Sequential) {
|
|
uintptr_t word = 0;
|
|
// Fill the word with a repeated 0xF0 pattern.
|
|
for (unsigned i = 0; i < sizeof(word); i++) {
|
|
word = (word << 8) | 0xF0;
|
|
}
|
|
// Check the pattern.
|
|
for (unsigned i = 0; i < sizeof(word); i++) {
|
|
EXPECT_EQ(0xF0u, (word >> (i * 8) & 0xFFu));
|
|
}
|
|
// Set the i-th byte value to i.
|
|
uintptr_t mask = 0xFF;
|
|
for (unsigned i = 0; i < sizeof(word); i++) {
|
|
uintptr_t byte = static_cast<uintptr_t>(i) << (i * 8);
|
|
AsAtomicWord::SetBits(&word, byte, mask);
|
|
mask <<= 8;
|
|
}
|
|
for (unsigned i = 0; i < sizeof(word); i++) {
|
|
EXPECT_EQ(i, (word >> (i * 8) & 0xFFu));
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
class BitSettingThread final : public Thread {
|
|
public:
|
|
BitSettingThread()
|
|
: Thread(Options("BitSettingThread")),
|
|
word_addr_(nullptr),
|
|
bit_index_(0) {}
|
|
|
|
void Initialize(uintptr_t* word_addr, int bit_index) {
|
|
word_addr_ = word_addr;
|
|
bit_index_ = bit_index;
|
|
}
|
|
|
|
void Run() override {
|
|
uintptr_t bit = 1;
|
|
bit = bit << bit_index_;
|
|
AsAtomicWord::SetBits(word_addr_, bit, bit);
|
|
}
|
|
|
|
private:
|
|
uintptr_t* word_addr_;
|
|
int bit_index_;
|
|
};
|
|
|
|
} // namespace.
|
|
|
|
TEST(AsAtomicWord, SetBits_Concurrent) {
|
|
const int kBitCount = sizeof(uintptr_t) * 8;
|
|
const int kThreadCount = kBitCount / 2;
|
|
BitSettingThread threads[kThreadCount];
|
|
|
|
uintptr_t word;
|
|
AsAtomicWord::Relaxed_Store(&word, 0);
|
|
for (int i = 0; i < kThreadCount; i++) {
|
|
// Thread i sets bit number i * 2.
|
|
threads[i].Initialize(&word, i * 2);
|
|
}
|
|
for (int i = 0; i < kThreadCount; i++) {
|
|
threads[i].Start();
|
|
}
|
|
for (int i = 0; i < kThreadCount; i++) {
|
|
threads[i].Join();
|
|
}
|
|
uintptr_t actual_word = AsAtomicWord::Relaxed_Load(&word);
|
|
for (int i = 0; i < kBitCount; i++) {
|
|
// Every second bit must be set.
|
|
uintptr_t expected = (i % 2 == 0);
|
|
EXPECT_EQ(expected, actual_word & 1u);
|
|
actual_word >>= 1;
|
|
}
|
|
}
|
|
|
|
} // namespace base
|
|
} // namespace v8
|