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