1 /*
2  * Copyright (c) 2021-2022 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 <chrono>
17 #include <cstdio>
18 #include <sys/prctl.h>
19 #include <gtest/gtest.h>
20 #include "thread_pool.h"
21 
22 using namespace testing::ext;
23 
24 namespace OHOS {
25 namespace {
26 class UtilsThreadPoolTest : public testing::Test
27 {
28 public :
29     static void SetUpTestCase(void);
30     static void TearDownTestCase(void);
31     void SetUp();
32     void TearDown();
33 };
34 
35 int g_times = 0;
36 bool g_ready = false;
37 std::mutex g_mutex;
38 std::condition_variable g_cv;
39 
SetUpTestCase(void)40 void UtilsThreadPoolTest::SetUpTestCase(void)
41 {
42     // step 2: input testsuit setup step
43 }
44 
TearDownTestCase(void)45 void UtilsThreadPoolTest::TearDownTestCase(void)
46 {
47     // step 2: input testsuit teardown step
48 }
49 
SetUp(void)50 void UtilsThreadPoolTest::SetUp(void)
51 {
52     // recover
53     g_times = 0;
54     g_ready = false;
55 }
56 
TearDown(void)57 void UtilsThreadPoolTest::TearDown(void)
58 {
59     // recover
60     g_times = 0;
61     g_ready = false;
62 
63 }
64 
65 HWTEST_F(UtilsThreadPoolTest, test_01, TestSize.Level0)
66 {
67     ThreadPool pool;
68     EXPECT_EQ(pool.GetName(), "");
69     EXPECT_EQ((int)pool.GetMaxTaskNum(), 0);
70     EXPECT_EQ((int)pool.GetThreadsNum(), 0);
71     EXPECT_EQ((int)pool.GetCurTaskNum(), 0);
72 }
73 
74 HWTEST_F(UtilsThreadPoolTest, test_02, TestSize.Level0)
75 {
76     ThreadPool pool("test_02_pool");
77     EXPECT_EQ(pool.GetName(), "test_02_pool");
78     EXPECT_EQ((int)pool.GetMaxTaskNum(), 0);
79     EXPECT_EQ((int)pool.GetThreadsNum(), 0);
80     EXPECT_EQ((int)pool.GetCurTaskNum(), 0);
81 }
82 
83 HWTEST_F(UtilsThreadPoolTest, test_03, TestSize.Level0)
84 {
85     ThreadPool pool("test_02_pool");
86     pool.SetMaxTaskNum(10);
87     EXPECT_EQ(pool.GetName(), "test_02_pool");
88     EXPECT_EQ((int)pool.GetMaxTaskNum(), 10);
89     EXPECT_EQ((int)pool.GetThreadsNum(), 0);
90     EXPECT_EQ((int)pool.GetCurTaskNum(), 0);
91 }
92 
93 HWTEST_F(UtilsThreadPoolTest, test_04, TestSize.Level0)
94 {
95     ThreadPool pool;
96     pool.Start(4);
97     EXPECT_EQ(pool.GetName(), "");
98     EXPECT_EQ((int)pool.GetMaxTaskNum(), 0);
99     EXPECT_EQ((int)pool.GetThreadsNum(), 4);
100     EXPECT_EQ((int)pool.GetCurTaskNum(), 0);
101 
102     // add no task, g_times has no change
103     EXPECT_EQ(g_times, 0);
104     pool.Stop();
105 }
106 
TestFuncAddOneTime(int & i)107 void TestFuncAddOneTime(int& i)
108 {
109     std::lock_guard<std::mutex> lock(g_mutex);
110     ++g_times;
111 }
112 
TestFuncSubOneTime(int & i)113 void TestFuncSubOneTime(int& i)
114 {
115     std::lock_guard<std::mutex> lock(g_mutex);
116     --g_times;
117 }
118 
119 // simple task, total task num less than the MaxTaskNum
120 HWTEST_F(UtilsThreadPoolTest, test_05, TestSize.Level0)
121 {
122     ThreadPool pool;
123     pool.Start(5);
124     EXPECT_EQ(pool.GetName(), "");
125     EXPECT_EQ((int)pool.GetThreadsNum(), 5);
126     EXPECT_EQ((int)pool.GetCurTaskNum(), 0);
127 
128     for (int i = 0; i < 3; ++i)
129     {
130         auto task = std::bind(TestFuncAddOneTime, i);
131         pool.AddTask(task);
132     }
133 
134     for (int i = 0; i < 2; ++i)
135     {
136         auto task = std::bind(TestFuncSubOneTime, i);
137         pool.AddTask(task);
138     }
139 
140     sleep(1);
141     EXPECT_EQ((int)pool.GetCurTaskNum(), 0);
142     // add 5 tasks, add 3 times and sub 2 times
143     EXPECT_EQ(g_times, 1);
144     pool.Stop();
145 }
146 
147 // simaple task, total task num exceed the MaxTaskNum and the threads num
148 HWTEST_F(UtilsThreadPoolTest, test_06, TestSize.Level0)
149 {
150     ThreadPool pool;
151     pool.Start(5);
152     EXPECT_EQ(pool.GetName(), "");
153     EXPECT_EQ((int)pool.GetThreadsNum(), 5);
154     EXPECT_EQ((int)pool.GetCurTaskNum(), 0);
155 
156     pool.SetMaxTaskNum(10);
157 
158     for (int i = 0; i < 8; ++i)
159     {
160         auto task = std::bind(TestFuncAddOneTime, i);
161         pool.AddTask(task);
162     }
163 
164     for (int i = 0; i < 7; ++i)
165     {
166         auto task = std::bind(TestFuncSubOneTime, i);
167         pool.AddTask(task);
168     }
169 
170     sleep(1);
171     // 1 second should be enough to complete these tasks. if not, this case would be fail
172     EXPECT_EQ((int)pool.GetCurTaskNum(), 0);
173     // add 5 task, add 3 times and sub 2 times
174     EXPECT_EQ(g_times, 1);
175     pool.Stop();
176 }
177 
TestFuncAddWait(int & i)178 void TestFuncAddWait(int& i)
179 {
180     std::unique_lock<std::mutex> lk(g_mutex);
181     ++g_times;
182     printf("after func:%s0%d called, :%d\n", __func__, i, g_times);
183     g_cv.wait(lk, [] {return g_ready;});
184     printf("func:%s0%d received ready signal!\n", __func__, i);
185 }
186 
TestFuncSubWait(int & i)187 void TestFuncSubWait(int& i)
188 {
189     std::unique_lock<std::mutex> lk(g_mutex);
190     --g_times;
191     printf("after func:%s0%d called, :%d\n", __func__, i, g_times);
192     g_cv.wait(lk, [] {return g_ready;});
193     printf("func:%s0%d received ready signal!\n", __func__, i);
194 }
195 
196 // complex task, wait for notify by the main thread
197 // total task num less than the threads num and the MaxTaskNum
198 HWTEST_F(UtilsThreadPoolTest, test_07, TestSize.Level0)
199 {
200     ThreadPool pool;
201     pool.Start(5);
202     EXPECT_EQ(pool.GetName(), "");
203     EXPECT_EQ((int)pool.GetThreadsNum(), 5);
204     EXPECT_EQ((int)pool.GetCurTaskNum(), 0);
205 
206     for (int i = 0; i < 3; ++i)
207     {
208         auto task = std::bind(TestFuncAddWait, i);
209         pool.AddTask(task);
210     }
211 
212     for (int i = 0; i < 2; ++i)
213     {
214         auto task = std::bind(TestFuncSubWait, i);
215         pool.AddTask(task);
216     }
217 
218     std::this_thread::sleep_for(std::chrono::seconds(1)); // release cpu proactively, let the task threads go into wait
219     {
220         std::lock_guard<std::mutex> lk(g_mutex);
221         g_ready = true;
222     }
223 
224     g_cv.notify_all();
225 
226     // these tasks are endless Loop, 5 threads process 5 tasks, zero task remains in the task queue
227     EXPECT_EQ((int)pool.GetCurTaskNum(), 0);
228     // add 5 task, add 3 times and sub 2 times
229     EXPECT_EQ(g_times, 1);
230     pool.Stop();
231 }
232 
233 HWTEST_F(UtilsThreadPoolTest, test_08, TestSize.Level0)
234 {
235     ThreadPool pool;
236     pool.Start(5);
237     EXPECT_EQ(pool.GetName(), "");
238     EXPECT_EQ((int)pool.GetThreadsNum(), 5);
239     EXPECT_EQ((int)pool.GetCurTaskNum(), 0);
240 
241     pool.SetMaxTaskNum(10);
242 
243     // ADD 15 tasks
244     for (int i = 0; i < 8; ++i)
245     {
246         auto task = std::bind(TestFuncAddWait, i);
247         pool.AddTask(task);
248     }
249 
250     for (int i = 0; i < 7; ++i)
251     {
252         auto task = std::bind(TestFuncSubWait, i);
253         pool.AddTask(task);
254     }
255 
256     sleep(1);
257     // at this time, the first 5 tasks execute and wait for notify, the rest 10 tasks stay in the task queue.
258     EXPECT_EQ((int)pool.GetCurTaskNum(), 10);
259     // FIFO,
260     EXPECT_EQ(g_times, 5);
261 
262     // notify_all
263     {
264         std::lock_guard<std::mutex> lk(g_mutex);
265         g_ready = true;
266     }
267     g_cv.notify_all();
268 
269     // after noity, task thread wake up, and g_ready is true, new tasks didn't need to wait
270     sleep(1);
271     // these tasks are endless Loop, and total num of task exceed the MaxTaskNum
272     EXPECT_EQ((int)pool.GetCurTaskNum(), 0);
273     EXPECT_EQ(g_times, 1);
274     pool.Stop();
275 }
276 
TestFuncGetName(const std::string & poolName)277 void TestFuncGetName(const std::string& poolName)
278 {
279     char name[16];
280     prctl(PR_GET_NAME, name);
281     std::string nameStr(name);
282     size_t found = nameStr.find(poolName);
283     EXPECT_EQ(found, 0);
284 }
285 
286 /*
287  *  Test_09 is used to verify the name set to ThreadPool will be set as the real name of threads in pool.
288  */
289 HWTEST_F(UtilsThreadPoolTest, test_09, TestSize.Level0)
290 {
291     std::string poolName("test_09_pool");
292     ThreadPool pool(poolName);
293     pool.Start(5);
294     EXPECT_EQ(pool.GetName(), poolName);
295     EXPECT_EQ((int)pool.GetThreadsNum(), 5);
296     EXPECT_EQ((int)pool.GetCurTaskNum(), 0);
297 
298     for (int i = 0; i < 5; ++i)
299     {
300         auto task = std::bind(TestFuncGetName, poolName);
301         pool.AddTask(task);
302     }
303 
304     sleep(1);
305     // these tasks are endless Loop, 5 threads process 5 tasks, zero task remains in the task queue
306     EXPECT_EQ((int)pool.GetCurTaskNum(), 0);
307     pool.Stop();
308 }
309 }  // namespace
310 }  // namespace OHOS
311