Line data Source code
1 : // Copyright 2014 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include <limits.h>
6 :
7 : #include "src/base/atomic-utils.h"
8 : #include "src/base/platform/platform.h"
9 : #include "testing/gtest/include/gtest/gtest.h"
10 :
11 : namespace v8 {
12 : namespace base {
13 : namespace {
14 :
15 : enum TestFlag : base::AtomicWord { kA, kB, kC };
16 :
17 : } // namespace
18 :
19 :
20 15373 : TEST(AtomicValue, Initial) {
21 : AtomicValue<TestFlag> a(kA);
22 2 : EXPECT_EQ(TestFlag::kA, a.Value());
23 1 : }
24 :
25 15373 : TEST(AtomicValue, SetValue) {
26 : AtomicValue<TestFlag> a(kB);
27 : a.SetValue(kC);
28 2 : EXPECT_EQ(TestFlag::kC, a.Value());
29 1 : }
30 :
31 :
32 15373 : TEST(AtomicValue, WithVoidStar) {
33 : AtomicValue<void*> a(nullptr);
34 : AtomicValue<void*> dummy(nullptr);
35 1 : EXPECT_EQ(nullptr, a.Value());
36 : a.SetValue(&a);
37 2 : EXPECT_EQ(&a, a.Value());
38 1 : }
39 :
40 15373 : TEST(AsAtomic8, CompareAndSwap_Sequential) {
41 : uint8_t bytes[8];
42 17 : for (int i = 0; i < 8; i++) {
43 8 : bytes[i] = 0xF0 + i;
44 : }
45 17 : for (int i = 0; i < 8; i++) {
46 24 : EXPECT_EQ(0xF0 + i,
47 0 : AsAtomic8::Release_CompareAndSwap(&bytes[i], i, 0xF7 + i));
48 : }
49 17 : for (int i = 0; i < 8; i++) {
50 24 : EXPECT_EQ(0xF0 + i,
51 0 : AsAtomic8::Release_CompareAndSwap(&bytes[i], 0xF0 + i, 0xF7 + i));
52 : }
53 17 : for (int i = 0; i < 8; i++) {
54 16 : EXPECT_EQ(0xF7 + i, bytes[i]);
55 : }
56 1 : }
57 :
58 : namespace {
59 :
60 32 : class ByteIncrementingThread final : public Thread {
61 : public:
62 : ByteIncrementingThread()
63 : : Thread(Options("ByteIncrementingThread")),
64 : byte_addr_(nullptr),
65 32 : increments_(0) {}
66 :
67 : void Initialize(uint8_t* byte_addr, int increments) {
68 32 : byte_addr_ = byte_addr;
69 32 : increments_ = increments;
70 : }
71 :
72 32 : void Run() override {
73 672 : for (int i = 0; i < increments_; i++) {
74 320 : Increment();
75 : }
76 32 : }
77 :
78 320 : void Increment() {
79 : uint8_t byte;
80 320 : do {
81 320 : byte = AsAtomic8::Relaxed_Load(byte_addr_);
82 320 : } while (AsAtomic8::Release_CompareAndSwap(byte_addr_, byte, byte + 1) !=
83 : byte);
84 320 : }
85 :
86 : private:
87 : uint8_t* byte_addr_;
88 : int increments_;
89 : };
90 :
91 : } // namespace
92 :
93 15373 : TEST(AsAtomic8, CompareAndSwap_Concurrent) {
94 : const int kIncrements = 10;
95 : const int kByteCount = 8;
96 : uint8_t bytes[kByteCount];
97 : const int kThreadsPerByte = 4;
98 : const int kThreadCount = kByteCount * kThreadsPerByte;
99 98 : ByteIncrementingThread threads[kThreadCount];
100 :
101 17 : for (int i = 0; i < kByteCount; i++) {
102 8 : AsAtomic8::Relaxed_Store(&bytes[i], i);
103 72 : for (int j = 0; j < kThreadsPerByte; j++) {
104 32 : threads[i * kThreadsPerByte + j].Initialize(&bytes[i], kIncrements);
105 : }
106 : }
107 65 : for (int i = 0; i < kThreadCount; i++) {
108 32 : threads[i].Start();
109 : }
110 :
111 65 : for (int i = 0; i < kThreadCount; i++) {
112 32 : threads[i].Join();
113 : }
114 :
115 17 : for (int i = 0; i < kByteCount; i++) {
116 24 : EXPECT_EQ(i + kIncrements * kThreadsPerByte,
117 0 : AsAtomic8::Relaxed_Load(&bytes[i]));
118 : }
119 1 : }
120 :
121 15373 : TEST(AsAtomicWord, SetBits_Sequential) {
122 1 : uintptr_t word = 0;
123 : // Fill the word with a repeated 0xF0 pattern.
124 17 : for (unsigned i = 0; i < sizeof(word); i++) {
125 8 : word = (word << 8) | 0xF0;
126 : }
127 : // Check the pattern.
128 17 : for (unsigned i = 0; i < sizeof(word); i++) {
129 16 : EXPECT_EQ(0xF0u, (word >> (i * 8) & 0xFFu));
130 : }
131 : // Set the i-th byte value to i.
132 : uintptr_t mask = 0xFF;
133 17 : for (unsigned i = 0; i < sizeof(word); i++) {
134 8 : uintptr_t byte = static_cast<uintptr_t>(i) << (i * 8);
135 8 : AsAtomicWord::SetBits(&word, byte, mask);
136 8 : mask <<= 8;
137 : }
138 9 : for (unsigned i = 0; i < sizeof(word); i++) {
139 16 : EXPECT_EQ(i, (word >> (i * 8) & 0xFFu));
140 : }
141 1 : }
142 :
143 : namespace {
144 :
145 32 : class BitSettingThread final : public Thread {
146 : public:
147 : BitSettingThread()
148 : : Thread(Options("BitSettingThread")),
149 : word_addr_(nullptr),
150 32 : bit_index_(0) {}
151 :
152 : void Initialize(uintptr_t* word_addr, int bit_index) {
153 32 : word_addr_ = word_addr;
154 32 : bit_index_ = bit_index;
155 : }
156 :
157 32 : void Run() override {
158 : uintptr_t bit = 1;
159 32 : bit = bit << bit_index_;
160 32 : AsAtomicWord::SetBits(word_addr_, bit, bit);
161 32 : }
162 :
163 : private:
164 : uintptr_t* word_addr_;
165 : int bit_index_;
166 : };
167 :
168 : } // namespace.
169 :
170 15373 : TEST(AsAtomicWord, SetBits_Concurrent) {
171 : const int kBitCount = sizeof(uintptr_t) * 8;
172 : const int kThreadCount = kBitCount / 2;
173 98 : BitSettingThread threads[kThreadCount];
174 :
175 : uintptr_t word;
176 : AsAtomicWord::Relaxed_Store(&word, 0);
177 65 : for (int i = 0; i < kThreadCount; i++) {
178 : // Thread i sets bit number i * 2.
179 32 : threads[i].Initialize(&word, i * 2);
180 : }
181 65 : for (int i = 0; i < kThreadCount; i++) {
182 32 : threads[i].Start();
183 : }
184 65 : for (int i = 0; i < kThreadCount; i++) {
185 32 : threads[i].Join();
186 : }
187 : uintptr_t actual_word = AsAtomicWord::Relaxed_Load(&word);
188 129 : for (int i = 0; i < kBitCount; i++) {
189 : // Every second bit must be set.
190 64 : uintptr_t expected = (i % 2 == 0);
191 128 : EXPECT_EQ(expected, actual_word & 1u);
192 64 : actual_word >>= 1;
193 : }
194 1 : }
195 :
196 : } // namespace base
197 9222 : } // namespace v8
|