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_ex.h"
18 #include <iostream>
19 #include <cstdio>
20 #include <sys/prctl.h>
21 #include <unistd.h>
22 #include <sys/resource.h>
23 #include "benchmark_log.h"
24 #include "benchmark_assert.h"
25 using namespace std;
26 
27 namespace OHOS {
28 namespace {
29 
30 class BenchmarkThreadTest : public benchmark::Fixture {
31 public:
SetUp(const::benchmark::State & state)32     void SetUp(const ::benchmark::State& state) override
33     {
34     }
35 
TearDown(const::benchmark::State & state)36     void TearDown(const ::benchmark::State& state) override
37     {
38     }
39 
BenchmarkThreadTest()40     BenchmarkThreadTest()
41     {
42         Iterations(iterations);
43         Repetitions(repetitions);
44         ReportAggregatesOnly();
45     }
46 
47     ~BenchmarkThreadTest() override = default;
48 
49 protected:
50     const int32_t repetitions = 3;
51     const int32_t iterations = 100;
52 };
53 
54 static int g_times = 0;
55 constexpr int DEFAULT_PRIO = 0;
56 const std::string& DEFAULT_THREAD_NAME = "default";
57 const int INITIAL_TIMES = 0;
58 const int INITIAL_TEST_VALUE = 0;
59 const int THREAD_STACK_SIZE = 1024;
60 const int INVALID_THREAD_ID = -1;
61 const int SLEEP_FOR_ONE_SECOND = 1;
62 
63 using ThreadRunFunc = bool (*)(int& data);
64 
TestRun01(int & data)65 bool TestRun01(int& data)
66 {
67     BENCHMARK_LOGD("ThreadTest bool TestRun01 is called.");
68     ++data;
69     return false;
70 }
71 
TestRun02(int & data)72 bool TestRun02(int& data)
73 {
74     BENCHMARK_LOGD("ThreadTest bool TestRun02 is called.");
75     ++data;
76     return true;
77 }
78 
TestRun03(int & data)79 bool TestRun03(int& data)
80 {
81     BENCHMARK_LOGD("ThreadTest bool TestRun03 is called.");
82     static const int TRY_TIMES = 10;
83     if (g_times <= TRY_TIMES) {
84         ++data;
85         return true;
86     }
87 
88     return false;
89 }
90 
91 class TestThread : public OHOS::Thread {
92 public:
TestThread(const int data,const bool readyToWork,ThreadRunFunc runFunc)93     TestThread(const int data, const bool readyToWork, ThreadRunFunc runFunc)
94         : data_(data),
95         priority_(DEFAULT_PRIO),
96         name_(DEFAULT_THREAD_NAME),
97         readyToWork_(readyToWork),
98         runFunc_(runFunc)
99         {};
100 
101     TestThread() = delete;
~TestThread()102     ~TestThread() {}
103 
104     bool ReadyToWork() override;
105 
106     int data_;
107     int priority_;
108     std::string name_;
109 
110 protected:
111     bool Run() override;
112 
113 private:
114     bool readyToWork_;
115     ThreadRunFunc runFunc_;
116 };
117 
ReadyToWork()118 bool TestThread::ReadyToWork()
119 {
120     BENCHMARK_LOGD("ThreadTest bool TestThread::ReadyToWork is called.");
121     return readyToWork_;
122 }
123 
Run()124 bool TestThread::Run()
125 {
126     BENCHMARK_LOGD("ThreadTest bool TestThread::Run is called.");
127     priority_ = getpriority(PRIO_PROCESS, 0);
128     char threadName[MAX_THREAD_NAME_LEN + 1] = {0};
129     prctl(PR_GET_NAME, threadName, 0, 0);
130     name_ = threadName;
131 
132     if (runFunc_ != nullptr) {
133         return (*runFunc_)(data_);
134     }
135 
136     return false;
137 }
138 
139 /*
140  * @tc.name: testThread001
141  * @tc.desc: ThreadTest
142  */
BENCHMARK_F(BenchmarkThreadTest,testThread001)143 BENCHMARK_F(BenchmarkThreadTest, testThread001)(benchmark::State& state)
144 {
145     BENCHMARK_LOGD("ThreadTest testThread001 start.");
146     const int expectedDataValue = 0;
147     while (state.KeepRunning()) {
148         g_times = 0;
149         std::unique_ptr<TestThread> test = std::make_unique<TestThread>(INITIAL_TEST_VALUE, false, TestRun01);
150         ThreadStatus status = test->Start("test_thread_01", THREAD_PROI_LOW, THREAD_STACK_SIZE);
151         AssertEqual((status == ThreadStatus::OK), true,
152             "(status == ThreadStatus::OK) did not equal true as expected.", state);
153 
154         pthread_t thread = test->GetThread();
155 
156         // pthread_equal return non-zero if equal
157         AssertEqual((pthread_equal(thread, INVALID_THREAD_ID) != 0), (test->IsRunning() ? false : true),
158             "(pthread_equal(thread, INVALID_THREAD_ID) != 0) did not equal \
159             (test->IsRunning() ? false : true) as expected.", state);
160 
161         // ReadyToWork return false, RUN will not be called!
162         AssertEqual(test->priority_, DEFAULT_PRIO, "test->priority_ did not equal DEFAULT_PRIO as expected.", state);
163         AssertEqual(test->name_, DEFAULT_THREAD_NAME,
164             "test->name_ did not equal DEFAULT_THREAD_NAME as expected.", state);
165 
166         // get stacksize of threa, may be different because of system memory align
167         AssertEqual(test->data_, expectedDataValue,
168             "test->data_ did not equal EXPECTED_DATA_VALUE as expected.", state);
169         AssertEqual(g_times, INITIAL_TIMES, "g_times did not equal INITIAL_TIMES as expected.", state);
170         test->NotifyExitSync();
171         sleep(SLEEP_FOR_ONE_SECOND);
172         AssertEqual((pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0), (test->IsRunning() ? false : true),
173             "(pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0) did not equal \
174             (test->IsRunning() ? false : true) as expected.", state);
175     }
176     BENCHMARK_LOGD("ThreadTest testThread001 end.");
177 }
178 
179 /*
180  * @tc.name: testThread002
181  * @tc.desc: ThreadTest
182  */
BENCHMARK_F(BenchmarkThreadTest,testThread002)183 BENCHMARK_F(BenchmarkThreadTest, testThread002)(benchmark::State& state)
184 {
185     BENCHMARK_LOGD("ThreadTest testThread002 start.");
186     const int expectedDataValue = 1;
187     while (state.KeepRunning()) {
188         g_times = 0;
189         std::unique_ptr<TestThread> test = std::make_unique<TestThread>(INITIAL_TEST_VALUE, true, TestRun01);
190         ThreadStatus status = test->Start("test_thread_02", THREAD_PROI_LOW, THREAD_STACK_SIZE);
191 
192         AssertEqual((status == ThreadStatus::OK), true,
193             "status == ThreadStatus::OK did not equal true as expected.", state);
194 
195         sleep(SLEEP_FOR_ONE_SECOND); // let the new thread has chance to run
196 
197         // pthread_equal return non-zero if equal, RUN return false,may exit already
198         AssertEqual((pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0), (test->IsRunning() ? false : true),
199             "(pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0) did not equal \
200             (test->IsRunning() ? false : true) as expected.", state);
201 
202         // ReadyToWork return true, RUN will be called!
203         AssertEqual(test->priority_, THREAD_PROI_LOW,
204             "test->priority_ did not equal THREAD_PROI_LOW as expected.", state);
205         AssertEqual(test->name_, "test_thread_02",
206             "test->name_ did not equal \"test_thread_02\" as expected.", state);
207         AssertEqual(test->data_, expectedDataValue, "test->data_ did not equal expectedDataValue as expected.", state);
208         AssertEqual(g_times, INITIAL_TIMES, "g_times did not equal INITIAL_TIMES as expected.", state);
209 
210         test->NotifyExitSync();
211         sleep(SLEEP_FOR_ONE_SECOND);
212         AssertEqual((pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0), (test->IsRunning() ? false : true),
213             "pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0 did not equal \
214             (test->IsRunning() ? false : true) as expected.", state);
215     }
216     BENCHMARK_LOGD("ThreadTest testThread002 end.");
217 }
218 
ThreadTestProcedure(std::unique_ptr<TestThread> & test,const int expectedDataValue,benchmark::State & state)219 static void ThreadTestProcedure(std::unique_ptr<TestThread>& test, const int expectedDataValue, benchmark::State& state)
220 {
221     pthread_t thread = test->GetThread();
222 
223     // pthread_equal return non-zero if equal
224     AssertEqual((pthread_equal(thread, INVALID_THREAD_ID) != 0), (test->IsRunning() ? false : true),
225         "pthread_equal(thread, INVALID_THREAD_ID) != 0 did not equal \
226         (test->IsRunning() ? false : true) as expected.", state);
227 
228     // ReadyToWork return false, RUN will not be called!
229     AssertEqual(test->priority_, DEFAULT_PRIO,
230         "test->priority_ did not equal DEFAULT_PRIO as expected.", state);
231     AssertEqual(test->name_, DEFAULT_THREAD_NAME,
232         "test->name_ did not equal DEFAULT_THREAD_NAME as expected.", state);
233     AssertEqual(test->data_, expectedDataValue, "test->data_ did not equal expectedDataValue as expected.", state);
234     AssertEqual(g_times, INITIAL_TIMES, "g_times did not equal INITIAL_TIMES as expected.", state);
235 
236     test->NotifyExitSync();
237     sleep(SLEEP_FOR_ONE_SECOND);
238     AssertEqual((pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0), (test->IsRunning() ? false : true),
239         "pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0 did not equal \
240         (test->IsRunning() ? false : true) as expected.", state);
241 }
242 
243 /*
244  * @tc.name: testThread003
245  * @tc.desc: ThreadTest
246  */
BENCHMARK_F(BenchmarkThreadTest,testThread003)247 BENCHMARK_F(BenchmarkThreadTest, testThread003)(benchmark::State& state)
248 {
249     BENCHMARK_LOGD("ThreadTest testThread003 start.");
250     const int expectedDataValue = 0;
251     while (state.KeepRunning()) {
252         g_times = 0;
253         std::unique_ptr<TestThread> test = std::make_unique<TestThread>(INITIAL_TEST_VALUE, false, TestRun02);
254         ThreadStatus status = test->Start("test_thread_03", THREAD_PROI_LOW, THREAD_STACK_SIZE);
255         AssertEqual((status == ThreadStatus::OK), true,
256             "status == ThreadStatus::OK did not equal true as expected.", state);
257 
258         ThreadTestProcedure(test, expectedDataValue, state);
259     }
260     BENCHMARK_LOGD("ThreadTest testThread003 end.");
261 }
262 
263 /*
264  * @tc.name: testThread004
265  * @tc.desc: ThreadTest
266  */
BENCHMARK_F(BenchmarkThreadTest,testThread004)267 BENCHMARK_F(BenchmarkThreadTest, testThread004)(benchmark::State& state)
268 {
269     BENCHMARK_LOGD("ThreadTest testThread004 start.");
270     const int comparisonValue = 1;
271     while (state.KeepRunning()) {
272         g_times = 0;
273         std::unique_ptr<TestThread> test = std::make_unique<TestThread>(INITIAL_TEST_VALUE, true, TestRun02);
274         ThreadStatus status = test->Start("test_thread_04", THREAD_PROI_LOW, THREAD_STACK_SIZE);
275 
276         AssertEqual((status == ThreadStatus::OK), true,
277             "status == ThreadStatus::OK did not equal true as expected.", state);
278 
279         sleep(SLEEP_FOR_ONE_SECOND); // let the new thread has chance to run
280 
281         // pthread_equal return non-zero if equal, RUN return false,may exit already
282         AssertEqual((pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0), (test->IsRunning() ? false : true),
283             "pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0 did not equal \
284             (test->IsRunning() ? false : true) as expected.", state);
285         // ReadyToWork return true, RUN will be called!
286         AssertEqual(test->priority_, THREAD_PROI_LOW,
287             "test->priority_ did not equal THREAD_PROI_LOW as expected.", state);
288         AssertEqual(test->name_, "test_thread_04",
289             "test->name_ did not equal \"test_thread_04\" as expected.", state);
290         AssertGreaterThan(test->data_, comparisonValue,
291             "test->data_ was not greater than comparisonValue as expected.", state);
292         AssertEqual(g_times, INITIAL_TIMES, "g_times did not equal INITIAL_TIMES as expected.", state);
293 
294         test->NotifyExitSync();
295         sleep(SLEEP_FOR_ONE_SECOND);
296         AssertEqual((pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0), (test->IsRunning() ? false : true),
297             "pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0 did not equal \
298             (test->IsRunning() ? false : true) as expected.", state);
299     }
300     BENCHMARK_LOGD("ThreadTest testThread004 end.");
301 }
302 
303 /*
304  * @tc.name: testThread005
305  * @tc.desc: ThreadTest
306  */
BENCHMARK_F(BenchmarkThreadTest,testThread005)307 BENCHMARK_F(BenchmarkThreadTest, testThread005)(benchmark::State& state)
308 {
309     BENCHMARK_LOGD("ThreadTest testThread005 start.");
310     const int expectedDataValue = 0;
311     while (state.KeepRunning()) {
312         g_times = 0;
313         std::unique_ptr<TestThread> test = std::make_unique<TestThread>(INITIAL_TEST_VALUE, false, TestRun03);
314         ThreadStatus status = test->Start("test_thread_05", THREAD_PROI_LOW, THREAD_STACK_SIZE);
315         AssertEqual((status == ThreadStatus::OK), true,
316             "status == ThreadStatus::OK did not equal true as expected.", state);
317 
318         ThreadTestProcedure(test, expectedDataValue, state);
319     }
320     BENCHMARK_LOGD("ThreadTest testThread005 end.");
321 }
322 
323 /*
324  * @tc.name: testThread006
325  * @tc.desc: ThreadTest
326  */
BENCHMARK_F(BenchmarkThreadTest,testThread006)327 BENCHMARK_F(BenchmarkThreadTest, testThread006)(benchmark::State& state)
328 {
329     BENCHMARK_LOGD("ThreadTest testThread006 start.");
330     const int comparisonValue = 10;
331     while (state.KeepRunning()) {
332         g_times = 0;
333         std::unique_ptr<TestThread> test = std::make_unique<TestThread>(INITIAL_TEST_VALUE, true, TestRun03);
334         ThreadStatus status = test->Start("test_thread_06", THREAD_PROI_LOW, THREAD_STACK_SIZE);
335         AssertEqual((status == ThreadStatus::OK), true,
336             "status == ThreadStatus::OK did not equal true as expected.", state);
337 
338         sleep(SLEEP_FOR_ONE_SECOND); // let the new thread has chance to run
339 
340         // pthread_equal return non-zero if equal, RUN return false,may exit already
341         AssertEqual((pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0), (test->IsRunning() ? false : true),
342             "pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0 did not equal \
343             (test->IsRunning() ? false : true) as expected.", state);
344         // ReadyToWork return true, RUN will be called!
345         AssertEqual(test->priority_, THREAD_PROI_LOW,
346             "test->priority_ did not equal THREAD_PROI_LOW as expected.", state);
347         AssertEqual(test->name_, "test_thread_06",
348             "test->name_ did not equal \"test_thread_06\" as expected.", state);
349         AssertGreaterThan(test->data_, comparisonValue,
350             "test->data_ was not greater than comparisonValue as expected.", state);
351         AssertEqual(g_times, INITIAL_TIMES, "g_times did not equal INITIAL_TIMES as expected.", state);
352 
353         g_times = 100;
354         AssertGreaterThan(test->data_, comparisonValue,
355             "test->data_ was not greater than comparisonValue as expected.", state);
356 
357         sleep(SLEEP_FOR_ONE_SECOND); // let the new thread has chance to run
358 
359         // g_times > 10, TestRun03 return false, thread exit
360         AssertEqual((pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0), (test->IsRunning() ? false : true),
361             "pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0 did not equal \
362             (test->IsRunning() ? false : true) as expected.", state);
363     }
364     BENCHMARK_LOGD("ThreadTest testThread006 end.");
365 }
366 
367 /*
368  * @tc.name: testThread007
369  * @tc.desc: ThreadTest
370  */
BENCHMARK_F(BenchmarkThreadTest,testThread007)371 BENCHMARK_F(BenchmarkThreadTest, testThread007)(benchmark::State& state)
372 {
373     BENCHMARK_LOGD("ThreadTest testThread007 start.");
374     while (state.KeepRunning()) {
375         g_times = 0;
376         std::unique_ptr<TestThread> test = std::make_unique<TestThread>(INITIAL_TEST_VALUE, true, TestRun03);
377         ThreadStatus status = test->Start("", THREAD_PROI_LOW, 0);
378         AssertEqual((status == ThreadStatus::OK), true,
379             "status == ThreadStatus::OK did not equal true as expected.", state);
380 
381         if (test->IsRunning()) {
382             status = test->Start("", THREAD_PROI_NORMAL, THREAD_STACK_SIZE);
383             AssertEqual(status == ThreadStatus::INVALID_OPERATION, true,
384                 "status == ThreadStatus::INVALID_OPERATION did not equal true as expected.", state);
385 
386             test->NotifyExitSync();
387             AssertEqual((pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0), (test->IsRunning() ? false : true),
388                 "pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0 did not equal \
389                 (test->IsRunning() ? false : true) as expected.", state);
390         }
391 
392         sleep(SLEEP_FOR_ONE_SECOND); // let the new thread has chance to run
393 
394         AssertEqual((pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0), (test->IsRunning() ? false : true),
395             "pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0 did not equal \
396             (test->IsRunning() ? false : true) as expected.", state);
397     }
398     BENCHMARK_LOGD("ThreadTest testThread007 end.");
399 }
400 
401 template <typename T>
ThreadTestStart(std::unique_ptr<T> & test,benchmark::State & state)402 void ThreadTestStart(std::unique_ptr<T>& test, benchmark::State& state)
403 {
404     ThreadStatus status = test->Start("", THREAD_PROI_NORMAL, THREAD_STACK_SIZE);
405     AssertEqual((status == ThreadStatus::OK), true,
406         "status == ThreadStatus::OK did not equal true as expected.", state);
407 
408     sleep(SLEEP_FOR_ONE_SECOND);
409     test->NotifyExitAsync();
410 
411     sleep(SLEEP_FOR_ONE_SECOND); // let the new thread has chance to run
412     AssertEqual((pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0), (test->IsRunning() ? false : true),
413         "pthread_equal(test->GetThread(), INVALID_THREAD_ID) != 0 did not equal \
414         (test->IsRunning() ? false : true) as expected.", state);
415 }
416 
417 /*
418  * @tc.name: testThread008
419  * @tc.desc: ThreadTest
420  */
BENCHMARK_F(BenchmarkThreadTest,testThread008)421 BENCHMARK_F(BenchmarkThreadTest, testThread008)(benchmark::State& state)
422 {
423     BENCHMARK_LOGD("ThreadTest testThread008 start.");
424     while (state.KeepRunning()) {
425         g_times = 0;
426         std::unique_ptr<TestThread> test = std::make_unique<TestThread>(INITIAL_TEST_VALUE, true, TestRun03);
427 
428         bool res = test->Thread::ReadyToWork();
429         AssertEqual(res, true, "res did not equal true as expected.", state);
430 
431         ThreadTestStart<TestThread>(test, state);
432     }
433     BENCHMARK_LOGD("ThreadTest testThread008 end.");
434 }
435 
436 class TestThread2 : public OHOS::Thread {
437 public:
TestThread2(const int data,ThreadRunFunc runFunc)438     TestThread2(const int data, ThreadRunFunc runFunc)
439         : data_(data), priority_(DEFAULT_PRIO), name_(DEFAULT_THREAD_NAME), runFunc_(runFunc)
440         {};
441 
442     TestThread2() = delete;
~TestThread2()443     ~TestThread2() {}
444 
445     int data_;
446     int priority_;
447     std::string name_;
448 protected:
449     bool Run() override;
450 
451 private:
452     ThreadRunFunc runFunc_;
453 };
454 
Run()455 bool TestThread2::Run()
456 {
457     BENCHMARK_LOGD("ThreadTest bool TestThread2::Run is called.");
458     priority_ = getpriority(PRIO_PROCESS, 0);
459     char threadName[MAX_THREAD_NAME_LEN + 1] = {0};
460     prctl(PR_GET_NAME, threadName, 0, 0);
461     name_ = threadName;
462 
463     if (runFunc_ != nullptr) {
464         return (*runFunc_)(data_);
465     }
466 
467     return false;
468 }
469 
470 /*
471  * @tc.name: testThread009
472  * @tc.desc: ThreadTest
473  */
BENCHMARK_F(BenchmarkThreadTest,testThread009)474 BENCHMARK_F(BenchmarkThreadTest, testThread009)(benchmark::State& state)
475 {
476     BENCHMARK_LOGD("ThreadTest testThread009 start.");
477     while (state.KeepRunning()) {
478         g_times = 0;
479         std::unique_ptr<TestThread2> test = std::make_unique<TestThread2>(0, TestRun03);
480 
481         bool res = test->ReadyToWork();
482         AssertEqual(res, true, "res did not equal true as expected.", state);
483 
484         ThreadTestStart<TestThread2>(test, state);
485     }
486     BENCHMARK_LOGD("ThreadTest testThread009 end.");
487 }
488 }  // namespace
489 }  // namespace OHOS
490 // Run the benchmark
491 BENCHMARK_MAIN();