blob: 7ef0e948d7a2ff3cf6b8c1d7d3614d0bd4ae028f [file] [log] [blame]
// 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 {
namespace {
enum TestFlag : base::AtomicWord { kA, kB, kC };
} // namespace
TEST(AtomicValue, Initial) {
AtomicValue<TestFlag> a(kA);
EXPECT_EQ(TestFlag::kA, 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());
}
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++) {
CHECK(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++) {
CHECK(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