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 :
14 13158 : TEST(AtomicNumber, Constructor) {
15 : // Test some common types.
16 : AtomicNumber<int> zero_int;
17 : AtomicNumber<size_t> zero_size_t;
18 : AtomicNumber<intptr_t> zero_intptr_t;
19 2 : EXPECT_EQ(0, zero_int.Value());
20 2 : EXPECT_EQ(0u, zero_size_t.Value());
21 2 : EXPECT_EQ(0, zero_intptr_t.Value());
22 1 : }
23 :
24 :
25 13158 : TEST(AtomicNumber, Value) {
26 : AtomicNumber<int> a(1);
27 2 : EXPECT_EQ(1, a.Value());
28 : AtomicNumber<int> b(-1);
29 2 : EXPECT_EQ(-1, b.Value());
30 : AtomicNumber<size_t> c(1);
31 2 : EXPECT_EQ(1u, c.Value());
32 : AtomicNumber<size_t> d(static_cast<size_t>(-1));
33 2 : EXPECT_EQ(std::numeric_limits<size_t>::max(), d.Value());
34 1 : }
35 :
36 :
37 13158 : TEST(AtomicNumber, SetValue) {
38 : AtomicNumber<int> a(1);
39 : a.SetValue(-1);
40 2 : EXPECT_EQ(-1, a.Value());
41 1 : }
42 :
43 :
44 13158 : TEST(AtomicNumber, Increment) {
45 : AtomicNumber<int> a(std::numeric_limits<int>::max());
46 : a.Increment(1);
47 2 : EXPECT_EQ(std::numeric_limits<int>::min(), a.Value());
48 : // Check that potential signed-ness of the underlying storage has no impact
49 : // on unsigned types.
50 : AtomicNumber<size_t> b(std::numeric_limits<intptr_t>::max());
51 : b.Increment(1);
52 2 : EXPECT_EQ(static_cast<size_t>(std::numeric_limits<intptr_t>::max()) + 1,
53 0 : b.Value());
54 : // Should work as decrement as well.
55 : AtomicNumber<size_t> c(1);
56 : c.Increment(-1);
57 2 : EXPECT_EQ(0u, c.Value());
58 : c.Increment(-1);
59 2 : EXPECT_EQ(std::numeric_limits<size_t>::max(), c.Value());
60 1 : }
61 :
62 13158 : TEST(AtomicNumber, Decrement) {
63 : AtomicNumber<size_t> a(std::numeric_limits<size_t>::max());
64 : a.Increment(1);
65 2 : EXPECT_EQ(0u, a.Value());
66 : a.Decrement(1);
67 2 : EXPECT_EQ(std::numeric_limits<size_t>::max(), a.Value());
68 1 : }
69 :
70 13158 : TEST(AtomicNumber, OperatorAdditionAssignment) {
71 : AtomicNumber<size_t> a(0u);
72 : AtomicNumber<size_t> b(std::numeric_limits<size_t>::max());
73 : a += b.Value();
74 3 : EXPECT_EQ(a.Value(), b.Value());
75 3 : EXPECT_EQ(b.Value(), std::numeric_limits<size_t>::max());
76 1 : }
77 :
78 13158 : TEST(AtomicNumber, OperatorSubtractionAssignment) {
79 : AtomicNumber<size_t> a(std::numeric_limits<size_t>::max());
80 : AtomicNumber<size_t> b(std::numeric_limits<size_t>::max());
81 : a -= b.Value();
82 3 : EXPECT_EQ(a.Value(), 0u);
83 3 : EXPECT_EQ(b.Value(), std::numeric_limits<size_t>::max());
84 1 : }
85 :
86 : namespace {
87 :
88 : enum TestFlag : base::AtomicWord {
89 : kA,
90 : kB,
91 : kC,
92 : };
93 :
94 : } // namespace
95 :
96 :
97 13158 : TEST(AtomicValue, Initial) {
98 : AtomicValue<TestFlag> a(kA);
99 2 : EXPECT_EQ(TestFlag::kA, a.Value());
100 1 : }
101 :
102 :
103 13158 : TEST(AtomicValue, TrySetValue) {
104 : AtomicValue<TestFlag> a(kA);
105 2 : EXPECT_FALSE(a.TrySetValue(kB, kC));
106 1 : EXPECT_TRUE(a.TrySetValue(kA, kC));
107 2 : EXPECT_EQ(TestFlag::kC, a.Value());
108 1 : }
109 :
110 :
111 13158 : TEST(AtomicValue, SetValue) {
112 : AtomicValue<TestFlag> a(kB);
113 : a.SetValue(kC);
114 2 : EXPECT_EQ(TestFlag::kC, a.Value());
115 1 : }
116 :
117 :
118 13158 : TEST(AtomicValue, WithVoidStar) {
119 : AtomicValue<void*> a(nullptr);
120 : AtomicValue<void*> dummy(nullptr);
121 1 : EXPECT_EQ(nullptr, a.Value());
122 : a.SetValue(&a);
123 2 : EXPECT_EQ(&a, a.Value());
124 2 : EXPECT_FALSE(a.TrySetValue(nullptr, &dummy));
125 1 : EXPECT_TRUE(a.TrySetValue(&a, &dummy));
126 2 : EXPECT_EQ(&dummy, a.Value());
127 1 : }
128 :
129 13158 : TEST(AsAtomic8, CompareAndSwap_Sequential) {
130 : uint8_t bytes[8];
131 9 : for (int i = 0; i < 8; i++) {
132 8 : bytes[i] = 0xF0 + i;
133 : }
134 8 : for (int i = 0; i < 8; i++) {
135 24 : EXPECT_EQ(0xF0 + i,
136 0 : AsAtomic8::Release_CompareAndSwap(&bytes[i], i, 0xF7 + i));
137 : }
138 8 : for (int i = 0; i < 8; i++) {
139 24 : EXPECT_EQ(0xF0 + i,
140 0 : AsAtomic8::Release_CompareAndSwap(&bytes[i], 0xF0 + i, 0xF7 + i));
141 : }
142 8 : for (int i = 0; i < 8; i++) {
143 16 : EXPECT_EQ(0xF7 + i, bytes[i]);
144 : }
145 1 : }
146 :
147 : namespace {
148 :
149 32 : class ByteIncrementingThread final : public Thread {
150 : public:
151 : ByteIncrementingThread()
152 : : Thread(Options("ByteIncrementingThread")),
153 : byte_addr_(nullptr),
154 32 : increments_(0) {}
155 :
156 : void Initialize(uint8_t* byte_addr, int increments) {
157 32 : byte_addr_ = byte_addr;
158 32 : increments_ = increments;
159 : }
160 :
161 32 : void Run() override {
162 352 : for (int i = 0; i < increments_; i++) {
163 320 : Increment();
164 : }
165 32 : }
166 :
167 320 : void Increment() {
168 : uint8_t byte;
169 320 : do {
170 320 : byte = AsAtomic8::Relaxed_Load(byte_addr_);
171 320 : } while (AsAtomic8::Release_CompareAndSwap(byte_addr_, byte, byte + 1) !=
172 : byte);
173 320 : }
174 :
175 : private:
176 : uint8_t* byte_addr_;
177 : int increments_;
178 : };
179 :
180 : } // namespace
181 :
182 13158 : TEST(AsAtomic8, CompareAndSwap_Concurrent) {
183 : const int kIncrements = 10;
184 : const int kByteCount = 8;
185 : uint8_t bytes[kByteCount];
186 : const int kThreadsPerByte = 4;
187 : const int kThreadCount = kByteCount * kThreadsPerByte;
188 33 : ByteIncrementingThread threads[kThreadCount];
189 :
190 8 : for (int i = 0; i < kByteCount; i++) {
191 8 : AsAtomic8::Relaxed_Store(&bytes[i], i);
192 40 : for (int j = 0; j < kThreadsPerByte; j++) {
193 32 : threads[i * kThreadsPerByte + j].Initialize(&bytes[i], kIncrements);
194 : }
195 : }
196 32 : for (int i = 0; i < kThreadCount; i++) {
197 32 : threads[i].Start();
198 : }
199 :
200 32 : for (int i = 0; i < kThreadCount; i++) {
201 32 : threads[i].Join();
202 : }
203 :
204 8 : for (int i = 0; i < kByteCount; i++) {
205 24 : EXPECT_EQ(i + kIncrements * kThreadsPerByte,
206 0 : AsAtomic8::Relaxed_Load(&bytes[i]));
207 33 : }
208 1 : }
209 :
210 13158 : TEST(AsAtomicWord, SetBits_Sequential) {
211 1 : uintptr_t word = 0;
212 : // Fill the word with a repeated 0xF0 pattern.
213 9 : for (unsigned i = 0; i < sizeof(word); i++) {
214 8 : word = (word << 8) | 0xF0;
215 : }
216 : // Check the pattern.
217 8 : for (unsigned i = 0; i < sizeof(word); i++) {
218 16 : EXPECT_EQ(0xF0u, (word >> (i * 8) & 0xFFu));
219 : }
220 : // Set the i-th byte value to i.
221 : uintptr_t mask = 0xFF;
222 8 : for (unsigned i = 0; i < sizeof(word); i++) {
223 8 : uintptr_t byte = static_cast<uintptr_t>(i) << (i * 8);
224 8 : AsAtomicWord::SetBits(&word, byte, mask);
225 8 : mask <<= 8;
226 : }
227 9 : for (unsigned i = 0; i < sizeof(word); i++) {
228 16 : EXPECT_EQ(i, (word >> (i * 8) & 0xFFu));
229 : }
230 1 : }
231 :
232 : namespace {
233 :
234 32 : class BitSettingThread final : public Thread {
235 : public:
236 : BitSettingThread()
237 : : Thread(Options("BitSettingThread")),
238 : word_addr_(nullptr),
239 32 : bit_index_(0) {}
240 :
241 : void Initialize(uintptr_t* word_addr, int bit_index) {
242 32 : word_addr_ = word_addr;
243 32 : bit_index_ = bit_index;
244 : }
245 :
246 32 : void Run() override {
247 : uintptr_t bit = 1;
248 32 : bit = bit << bit_index_;
249 32 : AsAtomicWord::SetBits(word_addr_, bit, bit);
250 32 : }
251 :
252 : private:
253 : uintptr_t* word_addr_;
254 : int bit_index_;
255 : };
256 :
257 : } // namespace.
258 :
259 13158 : TEST(AsAtomicWord, SetBits_Concurrent) {
260 : const int kBitCount = sizeof(uintptr_t) * 8;
261 : const int kThreadCount = kBitCount / 2;
262 33 : BitSettingThread threads[kThreadCount];
263 :
264 : uintptr_t word;
265 : AsAtomicWord::Relaxed_Store(&word, 0);
266 33 : for (int i = 0; i < kThreadCount; i++) {
267 : // Thread i sets bit number i * 2.
268 32 : threads[i].Initialize(&word, i * 2);
269 : }
270 32 : for (int i = 0; i < kThreadCount; i++) {
271 32 : threads[i].Start();
272 : }
273 32 : for (int i = 0; i < kThreadCount; i++) {
274 32 : threads[i].Join();
275 : }
276 : uintptr_t actual_word = AsAtomicWord::Relaxed_Load(&word);
277 65 : for (int i = 0; i < kBitCount; i++) {
278 : // Every second bit must be set.
279 64 : uintptr_t expected = (i % 2 == 0);
280 128 : EXPECT_EQ(expected, actual_word & 1u);
281 64 : actual_word >>= 1;
282 33 : }
283 1 : }
284 :
285 : } // namespace base
286 7893 : } // namespace v8
|