1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <array>
#include "base/memory/raw_ptr.h"
#include "base/threading/platform_thread.h"
#include "components/viz/service/debugger/rwlock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// Shared global variables
int writer_count = 0;
static const unsigned kNumCounts = 1000000;
class RWLockTest : public testing::Test {
private:
public:
static rwlock::RWLock test_rwlock;
};
rwlock::RWLock RWLockTest::test_rwlock;
// The writer thread reads through the entire vector at a certain
// state left by the readers, sums up all the existing integer
// values, stores it, and resets the vector to provide a blank
// slate for the reader threads to continue working on.
class WriterThread : public base::PlatformThread::Delegate {
private:
raw_ptr<std::vector<int>> array_;
int size_;
volatile int delay_counter = 0;
static const unsigned kNumWriterTries = 100;
static const unsigned kNumTimeDelay = 500000;
public:
WriterThread() = default;
void ThreadMain() override {
for (uint32_t writer_try = 0; writer_try < kNumWriterTries; ++writer_try) {
RWLockTest::test_rwlock.WriteLock();
for (int i = 0; i < size_; ++i) {
writer_count += (*array_)[i];
(*array_)[i] = 0;
}
// Reset the shared vector.
(*array_).clear();
(*array_).resize(size_);
RWLockTest::test_rwlock.WriteUnLock();
// Time delay so that the writer thread does not
// "burn out" too fast.
for (uint32_t _ = 0; _ < kNumTimeDelay; ++_) {
delay_counter = delay_counter + 1;
}
}
}
void Init(std::vector<int>* array, int size) {
array_ = array;
size_ = size;
}
};
// Each reader thread has a corresponding slot within the shared
// vector. Within each slot, each reader thread increments the slot's
// integer value by 1 for k number (kNumCounts) of iterations.
// Within each iteration, there is a small time buffer/delay (kNumTimeDelay)
// to allow the writer thread to squeeze and take the lock.
class ReaderThread : public base::PlatformThread::Delegate {
private:
static const unsigned kNumTimeDelay = 100;
volatile int delay_counter = 0;
raw_ptr<std::vector<int>> array_;
int array_index;
public:
ReaderThread() = default;
ReaderThread(const ReaderThread&) = delete;
ReaderThread& operator=(const ReaderThread&) = delete;
void ThreadMain() override {
for (uint32_t count = 0; count < kNumCounts; ++count) {
RWLockTest::test_rwlock.ReadLock();
++(*array_)[array_index];
RWLockTest::test_rwlock.ReadUnlock();
// Time buffer/delay element for writer to squeeze in
for (uint32_t _ = 0; _ < kNumTimeDelay; ++_) {
delay_counter = delay_counter + 1;
}
}
}
void Init(std::vector<int>* array, int index) {
array_ = array;
array_index = index;
}
};
TEST_F(RWLockTest, ReadWrite) {
static const unsigned kNumReaders = 4;
std::vector<int> arr(kNumReaders); // Must outlive `writer` and `readers`.
WriterThread writer;
std::array<ReaderThread, kNumReaders> readers;
std::array<base::PlatformThreadHandle, kNumReaders> handles;
// Initialize and start each reader thread.
for (uint32_t i = 0; i < kNumReaders; ++i) {
readers[i].Init(&arr, i);
}
for (uint32_t i = 0; i < kNumReaders; ++i) {
ASSERT_TRUE(base::PlatformThread::Create(0, &readers[i], &handles[i]));
}
// Initialize and start writer thread.
writer.Init(&arr, kNumReaders);
base::PlatformThreadHandle writer_handle;
ASSERT_TRUE(base::PlatformThread::Create(0, &writer, &writer_handle));
// Collect all reader threads.
for (auto& handle : handles) {
base::PlatformThread::Join(handle);
}
// Collect writer thread.
base::PlatformThread::Join(writer_handle);
int total_sum = 0;
// Add number counted by the remainder of reader threads.
for (uint32_t i = 0; i < kNumReaders; ++i) {
total_sum += arr[i];
}
// Add number counted by writer thread.
total_sum += writer_count;
EXPECT_EQ(static_cast<uint32_t>(total_sum), kNumReaders * kNumCounts);
}
} // namespace
|