1 /*
2 * Copyright (c) 2023-2024 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 "libpandabase/taskmanager/task_queue.h"
17 #include <gtest/gtest.h>
18 #include <thread>
19
20 namespace ark::taskmanager::internal {
21
22 class TaskTest : public testing::Test {
23 public:
24 static constexpr TaskProperties TASK_PROPERTIES {TaskType::GC, VMType::STATIC_VM, TaskExecutionMode::BACKGROUND};
25
26 TaskTest() = default;
27 ~TaskTest() override = default;
28
29 NO_COPY_SEMANTIC(TaskTest);
30 NO_MOVE_SEMANTIC(TaskTest);
31
32 private:
33 };
34
TEST_F(TaskTest,TaskPropertiesTest)35 TEST_F(TaskTest, TaskPropertiesTest)
36 {
37 for (auto taskType : ALL_TASK_TYPES) {
38 for (auto vmType : ALL_VM_TYPES) {
39 for (auto executionMode : ALL_TASK_EXECUTION_MODES) {
40 TaskProperties prop(taskType, vmType, executionMode);
41 ASSERT_EQ(prop.GetTaskType(), taskType);
42 ASSERT_EQ(prop.GetVMType(), vmType);
43 ASSERT_EQ(prop.GetTaskExecutionMode(), executionMode);
44 }
45 }
46 }
47 }
48
TEST_F(TaskTest,TaskSimpleTest)49 TEST_F(TaskTest, TaskSimpleTest)
50 {
51 std::string message = "task:";
52 Task task = Task::Create({TaskType::GC, VMType::DYNAMIC_VM, TaskExecutionMode::BACKGROUND},
53 [&message]() { message += "done"; });
54 EXPECT_EQ(message, "task:");
55 EXPECT_EQ(task.GetTaskProperties().GetTaskType(), TaskType::GC);
56 EXPECT_EQ(task.GetTaskProperties().GetVMType(), VMType::DYNAMIC_VM);
57 EXPECT_EQ(task.GetTaskProperties().GetTaskExecutionMode(), TaskExecutionMode::BACKGROUND);
58 task.RunTask();
59 EXPECT_EQ(message, "task:done");
60 EXPECT_EQ(task.GetTaskProperties().GetTaskType(), TaskType::GC);
61 EXPECT_EQ(task.GetTaskProperties().GetVMType(), VMType::DYNAMIC_VM);
62 EXPECT_EQ(task.GetTaskProperties().GetTaskExecutionMode(), TaskExecutionMode::BACKGROUND);
63 }
64
TEST_F(TaskTest,TaskQueueSimpleTest)65 TEST_F(TaskTest, TaskQueueSimpleTest)
66 {
67 size_t counter = 0;
68 // Creation of TaskQueue
69 constexpr uint8_t QUEUE_PRIORITY = TaskQueueInterface::MAX_PRIORITY;
70 SchedulableTaskQueueInterface *queue = TaskQueue<>::Create(TaskType::GC, VMType::DYNAMIC_VM, QUEUE_PRIORITY);
71 EXPECT_EQ(queue->GetTaskType(), TaskType::GC);
72 EXPECT_TRUE(queue->IsEmpty());
73 EXPECT_EQ(queue->Size(), 0);
74 EXPECT_EQ(queue->GetPriority(), QUEUE_PRIORITY);
75 // Add COUNT_OF_TASKS tasks in queue-> Each task increment counter.
76 constexpr size_t COUNT_OF_TASKS = 10;
77 for (size_t i = 0; i < COUNT_OF_TASKS; i++) {
78 queue->AddTask(Task::Create({TaskType::GC, VMType::DYNAMIC_VM, TaskExecutionMode::BACKGROUND},
79 [&counter]() { counter++; }));
80 }
81 EXPECT_EQ(queue->GetTaskType(), TaskType::GC);
82 EXPECT_FALSE(queue->IsEmpty());
83 EXPECT_EQ(queue->Size(), COUNT_OF_TASKS);
84 // Pop count_of_done_task tasks from queue and execute them.
85 constexpr size_t COUNT_OF_DONE_TASKS = 6;
86 ASSERT(COUNT_OF_DONE_TASKS < COUNT_OF_TASKS);
87 for (size_t i = 0; i < COUNT_OF_DONE_TASKS; i++) {
88 auto popTask = static_cast<SchedulableTaskQueueInterface *>(queue)->PopTask();
89 EXPECT_EQ(popTask.value().GetTaskProperties().GetTaskType(), TaskType::GC);
90 popTask.value().RunTask();
91 EXPECT_EQ(counter, i + 1);
92 }
93 // Now in queue counter_of_tasks - COUNT_OF_DONE_TASKS objects.
94 EXPECT_EQ(queue->GetTaskType(), TaskType::GC);
95 EXPECT_EQ(queue->IsEmpty(), COUNT_OF_TASKS == COUNT_OF_DONE_TASKS);
96 EXPECT_EQ(queue->Size(), COUNT_OF_TASKS - COUNT_OF_DONE_TASKS);
97 // Change priority of this queue
98 constexpr size_t NEW_QUEUE_PRIORITY = TaskQueueInterface::MIN_PRIORITY;
99 queue->SetPriority(NEW_QUEUE_PRIORITY);
100 EXPECT_EQ(queue->GetTaskType(), TaskType::GC);
101 EXPECT_EQ(queue->IsEmpty(), COUNT_OF_TASKS == COUNT_OF_DONE_TASKS);
102 EXPECT_EQ(queue->Size(), COUNT_OF_TASKS - COUNT_OF_DONE_TASKS);
103 EXPECT_EQ(queue->GetPriority(), NEW_QUEUE_PRIORITY);
104 // Add in queue counter_of_tasks new tasks-> Each add 2 to counter
105 for (size_t i = 0; i < COUNT_OF_TASKS; i++) {
106 queue->AddTask(Task::Create({TaskType::GC, VMType::DYNAMIC_VM, TaskExecutionMode::BACKGROUND},
107 [&counter]() { counter += 2U; }));
108 }
109 // After we have 2 * counter_of_tasks - counter_of_done_tasks objects in queue
110 EXPECT_EQ(queue->GetTaskType(), TaskType::GC);
111 EXPECT_FALSE(queue->IsEmpty());
112 EXPECT_EQ(queue->Size(), 2U * COUNT_OF_TASKS - COUNT_OF_DONE_TASKS);
113 // Pop and execute all tasks in queue->
114 while (!queue->IsEmpty()) {
115 auto nextTask = static_cast<SchedulableTaskQueueInterface *>(queue)->PopTask();
116 nextTask.value().RunTask();
117 }
118 // After all task is done, counter = 3 * COUNT_OF_TASKS
119 EXPECT_EQ(counter, 3 * COUNT_OF_TASKS);
120 EXPECT_EQ(queue->GetTaskType(), TaskType::GC);
121 EXPECT_EQ(queue->Size(), 0);
122 TaskQueue<>::Destroy(queue);
123 }
124
TEST_F(TaskTest,TaskQueueMultithreadingOnePushOnePop)125 TEST_F(TaskTest, TaskQueueMultithreadingOnePushOnePop)
126 {
127 constexpr uint8_t QUEUE_PRIORITY = TaskQueueInterface::MAX_PRIORITY;
128 SchedulableTaskQueueInterface *queue = TaskQueue<>::Create(TaskType::GC, VMType::STATIC_VM, QUEUE_PRIORITY);
129 std::atomic_size_t counter = 0;
130 constexpr size_t RESULT_COUNT = 10'000;
131 auto pusher = [&queue, &counter]() {
132 for (size_t i = 0; i < RESULT_COUNT; i++) {
133 queue->AddTask(Task::Create(TASK_PROPERTIES, [&counter]() { counter++; }));
134 }
135 };
136 auto popper = [&queue]() {
137 for (size_t i = 0; i < RESULT_COUNT;) {
138 auto task = queue->PopTask();
139 if (!task.has_value()) {
140 continue;
141 }
142 task->RunTask();
143 i++;
144 }
145 };
146 auto *worker1 = new std::thread(pusher);
147 auto *worker2 = new std::thread(popper);
148 worker1->join();
149 worker2->join();
150 delete worker1;
151 delete worker2;
152 EXPECT_EQ(counter, RESULT_COUNT);
153 TaskQueue<>::Destroy(queue);
154 }
155
TEST_F(TaskTest,TaskQueueMultithreadingNPushNPop)156 TEST_F(TaskTest, TaskQueueMultithreadingNPushNPop)
157 {
158 constexpr uint8_t QUEUE_PRIORITY = TaskQueueInterface::MAX_PRIORITY;
159 SchedulableTaskQueueInterface *queue = TaskQueue<>::Create(TaskType::GC, VMType::STATIC_VM, QUEUE_PRIORITY);
160 std::atomic_size_t counter = 0;
161 constexpr size_t RESULT_COUNT = 10'000;
162 auto pusher = [&queue, &counter]() {
163 for (size_t i = 0; i < RESULT_COUNT; i++) {
164 queue->AddTask(Task::Create(TASK_PROPERTIES, [&counter]() { counter++; }));
165 }
166 };
167 os::memory::Mutex popLock;
168 auto popper = [&queue, &popLock]() {
169 std::optional<Task> task;
170 for (size_t i = 0; i < RESULT_COUNT;) {
171 {
172 os::memory::LockHolder popLockGourd(popLock);
173 task = queue->PopTask();
174 }
175 if (!task.has_value()) {
176 continue;
177 }
178 task->RunTask();
179 i++;
180 }
181 };
182 std::vector<std::thread *> pushers;
183 std::vector<std::thread *> poppers;
184 constexpr size_t COUNT_OF_WORKERS = 10;
185 for (size_t i = 0; i < COUNT_OF_WORKERS; i++) {
186 pushers.push_back(new std::thread(pusher));
187 poppers.push_back(new std::thread(popper));
188 }
189 for (size_t i = 0; i < COUNT_OF_WORKERS; i++) {
190 pushers[i]->join();
191 poppers[i]->join();
192 delete pushers[i];
193 delete poppers[i];
194 }
195 EXPECT_EQ(counter, RESULT_COUNT * COUNT_OF_WORKERS);
196 TaskQueue<>::Destroy(queue);
197 }
198
TEST_F(TaskTest,TaskQueueWaitForQueueEmptyAndFinish)199 TEST_F(TaskTest, TaskQueueWaitForQueueEmptyAndFinish)
200 {
201 constexpr uint8_t QUEUE_PRIORITY = TaskQueueInterface::MAX_PRIORITY;
202 SchedulableTaskQueueInterface *queue = TaskQueue<>::Create(TaskType::GC, VMType::STATIC_VM, QUEUE_PRIORITY);
203 std::atomic_size_t counter = 0;
204 constexpr size_t TASK_COUNT = 100'000;
205 for (size_t i = 0; i < TASK_COUNT; i++) {
206 queue->AddTask(Task::Create(TASK_PROPERTIES, [&counter]() { counter++; }));
207 }
208
209 constexpr size_t THREAD_COUNTER = 10;
210 os::memory::Mutex popLock;
211 std::vector<std::thread> poppers;
212 for (size_t i = 0; i < THREAD_COUNTER; i++) {
213 poppers.emplace_back([&queue, &popLock]() {
214 while (true) {
215 os::memory::LockHolder popLockGourd(popLock);
216 auto task = queue->PopTask();
217 if (!task.has_value()) {
218 break;
219 }
220 task.value().RunTask();
221 }
222 });
223 }
224
225 for (auto &popper : poppers) {
226 popper.join();
227 }
228 EXPECT_EQ(counter, TASK_COUNT);
229 TaskQueue<>::Destroy(queue);
230 }
231
TEST_F(TaskTest,TaskQueueForegroundAndBackgroundTasks)232 TEST_F(TaskTest, TaskQueueForegroundAndBackgroundTasks)
233 {
234 constexpr uint8_t QUEUE_PRIORITY = TaskQueueInterface::MAX_PRIORITY;
235 SchedulableTaskQueueInterface *queue = TaskQueue<>::Create(TaskType::GC, VMType::STATIC_VM, QUEUE_PRIORITY);
236 std::queue<TaskExecutionMode> modeQueue;
237 constexpr TaskProperties FOREGROUND_PROPERTIES(TaskType::GC, VMType::STATIC_VM, TaskExecutionMode::FOREGROUND);
238 constexpr TaskProperties BACKGROUND_PROPERTIES(TaskType::GC, VMType::STATIC_VM, TaskExecutionMode::BACKGROUND);
239 constexpr size_t TASKS_COUNT = 100;
240
241 for (size_t i = 0; i < TASKS_COUNT; i++) {
242 queue->AddTask(
243 Task::Create(BACKGROUND_PROPERTIES, [&modeQueue]() { modeQueue.push(TaskExecutionMode::BACKGROUND); }));
244 }
245 for (size_t i = 0; i < TASKS_COUNT; i++) {
246 queue->AddTask(
247 Task::Create(FOREGROUND_PROPERTIES, [&modeQueue]() { modeQueue.push(TaskExecutionMode::FOREGROUND); }));
248 }
249
250 for (size_t i = 0; i < 2U * TASKS_COUNT; i++) {
251 auto task = queue->PopTask();
252 ASSERT_TRUE(task.has_value());
253 task.value().RunTask();
254 }
255
256 for (size_t i = 0; i < TASKS_COUNT; i++) {
257 auto mode = modeQueue.front();
258 modeQueue.pop();
259 EXPECT_EQ(mode, TaskExecutionMode::FOREGROUND);
260 }
261 for (size_t i = 0; i < TASKS_COUNT; i++) {
262 auto mode = modeQueue.front();
263 modeQueue.pop();
264 EXPECT_EQ(mode, TaskExecutionMode::BACKGROUND);
265 }
266 EXPECT_TRUE(modeQueue.empty());
267 TaskQueue<>::Destroy(queue);
268 }
269
TEST_F(TaskTest,PopTaskWithExecutionMode)270 TEST_F(TaskTest, PopTaskWithExecutionMode)
271 {
272 constexpr uint8_t QUEUE_PRIORITY = TaskQueueInterface::MAX_PRIORITY;
273 SchedulableTaskQueueInterface *queue = TaskQueue<>::Create(TaskType::GC, VMType::STATIC_VM, QUEUE_PRIORITY);
274 std::queue<TaskExecutionMode> modeQueue;
275 constexpr TaskProperties FOREGROUND_PROPERTIES(TaskType::GC, VMType::STATIC_VM, TaskExecutionMode::FOREGROUND);
276 constexpr TaskProperties BACKGROUND_PROPERTIES(TaskType::GC, VMType::STATIC_VM, TaskExecutionMode::BACKGROUND);
277 constexpr size_t TASKS_COUNT = 100;
278
279 for (size_t i = 0; i < TASKS_COUNT; i++) {
280 queue->AddTask(
281 Task::Create(BACKGROUND_PROPERTIES, [&modeQueue]() { modeQueue.push(TaskExecutionMode::BACKGROUND); }));
282 }
283 for (size_t i = 0; i < TASKS_COUNT; i++) {
284 queue->AddTask(
285 Task::Create(FOREGROUND_PROPERTIES, [&modeQueue]() { modeQueue.push(TaskExecutionMode::FOREGROUND); }));
286 }
287
288 for (size_t i = 0; i < TASKS_COUNT; i++) {
289 auto task = queue->PopTask(TaskExecutionMode::FOREGROUND);
290 ASSERT_TRUE(task.has_value());
291 task.value().RunTask();
292 task = queue->PopTask(TaskExecutionMode::BACKGROUND);
293 ASSERT_TRUE(task.has_value());
294 task.value().RunTask();
295 }
296
297 for (size_t i = 0; i < TASKS_COUNT; i++) {
298 auto mode = modeQueue.front();
299 modeQueue.pop();
300 EXPECT_EQ(mode, TaskExecutionMode::FOREGROUND);
301 mode = modeQueue.front();
302 modeQueue.pop();
303 EXPECT_EQ(mode, TaskExecutionMode::BACKGROUND);
304 }
305 EXPECT_TRUE(modeQueue.empty());
306 TaskQueue<>::Destroy(queue);
307 }
308
309 } // namespace ark::taskmanager::internal
310