1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <benchmark/benchmark.h>
17 #include <thread>
18 #include <string>
19 #include "rwlock.h"
20 #include "benchmark_log.h"
21 #include "benchmark_assert.h"
22 using namespace std;
23
24 namespace OHOS {
25 namespace {
26
27 class BenchmarkRWLockTest : public benchmark::Fixture {
28 public:
SetUp(const::benchmark::State & state)29 void SetUp(const ::benchmark::State& state) override
30 {
31 }
32
TearDown(const::benchmark::State & state)33 void TearDown(const ::benchmark::State& state) override
34 {
35 }
36
BenchmarkRWLockTest()37 BenchmarkRWLockTest()
38 {
39 Iterations(iterations);
40 Repetitions(repetitions);
41 ReportAggregatesOnly();
42 }
43
44 ~BenchmarkRWLockTest() override = default;
45
46 protected:
47 const int32_t repetitions = 3;
48 const int32_t iterations = 1000;
49 };
50
51 const int SLEEP_DURATION_MS = 4;
52
53 // This class is designed for test RWLock. "buf_" is protected by "rwLock_".
54 class TestRWLock {
55 public:
TestRWLock()56 TestRWLock():rwLock_(), buf_() {}
57
TestRWLock(bool writeFirst)58 explicit TestRWLock(bool writeFirst):rwLock_(writeFirst), buf_() {}
59
WriteStr(const string & str)60 void WriteStr(const string& str)
61 {
62 BENCHMARK_LOGD("RWLockTest void WriteStr is called.");
63 rwLock_.LockWrite();
64 for (auto it = str.begin(); it != str.end(); it++) {
65 buf_.push_back(*it);
66 this_thread::sleep_for(std::chrono::milliseconds(10)); // 10: Extend time of holding the lock
67 }
68 rwLock_.UnLockWrite();
69 return;
70 }
71
ReadStr(string & str)72 void ReadStr(string& str)
73 {
74 BENCHMARK_LOGD("RWLockTest void ReadStr is called.");
75 rwLock_.LockRead();
76 for (auto it = buf_.begin(); it != buf_.end(); it++) {
77 str.push_back(*it);
78 this_thread::sleep_for(std::chrono::milliseconds(10)); // 10: Extend time of holding the lock
79 }
80 rwLock_.UnLockRead();
81 return;
82 }
83 private:
84 Utils::RWLock rwLock_;
85 string buf_;
86 };
87
88 const string WRITE_IN_1("write1");
89 const string WRITE_IN_2("write2");
90
91 /*
92 * @tc.name: testRWLock001
93 * @tc.desc: RWLock here is under write-first mode. If there are some writing operation waiting,
94 * reading will never happen. Reading operations are likely to run at the same time, when all writing operations
95 * have finished.
96 */
BENCHMARK_F(BenchmarkRWLockTest,testRWLock001)97 BENCHMARK_F(BenchmarkRWLockTest, testRWLock001)(benchmark::State& state)
98 {
99 BENCHMARK_LOGD("RWLockTest testRWLock001 start.");
100 while (state.KeepRunning()) {
101 TestRWLock test;
102
103 thread first(bind(&TestRWLock::WriteStr, ref(test), ref(WRITE_IN_1)));
104 // Try our best to make `first` get the lock
105 this_thread::sleep_for(std::chrono::milliseconds(SLEEP_DURATION_MS));
106
107 string readOut1("");
108 thread second(bind(&TestRWLock::ReadStr, ref(test), ref(readOut1)));
109 thread third(bind(&TestRWLock::WriteStr, ref(test), ref(WRITE_IN_2)));
110 string readOut2("");
111 thread fourth(bind(&TestRWLock::ReadStr, ref(test), ref(readOut2)));
112
113
114 first.join();
115 second.join();
116 third.join();
117 fourth.join();
118
119 AssertEqual(readOut1, WRITE_IN_1 + WRITE_IN_2,
120 "readOut1 did not equal WRITE_IN_1 + WRITE_IN_2 as expected.", state);
121 AssertEqual(readOut2, WRITE_IN_1 + WRITE_IN_2,
122 "readOut2 did not equal WRITE_IN_1 + WRITE_IN_2 as expected.", state);
123 }
124 BENCHMARK_LOGD("RWLockTest testRWLock001 end.");
125 }
126
127 /*
128 * @tc.name: testRWLock002
129 * @tc.desc: RWLock here is not under write-first mode. So if there are writing and reading operations in queue
130 * with a writing mission running, they will compete when the writing mission completing, but reading operations are
131 * likely to run at the same time.
132 */
BENCHMARK_F(BenchmarkRWLockTest,testRWLock002)133 BENCHMARK_F(BenchmarkRWLockTest, testRWLock002)(benchmark::State& state)
134 {
135 BENCHMARK_LOGD("RWLockTest testRWLock002 start.");
136 while (state.KeepRunning()) {
137 TestRWLock test(false);
138
139 thread first(bind(&TestRWLock::WriteStr, ref(test), ref(WRITE_IN_1)));
140 this_thread::sleep_for(chrono::milliseconds(SLEEP_DURATION_MS));
141
142 string readOut1("");
143 thread second(bind(&TestRWLock::ReadStr, ref(test), ref(readOut1)));
144 thread third(bind(&TestRWLock::WriteStr, ref(test), ref(WRITE_IN_2)));
145 string readOut2("");
146 thread fourth(bind(&TestRWLock::ReadStr, ref(test), ref(readOut2)));
147
148 first.join();
149 second.join();
150 third.join();
151 fourth.join();
152
153 AssertEqual(readOut1, readOut2, "readOut1 did not equal readOut2 as expected.", state);
154 }
155 BENCHMARK_LOGD("RWLockTest testRWLock002 end.");
156 }
157
158 /*
159 * @tc.name: testRWLockDefaultConstructor001
160 * @tc.desc: This test case validates the default constructor of RWLock. By default, the RWLock is in write-first mode.
161 * In this mode, if there are pending write operations, read operations will not occur. This test case creates
162 * a default RWLock and attempts to perform read operations while write operations are pending.
163 * The expected behavior is that the read operations should not occur until the write operations are complete.
164 */
BENCHMARK_F(BenchmarkRWLockTest,testRWLockDefaultConstructor001)165 BENCHMARK_F(BenchmarkRWLockTest, testRWLockDefaultConstructor001)(benchmark::State& state)
166 {
167 BENCHMARK_LOGD("RWLockTest testRWLockDefaultConstructor001 start.");
168 while (state.KeepRunning()) {
169 TestRWLock test;
170
171 thread first(bind(&TestRWLock::WriteStr, ref(test), ref(WRITE_IN_1)));
172 this_thread::sleep_for(std::chrono::milliseconds(SLEEP_DURATION_MS));
173
174 string readOut1("");
175 thread second(bind(&TestRWLock::ReadStr, ref(test), ref(readOut1)));
176
177 first.join();
178 second.join();
179
180 AssertEqual(readOut1, WRITE_IN_1, "readOut1 did not equal WRITE_IN_1 as expected.", state);
181 }
182 BENCHMARK_LOGD("RWLockTest testRWLockDefaultConstructor001 end.");
183 }
184
185 /*
186 * @tc.name: testUniqueWriteGuardScope001
187 * @tc.desc: This benchmark test is designed to test the functionality of the UniqueWriteGuard class.
188 * In this test, a write lock is acquired on an instance of the RWLock class using an instance of
189 * the UniqueWriteGuard class. The WriteStr method of the TestRWLock class is then called to write a string to
190 * the buffer of the TestRWLock instance. After the write operation, the write lock is automatically released
191 * because the UniqueWriteGuard instance goes out of scope. The ReadStr method of the TestRWLock class is then called
192 * to read the string from the buffer of the TestRWLock instance. If the read string does not match the written
193 * string, the test fails and an error message is logged. This test case is repeated multiple times to measure
194 * the performance of the write lock operation.
195 */
BENCHMARK_F(BenchmarkRWLockTest,testUniqueWriteGuardScope001)196 BENCHMARK_F(BenchmarkRWLockTest, testUniqueWriteGuardScope001)(benchmark::State& state)
197 {
198 BENCHMARK_LOGD("RWLockTest testUniqueWriteGuardScope001 start.");
199 while (state.KeepRunning()) {
200 OHOS::Utils::RWLock rwLock_;
201 TestRWLock test;
202 string readOut1("");
203 OHOS::Utils::UniqueWriteGuard<OHOS::Utils::RWLock> guard(rwLock_);
204 test.WriteStr(WRITE_IN_1);
205 test.ReadStr(readOut1);
206 AssertEqual(readOut1, WRITE_IN_1, "readOut1 did not equal WRITE_IN_1 as expected.", state);
207 }
208 BENCHMARK_LOGD("RWLockTest testUniqueWriteGuardScope001 end.");
209 }
210
211 /*
212 * @tc.name: testUniqueReadGuardScope001
213 * @tc.desc: This benchmark test is designed to test the functionality of the UniqueReadGuard class.
214 * In this test, a read lock is acquired on an instance of the RWLock class using an instance of
215 * the UniqueReadGuard class. The ReadStr method of the TestRWLock class is then called to read the string from
216 * the buffer of the TestRWLock instance. After the read operation, the read lock is automatically released because
217 * the UniqueReadGuard instance goes out of scope. This test case is repeated multiple times to measure
218 * the performance of the read lock operation.
219 */
BENCHMARK_F(BenchmarkRWLockTest,testUniqueReadGuardScope001)220 BENCHMARK_F(BenchmarkRWLockTest, testUniqueReadGuardScope001)(benchmark::State& state)
221 {
222 BENCHMARK_LOGD("RWLockTest testUniqueReadGuardScope001 start.");
223 while (state.KeepRunning()) {
224 OHOS::Utils::RWLock rwLock_;
225 TestRWLock test;
226 string readOut1("");
227 test.WriteStr(WRITE_IN_1); // Write a string to the buffer before acquiring the read lock.
228 OHOS::Utils::UniqueReadGuard<OHOS::Utils::RWLock> guard(rwLock_);
229 test.ReadStr(readOut1);
230 AssertEqual(readOut1, WRITE_IN_1, "readOut1 did not equal WRITE_IN_1 as expected.", state);
231 }
232 BENCHMARK_LOGD("RWLockTest testUniqueReadGuardScope001 end.");
233 }
234 } // namespace
235 } // namespace OHOS
236 // Run the benchmark
237 BENCHMARK_MAIN();
238