• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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