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 "safe_queue.h"
18 #include <array>
19 #include <future>
20 #include <iostream>
21 #include <thread>
22 #include <chrono>
23 #include "benchmark_log.h"
24 #include "benchmark_assert.h"
25 using namespace std;
26 
27 namespace OHOS {
28 namespace {
29 
30 const unsigned int QUEUE_SLOTS = 10;
31 const unsigned int THREAD_NUM = QUEUE_SLOTS + 1;
32 const int TIME_INCREMENT = 2;
33 const int SLEEP_FOR_TWO_SECONDS = 2;
34 const int SLEEP_FOR_THREE_SECONDS = 3;
35 const int SLEEP_FOR_FOUR_SECONDS = 4;
36 
37 class BenchmarkSafeQueue : public benchmark::Fixture {
38 public:
SetUp(const::benchmark::State & state)39     void SetUp(const ::benchmark::State& state) override
40     {
41     }
42 
TearDown(const::benchmark::State & state)43     void TearDown(const ::benchmark::State& state) override
44     {
45     }
46 
BenchmarkSafeQueue()47     BenchmarkSafeQueue()
48     {
49         Iterations(iterations);
50         Repetitions(repetitions);
51         ReportAggregatesOnly();
52     }
53 
54     ~BenchmarkSafeQueue() override = default;
55 
56 protected:
57     const int32_t repetitions = 3;
58     const int32_t iterations = 50;
59 };
60 
61 class DemoThreadData {
62 public:
DemoThreadData()63     DemoThreadData()
64     {
65         putStatus = false;
66         getStatus = false;
67         eraseStatus = false;
68         emptyStatus = false;
69     }
70     static SafeQueue<int> shareQueue;
71     bool putStatus;
72     bool getStatus;
73     bool eraseStatus;
74     bool emptyStatus;
75 
Put(int i)76     void Put(int i)
77     {
78         shareQueue.Push(i);
79         putStatus = true;
80     }
81 
Get(int & i)82     void Get(int &i)
83     {
84         shareQueue.Pop(i);
85         getStatus = true;
86     }
87 
Erase(int & i)88     void Erase(int &i)
89     {
90         shareQueue.Erase(i);
91         eraseStatus = true;
92     }
93 
Empty()94     void Empty()
95     {
96         shareQueue.Empty();
97         emptyStatus = true;
98     }
99 };
100 
101 SafeQueue<int> DemoThreadData::shareQueue;
102 
PutHandleThreadDataTime(DemoThreadData & q,int i,std::chrono::system_clock::time_point absTime)103 void PutHandleThreadDataTime(DemoThreadData &q, int i, std::chrono::system_clock::time_point absTime)
104 {
105     BENCHMARK_LOGD("SafeQueue PutHandleThreadDataTime is called i:%{public}d .", i);
106     std::this_thread::sleep_until(absTime);
107 
108     q.Put(i);
109 }
110 
GetHandleThreadDataTime(DemoThreadData & q,int i,std::chrono::system_clock::time_point absTime)111 void GetHandleThreadDataTime(DemoThreadData &q, int i, std::chrono::system_clock::time_point absTime)
112 {
113     BENCHMARK_LOGD("SafeQueue GetHandleThreadDataTime is called i:%{public}d.", i);
114     std::this_thread::sleep_until(absTime);
115     int t = 0;
116     q.Get(t);
117 }
118 
EraseHandleThreadDataTime(DemoThreadData & q,int i,std::chrono::system_clock::time_point absTime)119 void EraseHandleThreadDataTime(DemoThreadData &q, int i, std::chrono::system_clock::time_point absTime)
120 {
121     BENCHMARK_LOGD("SafeQueue EraseHandleThreadDataTime is called i:%{public}d.", i);
122     std::this_thread::sleep_until(absTime);
123 
124     q.Erase(i);
125 }
126 
EmptyHandleThreadDataTime(DemoThreadData & q,std::chrono::system_clock::time_point absTime)127 void EmptyHandleThreadDataTime(DemoThreadData &q, std::chrono::system_clock::time_point absTime)
128 {
129     BENCHMARK_LOGD("SafeQueue EmptyHandleThreadDataTime is called.");
130     std::this_thread::sleep_until(absTime);
131 
132     q.Empty();
133 }
134 
135 class TestThreading {
136 public:
TestThreading()137     TestThreading()
138     {
139         demoDatas.fill(DemoThreadData());
140     }
141 
AllThreadPut(std::time_t & timeT)142     void AllThreadPut(std::time_t &timeT)
143     {
144         using std::chrono::system_clock;
145         for (unsigned int i = 0; i < THREAD_NUM; i++) {
146             threads[i] = std::thread(PutHandleThreadDataTime,
147                 std::ref(demoDatas[i]), i, system_clock::from_time_t(timeT));
148         }
149     }
AllThreadGet(std::time_t & timeT)150     void AllThreadGet(std::time_t &timeT)
151     {
152         using std::chrono::system_clock;
153         for (unsigned int i = 0; i < THREAD_NUM; i++) {
154             threads[i] = std::thread(GetHandleThreadDataTime,
155                 std::ref(demoDatas[i]), i, system_clock::from_time_t(timeT));
156         }
157     }
158 
AllThreadErase(std::time_t & timeT)159     void AllThreadErase(std::time_t &timeT)
160     {
161         using std::chrono::system_clock;
162         for (unsigned int i = 0; i < THREAD_NUM; i++) {
163             threads[i] = std::thread(EraseHandleThreadDataTime,
164                 std::ref(demoDatas[i]), i, system_clock::from_time_t(timeT));
165         }
166     }
167 
AllThreadEmpty(std::time_t & timeT)168     void AllThreadEmpty(std::time_t &timeT)
169     {
170         using std::chrono::system_clock;
171         for (unsigned int i = 0; i < THREAD_NUM; i++) {
172             threads[i] = std::thread(EmptyHandleThreadDataTime,
173                 std::ref(demoDatas[i]), system_clock::from_time_t(timeT));
174         }
175     }
176 
GetThreadDatePushedStatus(unsigned int & pushedIn,unsigned int & unpushedIn)177     void GetThreadDatePushedStatus(unsigned int &pushedIn, unsigned int &unpushedIn)
178     {
179         pushedIn = 0;
180         unpushedIn = 0;
181         for (auto &t : demoDatas) {
182             if (t.putStatus) {
183                 pushedIn++;
184             } else {
185                 unpushedIn++;
186             }
187         }
188         BENCHMARK_LOGD("SafeQueue GetThreadDatePushedStatus pIn:%{public}d upIn:%{public}d.", pushedIn, unpushedIn);
189     }
190 
GetThreadDateGetedStatus(unsigned int & getedOut,unsigned int & ungetedOut)191     void GetThreadDateGetedStatus(unsigned int &getedOut, unsigned int &ungetedOut)
192     {
193         BENCHMARK_LOGD("SafeQueue void GetThreadDateGetedStatus is called.");
194         getedOut = 0;
195         ungetedOut = 0;
196         for (auto &t : demoDatas) {
197             if (t.getStatus) {
198                 getedOut++;
199             } else {
200                 ungetedOut++;
201             }
202         }
203         BENCHMARK_LOGD("SafeQueue GetThreadDateGetedStatus gOut:%{public}d uOut:%{public}d.", getedOut, ungetedOut);
204     }
205 
GetThreadDateEraseStatus(unsigned int & erase,unsigned int & unErase)206     void GetThreadDateEraseStatus(unsigned int &erase, unsigned int &unErase)
207     {
208         erase = 0;
209         unErase = 0;
210         for (auto &t : demoDatas) {
211             if (t.eraseStatus) {
212                 erase++;
213             } else {
214                 unErase++;
215             }
216         }
217         BENCHMARK_LOGD("SafeQueue GetThreadDateEraseStatus erase:%{public}d unErase:%{public}d.", erase, unErase);
218     }
219 
GetThreadDateEmptyStatus(unsigned int & empty,unsigned int & unEmpty)220     void GetThreadDateEmptyStatus(unsigned int &empty, unsigned int &unEmpty)
221     {
222         empty = 0;
223         unEmpty = 0;
224         for (auto &t : demoDatas) {
225             if (t.emptyStatus) {
226                 empty++;
227             } else {
228                 unEmpty++;
229             }
230         }
231         BENCHMARK_LOGD("SafeQueue GetThreadDateEmptyStatus empty:%{public}d unEmpty:%{public}d.", empty, unEmpty);
232     }
233 
ResetStatus()234     void ResetStatus()
235     {
236         BENCHMARK_LOGD("SafeQueue void ResetStatus is called.");
237         for (auto &t : threads) {
238             t.join();
239         }
240 
241         DemoThreadData::shareQueue.Clear();
242     }
243 
244     std::thread threads[THREAD_NUM];
245     std::array<DemoThreadData, THREAD_NUM> demoDatas;
246 };
247 
248 /*
249 * Feature: SafeBlockQueue
250 * Function:put
251 * SubFunction: NA
252 * FunctionPoints:
253 * EnvConditions: NA
254 * CaseDescription: Multiple threads put, one thread gets, all threads finish running normally
255 */
BENCHMARK_F(BenchmarkSafeQueue,testMutilthreadPutAndOneThreadGetOnemptyQueue)256 BENCHMARK_F(BenchmarkSafeQueue, testMutilthreadPutAndOneThreadGetOnemptyQueue)(benchmark::State& state)
257 {
258     BENCHMARK_LOGD("SafeQueue testMutilthreadPutAndOneThreadGetOnemptyQueue start.");
259     while (state.KeepRunning()) {
260         TestThreading testThread;
261         using std::chrono::system_clock;
262         std::time_t timeT = system_clock::to_time_t(system_clock::now());
263         timeT += TIME_INCREMENT;
264         testThread.AllThreadPut(timeT);
265 
266         // 1. queue is full and some threads is blocked
267         std::this_thread::sleep_for(std::chrono::seconds(SLEEP_FOR_THREE_SECONDS));
268         AssertTrue((DemoThreadData::shareQueue.Size() > 0),
269             "DemoThreadData::shareQueue.Size() > 0 did not equal true as expected.", state);
270 
271         unsigned int pushedIn = 0;
272         unsigned int unpushedIn = 0;
273 
274         testThread.GetThreadDatePushedStatus(pushedIn, unpushedIn);
275         AssertEqual(pushedIn, THREAD_NUM, "pushedIn did not equal THREAD_NUM as expected.", state);
276 
277         //2.  get one out  and wait some put in
278         for (unsigned int i = 0; i < THREAD_NUM; i++) {
279             int t = 0;
280             testThread.demoDatas[0].Get(t);
281         }
282 
283         std::this_thread::sleep_for(std::chrono::seconds(SLEEP_FOR_TWO_SECONDS));
284         // queue is full and some threads is blocked and is not joined
285         AssertTrue((DemoThreadData::shareQueue.Size() == 0),
286             "DemoThreadData::shareQueue.Size() == 0 did not equal true as expected.", state);
287 
288         // here means all thread end ok or if some operation blocked and the testcase blocked
289         testThread.ResetStatus();
290     }
291     BENCHMARK_LOGD("SafeQueue testMutilthreadPutAndOneThreadGetOnemptyQueue end.");
292 }
293 
294 /*
295 * Feature: SafeBlockQueue
296 * Function:put
297 * SubFunction: NA
298 * FunctionPoints:
299 * EnvConditions: NA
300 * CaseDescription: Multi-threaded put() and Multi-threaded get() on the empty queue.
301 * When all threads are waiting to reach a certain
302 * time-point, everyone run concurrently to see the status of the queue and the state of the thread.
303 */
BENCHMARK_F(BenchmarkSafeQueue,testMutilthreadPutAndGetConcurrently)304 BENCHMARK_F(BenchmarkSafeQueue, testMutilthreadPutAndGetConcurrently)(benchmark::State& state)
305 {
306     BENCHMARK_LOGD("SafeQueue testMutilthreadPutAndGetConcurrently start.");
307     while (state.KeepRunning()) {
308         using std::chrono::system_clock;
309         std::time_t timeT = system_clock::to_time_t(system_clock::now());
310         timeT += TIME_INCREMENT;
311 
312         TestThreading putInTestThread;
313         putInTestThread.AllThreadPut(timeT);
314 
315         TestThreading getOutTestThread;
316         getOutTestThread.AllThreadGet(timeT);
317 
318         // 1. queue is full and some threads is blocked
319         std::this_thread::sleep_for(std::chrono::seconds(SLEEP_FOR_FOUR_SECONDS));
320 
321         unsigned int pushedIn = 0;
322         unsigned int unpushedIn = 0;
323         unsigned int getedOut = 0;
324         unsigned int ungetedOut = 0;
325         putInTestThread.GetThreadDatePushedStatus(pushedIn, unpushedIn);
326         getOutTestThread.GetThreadDateGetedStatus(getedOut, ungetedOut);
327         AssertEqual(pushedIn, THREAD_NUM, "pushedIn did not equal THREAD_NUM as expected.", state);
328         AssertEqual(getedOut, THREAD_NUM, "getedOut did not equal THREAD_NUM as expected.", state);
329 
330         putInTestThread.ResetStatus();
331         getOutTestThread.ResetStatus();
332     }
333     BENCHMARK_LOGD("SafeQueue testMutilthreadPutAndGetConcurrently end.");
334 }
335 
336 /*
337 * Feature: SafeBlockQueue
338 * Function:put
339 * SubFunction: NA
340 * FunctionPoints:
341 * EnvConditions: NA
342 * CaseDescription: Multi-threaded put() and Multi-threaded get() on the not empty queue.
343 * When all threads are waiting to reach a certain
344 * time-point, everyone run concurrently to see the status of the queue and the state of the thread.
345 */
BENCHMARK_F(BenchmarkSafeQueue,testMutilthreadConcurrentGetAndPopInNotEmptyQueue)346 BENCHMARK_F(BenchmarkSafeQueue, testMutilthreadConcurrentGetAndPopInNotEmptyQueue)(benchmark::State& state)
347 {
348     BENCHMARK_LOGD("SafeQueue testMutilthreadConcurrentGetAndPopInNotEmptyQueue start.");
349     while (state.KeepRunning()) {
350         //1. prepare
351         using std::chrono::system_clock;
352         std::time_t timeT = system_clock::to_time_t(system_clock::now());
353         timeT += TIME_INCREMENT;
354 
355         AssertTrue((DemoThreadData::shareQueue.Size() == 0),
356             "DemoThreadData::shareQueue.Size() == 0 did not equal true as expected.", state);
357 
358         int t = 1;
359         for (unsigned int i = 0; i < THREAD_NUM; i++) {
360             DemoThreadData::shareQueue.Push(t);
361         }
362 
363         AssertTrue((DemoThreadData::shareQueue.Size() == THREAD_NUM),
364             "DemoThreadData::shareQueue.Size() == THREAD_NUM did not equal true as expected.", state);
365 
366         //2. start thread put in not full queue
367         TestThreading putInTestThread;
368         putInTestThread.AllThreadPut(timeT);
369 
370         TestThreading getOutTestThread;
371         getOutTestThread.AllThreadGet(timeT);
372 
373         std::this_thread::sleep_for(std::chrono::seconds(SLEEP_FOR_THREE_SECONDS));
374         AssertTrue((DemoThreadData::shareQueue.Size() == THREAD_NUM),
375             "DemoThreadData::shareQueue.Size() == THREAD_NUM did not equal true as expected.", state);
376 
377         unsigned int getedOut = 0;
378         unsigned int ungetedOut = 0;
379         unsigned int pushedIn = 0;
380         unsigned int unpushedIn = 0;
381         getOutTestThread.GetThreadDateGetedStatus(getedOut, ungetedOut);
382         putInTestThread.GetThreadDatePushedStatus(pushedIn, unpushedIn);
383         AssertEqual(pushedIn, THREAD_NUM, "pushedIn did not equal THREAD_NUM as expected.", state);
384         AssertEqual(getedOut, THREAD_NUM, "getedOut did not equal THREAD_NUM as expected.", state);
385 
386         // 3. reset status
387         putInTestThread.ResetStatus();
388         getOutTestThread.ResetStatus();
389     }
390     BENCHMARK_LOGD("SafeQueue testMutilthreadConcurrentGetAndPopInNotEmptyQueue end.");
391 }
392 
393 /*
394 * Feature: SafeBlockQueue
395 * Function:erase empty
396 * SubFunction: NA
397 * FunctionPoints:
398 * EnvConditions: NA
399 * CaseDescription: Multi-threaded erase() and Multi-threaded empty() on the empty queue.
400 * When all threads are waiting to reach a certain
401 * time-point, everyone run concurrently to see the status of the queue and the state of the thread.
402 */
BENCHMARK_F(BenchmarkSafeQueue,testMutilthreadEraseAndEmptyConcurrently)403 BENCHMARK_F(BenchmarkSafeQueue, testMutilthreadEraseAndEmptyConcurrently)(benchmark::State& state)
404 {
405     BENCHMARK_LOGD("SafeQueue testMutilthreadEraseAndEmptyConcurrently start.");
406     while (state.KeepRunning()) {
407         using std::chrono::system_clock;
408         std::time_t timeT = system_clock::to_time_t(system_clock::now());
409         timeT += TIME_INCREMENT;
410 
411         TestThreading putThread;
412         putThread.AllThreadPut(timeT);
413 
414         TestThreading eraseThread;
415         eraseThread.AllThreadErase(timeT);
416 
417         TestThreading emptyThread;
418         emptyThread.AllThreadEmpty(timeT);
419         std::this_thread::sleep_for(std::chrono::seconds(SLEEP_FOR_FOUR_SECONDS));
420 
421         unsigned int pushedIn = 0;
422         unsigned int unpushedIn = 0;
423         unsigned int erase = 0;
424         unsigned int unerase = 0;
425         unsigned int empty = 0;
426         unsigned int unempty = 0;
427         putThread.GetThreadDatePushedStatus(pushedIn, unpushedIn);
428         eraseThread.GetThreadDateEraseStatus(erase, unerase);
429         emptyThread.GetThreadDateEmptyStatus(empty, unempty);
430         AssertEqual(pushedIn, THREAD_NUM, "pushedIn did not equal THREAD_NUM as expected.", state);
431         AssertEqual(erase, THREAD_NUM, "erase did not equal THREAD_NUM as expected.", state);
432         AssertEqual(empty, THREAD_NUM, "empty did not equal THREAD_NUM as expected.", state);
433 
434         putThread.ResetStatus();
435         eraseThread.ResetStatus();
436         emptyThread.ResetStatus();
437     }
438     BENCHMARK_LOGD("SafeQueue testMutilthreadEraseAndEmptyConcurrently end.");
439 }
440 }  // namespace
441 }  // namespace OHOS
442 // Run the benchmark
443 BENCHMARK_MAIN();