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