SimpleKernel 1.17.0
Loading...
Searching...
No Matches
rr_scheduler_test.cpp
Go to the documentation of this file.
1
5#include "rr_scheduler.hpp"
6
7#include <cstdint>
8
9#include "system_test.h"
11#include "task_messages.hpp"
12
13namespace {
14
15auto test_rr_basic_functionality() -> bool {
16 klog::Info("Running test_rr_basic_functionality...");
17
18 RoundRobinScheduler scheduler;
19
20 // 创建测试任务
21 TaskControlBlock task1("Task1", 1, nullptr, nullptr);
22 task1.fsm.Receive(MsgSchedule{});
23
24 TaskControlBlock task2("Task2", 2, nullptr, nullptr);
25 task2.fsm.Receive(MsgSchedule{});
26
27 TaskControlBlock task3("Task3", 3, nullptr, nullptr);
28 task3.fsm.Receive(MsgSchedule{});
29
30 // 测试空队列
31 EXPECT_TRUE(scheduler.IsEmpty(), "Scheduler should be empty initially");
32 EXPECT_EQ(scheduler.GetQueueSize(), 0,
33 "Queue size should be 0 for empty queue");
34 EXPECT_EQ(scheduler.PickNext(), nullptr,
35 "PickNext should return nullptr for empty queue");
36
37 // 加入任务
38 scheduler.Enqueue(&task1);
39 EXPECT_EQ(scheduler.GetQueueSize(), 1,
40 "Queue size should be 1 after enqueue");
41
42 scheduler.Enqueue(&task2);
43 scheduler.Enqueue(&task3);
44 EXPECT_EQ(scheduler.GetQueueSize(), 3,
45 "Queue size should be 3 after 3 enqueues");
46
47 // 验证 FIFO 顺序(第一轮)
48 auto* picked1 = scheduler.PickNext();
49 EXPECT_EQ(picked1, &task1, "First picked task should be task1");
50
51 auto* picked2 = scheduler.PickNext();
52 EXPECT_EQ(picked2, &task2, "Second picked task should be task2");
53
54 auto* picked3 = scheduler.PickNext();
55 EXPECT_EQ(picked3, &task3, "Third picked task should be task3");
56
57 EXPECT_EQ(scheduler.PickNext(), nullptr,
58 "PickNext should return nullptr after all tasks picked");
59
60 klog::Info("test_rr_basic_functionality passed");
61 return true;
62}
63
64auto test_rr_round_robin_behavior() -> bool {
65 klog::Info("Running test_rr_round_robin_behavior...");
66
67 RoundRobinScheduler scheduler;
68
69 TaskControlBlock task1("Task1", 1, nullptr, nullptr);
70 task1.fsm.Receive(MsgSchedule{});
71 TaskControlBlock task2("Task2", 2, nullptr, nullptr);
72 task2.fsm.Receive(MsgSchedule{});
73 TaskControlBlock task3("Task3", 3, nullptr, nullptr);
74 task3.fsm.Receive(MsgSchedule{});
75
76 // 第一轮
77 scheduler.Enqueue(&task1);
78 scheduler.Enqueue(&task2);
79 scheduler.Enqueue(&task3);
80
81 EXPECT_EQ(scheduler.PickNext(), &task1, "First round: task1");
82 EXPECT_EQ(scheduler.PickNext(), &task2, "First round: task2");
83 EXPECT_EQ(scheduler.PickNext(), &task3, "First round: task3");
84
85 // 模拟时间片用完,任务重新入队(第二轮)
86 scheduler.Enqueue(&task1);
87 scheduler.Enqueue(&task2);
88 scheduler.Enqueue(&task3);
89
90 EXPECT_EQ(scheduler.PickNext(), &task1, "Second round: task1");
91 EXPECT_EQ(scheduler.PickNext(), &task2, "Second round: task2");
92 EXPECT_EQ(scheduler.PickNext(), &task3, "Second round: task3");
93
94 EXPECT_TRUE(scheduler.IsEmpty(), "Scheduler should be empty after 2 rounds");
95
96 klog::Info("test_rr_round_robin_behavior passed");
97 return true;
98}
99
100auto test_rr_time_slice_management() -> bool {
101 klog::Info("Running test_rr_time_slice_management...");
102
103 RoundRobinScheduler scheduler;
104
105 TaskControlBlock task1("Task1", 1, nullptr, nullptr);
106 task1.fsm.Receive(MsgSchedule{});
107 task1.sched_info.time_slice_default = 20;
108 task1.sched_info.time_slice_remaining = 5; // 时间片快用完了
109
110 // 入队应该重置时间片
111 scheduler.Enqueue(&task1);
112 EXPECT_EQ(task1.sched_info.time_slice_remaining, 20,
113 "Enqueue should reset time slice");
114
115 // 取出任务
116 auto* picked = scheduler.PickNext();
117 EXPECT_EQ(picked, &task1, "Should pick task1");
118
119 // 模拟时间片耗尽
120 task1.sched_info.time_slice_remaining = 0;
121 bool should_reenqueue = scheduler.OnTimeSliceExpired(&task1);
122 EXPECT_TRUE(should_reenqueue, "OnTimeSliceExpired should return true for RR");
123 EXPECT_EQ(task1.sched_info.time_slice_remaining, 20,
124 "OnTimeSliceExpired should reset time slice");
125
126 klog::Info("test_rr_time_slice_management passed");
127 return true;
128}
129
130auto test_rr_dequeue() -> bool {
131 klog::Info("Running test_rr_dequeue...");
132
133 RoundRobinScheduler scheduler;
134
135 TaskControlBlock task1("Task1", 1, nullptr, nullptr);
136 task1.fsm.Receive(MsgSchedule{});
137 TaskControlBlock task2("Task2", 2, nullptr, nullptr);
138 task2.fsm.Receive(MsgSchedule{});
139 TaskControlBlock task3("Task3", 3, nullptr, nullptr);
140 task3.fsm.Receive(MsgSchedule{});
141 TaskControlBlock task4("Task4", 4, nullptr, nullptr);
142 task4.fsm.Receive(MsgSchedule{});
143
144 scheduler.Enqueue(&task1);
145 scheduler.Enqueue(&task2);
146 scheduler.Enqueue(&task3);
147 scheduler.Enqueue(&task4);
148
149 EXPECT_EQ(scheduler.GetQueueSize(), 4, "Queue size should be 4");
150
151 // 移除中间任务
152 scheduler.Dequeue(&task2);
153 EXPECT_EQ(scheduler.GetQueueSize(), 3,
154 "Queue size should be 3 after dequeue");
155
156 // 移除队首任务
157 scheduler.Dequeue(&task1);
158 EXPECT_EQ(scheduler.GetQueueSize(), 2,
159 "Queue size should be 2 after dequeue");
160
161 // 验证剩余任务顺序
162 auto* picked1 = scheduler.PickNext();
163 EXPECT_EQ(picked1, &task3, "First remaining task should be task3");
164
165 auto* picked2 = scheduler.PickNext();
166 EXPECT_EQ(picked2, &task4, "Second remaining task should be task4");
167
168 EXPECT_TRUE(scheduler.IsEmpty(), "Scheduler should be empty");
169
170 klog::Info("test_rr_dequeue passed");
171 return true;
172}
173
174auto test_rr_statistics() -> bool {
175 klog::Info("Running test_rr_statistics...");
176
177 RoundRobinScheduler scheduler;
178
179 TaskControlBlock task1("Task1", 1, nullptr, nullptr);
180 TaskControlBlock task2("Task2", 2, nullptr, nullptr);
181
182 // 初始统计
183 auto stats = scheduler.GetStats();
184 EXPECT_EQ(stats.total_enqueues, 0, "Initial enqueues should be 0");
185 EXPECT_EQ(stats.total_dequeues, 0, "Initial dequeues should be 0");
186 EXPECT_EQ(stats.total_picks, 0, "Initial picks should be 0");
187 EXPECT_EQ(stats.total_preemptions, 0, "Initial preemptions should be 0");
188
189 // 测试入队统计
190 scheduler.Enqueue(&task1);
191 scheduler.Enqueue(&task2);
192 stats = scheduler.GetStats();
193 EXPECT_EQ(stats.total_enqueues, 2, "Enqueues should be 2");
194
195 // 测试选择统计
196 (void)scheduler.PickNext();
197 (void)scheduler.PickNext();
198 stats = scheduler.GetStats();
199 EXPECT_EQ(stats.total_picks, 2, "Picks should be 2");
200
201 // 测试出队统计
202 scheduler.Enqueue(&task1);
203 scheduler.Dequeue(&task1);
204 stats = scheduler.GetStats();
205 EXPECT_EQ(stats.total_dequeues, 1, "Dequeues should be 1");
206
207 // 测试抢占统计
208 scheduler.OnPreempted(&task1);
209 scheduler.OnPreempted(&task2);
210 stats = scheduler.GetStats();
211 EXPECT_EQ(stats.total_preemptions, 2, "Preemptions should be 2");
212
213 // 重置统计
214 scheduler.ResetStats();
215 stats = scheduler.GetStats();
216 EXPECT_EQ(stats.total_enqueues, 0, "Enqueues should be 0 after reset");
217 EXPECT_EQ(stats.total_dequeues, 0, "Dequeues should be 0 after reset");
218 EXPECT_EQ(stats.total_picks, 0, "Picks should be 0 after reset");
219 EXPECT_EQ(stats.total_preemptions, 0, "Preemptions should be 0 after reset");
220
221 klog::Info("test_rr_statistics passed");
222 return true;
223}
224
225auto test_rr_fairness() -> bool {
226 klog::Info("Running test_rr_fairness...");
227
228 RoundRobinScheduler scheduler;
229 constexpr size_t kTaskCount = 50;
230 TaskControlBlock* tasks[kTaskCount];
231
232 // 初始化任务
233 for (size_t i = 0; i < kTaskCount; ++i) {
234 tasks[i] = new TaskControlBlock("Task", 10, nullptr, nullptr);
235 tasks[i]->fsm.Receive(MsgSchedule{});
236 scheduler.Enqueue(tasks[i]);
237 }
238
239 EXPECT_EQ(scheduler.GetQueueSize(), kTaskCount,
240 "Queue size should match task count");
241
242 // 验证所有任务按照加入顺序被选中
243 for (size_t i = 0; i < kTaskCount; ++i) {
244 auto* picked = scheduler.PickNext();
245 EXPECT_NE(picked, nullptr, "Picked task should not be nullptr");
246 EXPECT_EQ(picked, tasks[i], "Task should be picked in order");
247 }
248
249 EXPECT_TRUE(scheduler.IsEmpty(), "Scheduler should be empty after all picks");
250
251 // 清理内存
252 for (size_t i = 0; i < kTaskCount; ++i) {
253 delete tasks[i];
254 }
255
256 klog::Info("test_rr_fairness passed");
257 return true;
258}
259
260auto test_rr_mixed_operations() -> bool {
261 klog::Info("Running test_rr_mixed_operations...");
262
263 RoundRobinScheduler scheduler;
264
265 TaskControlBlock task1("Task1", 1, nullptr, nullptr);
266 task1.fsm.Receive(MsgSchedule{});
267 TaskControlBlock task2("Task2", 2, nullptr, nullptr);
268 task2.fsm.Receive(MsgSchedule{});
269 TaskControlBlock task3("Task3", 3, nullptr, nullptr);
270 task3.fsm.Receive(MsgSchedule{});
271 TaskControlBlock task4("Task4", 4, nullptr, nullptr);
272 task4.fsm.Receive(MsgSchedule{});
273 TaskControlBlock task5("Task5", 5, nullptr, nullptr);
274 task5.fsm.Receive(MsgSchedule{});
275
276 // 复杂的混合操作序列
277 scheduler.Enqueue(&task1);
278 scheduler.Enqueue(&task2);
279 scheduler.Enqueue(&task3);
280
281 auto* picked1 = scheduler.PickNext();
282 EXPECT_EQ(picked1, &task1, "First pick should be task1");
283
284 scheduler.Enqueue(&task4);
285 scheduler.Dequeue(&task3);
286 scheduler.Enqueue(&task5);
287
288 // 现在队列应该是 [task2, task4, task5]
289 EXPECT_EQ(scheduler.GetQueueSize(), 3, "Queue size should be 3");
290
291 auto* picked2 = scheduler.PickNext();
292 EXPECT_EQ(picked2, &task2, "Second pick should be task2");
293
294 auto* picked3 = scheduler.PickNext();
295 EXPECT_EQ(picked3, &task4, "Third pick should be task4");
296
297 auto* picked4 = scheduler.PickNext();
298 EXPECT_EQ(picked4, &task5, "Fourth pick should be task5");
299
300 EXPECT_TRUE(scheduler.IsEmpty(), "Scheduler should be empty");
301
302 klog::Info("test_rr_mixed_operations passed");
303 return true;
304}
305
306auto test_rr_multiple_rounds() -> bool {
307 klog::Info("Running test_rr_multiple_rounds...");
308
309 RoundRobinScheduler scheduler;
310
311 TaskControlBlock task1("Task1", 1, nullptr, nullptr);
312 task1.fsm.Receive(MsgSchedule{});
313 TaskControlBlock task2("Task2", 2, nullptr, nullptr);
314 task2.fsm.Receive(MsgSchedule{});
315 TaskControlBlock task3("Task3", 3, nullptr, nullptr);
316 task3.fsm.Receive(MsgSchedule{});
317
318 // 进行 5 轮时间片轮转
319 for (int round = 0; round < 5; ++round) {
320 scheduler.Enqueue(&task1);
321 scheduler.Enqueue(&task2);
322 scheduler.Enqueue(&task3);
323
324 EXPECT_EQ(scheduler.PickNext(), &task1, "Round robin: task1");
325 EXPECT_EQ(scheduler.PickNext(), &task2, "Round robin: task2");
326 EXPECT_EQ(scheduler.PickNext(), &task3, "Round robin: task3");
327 EXPECT_TRUE(scheduler.IsEmpty(), "Queue should be empty after each round");
328 }
329
330 klog::Info("test_rr_multiple_rounds passed");
331 return true;
332}
333
334auto test_rr_hooks() -> bool {
335 klog::Info("Running test_rr_hooks...");
336
337 RoundRobinScheduler scheduler;
338
339 TaskControlBlock task1("Task1", 1, nullptr, nullptr);
340 task1.fsm.Receive(MsgSchedule{});
341 task1.sched_info.priority = 5;
342
343 // 测试各种钩子函数不会崩溃
344 scheduler.OnScheduled(&task1);
345 scheduler.OnPreempted(&task1);
346 scheduler.BoostPriority(&task1, 10);
347 scheduler.RestorePriority(&task1);
348
349 // OnTick 应返回 false(RR 使用时间片而非 tick 计数)
350 bool need_resched = scheduler.OnTick(&task1);
351 EXPECT_EQ(need_resched, false, "OnTick should return false for RR");
352
353 // OnTimeSliceExpired 应返回 true(需要重新入队)
354 bool need_requeue = scheduler.OnTimeSliceExpired(&task1);
355 EXPECT_EQ(need_requeue, true, "OnTimeSliceExpired should return true for RR");
356
357 // 验证调度器仍正常工作
358 scheduler.Enqueue(&task1);
359 auto* picked = scheduler.PickNext();
360 EXPECT_EQ(picked, &task1, "Scheduler should still work after hook calls");
361
362 klog::Info("test_rr_hooks passed");
363 return true;
364}
365
366auto test_rr_robustness() -> bool {
367 klog::Info("Running test_rr_robustness...");
368
369 RoundRobinScheduler scheduler;
370
371 TaskControlBlock task1("Task1", 1, nullptr, nullptr);
372 task1.fsm.Receive(MsgSchedule{});
373
374 // 空队列操作
375 EXPECT_EQ(scheduler.PickNext(), nullptr,
376 "PickNext on empty queue should return nullptr");
377 scheduler.Dequeue(&task1); // 不应崩溃
378
379 // 重复移除
380 scheduler.Enqueue(&task1);
381 scheduler.Dequeue(&task1);
382 scheduler.Dequeue(&task1); // 再次移除已移除的任务,不应崩溃
383 EXPECT_TRUE(scheduler.IsEmpty(), "Scheduler should be empty");
384
385 // nullptr 处理
386 scheduler.Enqueue(nullptr); // 不应崩溃
387 scheduler.Dequeue(nullptr); // 不应崩溃
388 EXPECT_TRUE(scheduler.IsEmpty(), "Scheduler should still be empty");
389
390 klog::Info("test_rr_robustness passed");
391 return true;
392}
393
394auto test_rr_interleaved_operations() -> bool {
395 klog::Info("Running test_rr_interleaved_operations...");
396
397 RoundRobinScheduler scheduler;
398
399 TaskControlBlock task1("Task1", 1, nullptr, nullptr);
400 task1.fsm.Receive(MsgSchedule{});
401 TaskControlBlock task2("Task2", 2, nullptr, nullptr);
402 task2.fsm.Receive(MsgSchedule{});
403 TaskControlBlock task3("Task3", 3, nullptr, nullptr);
404 task3.fsm.Receive(MsgSchedule{});
405
406 // 交替的入队和出队操作
407 scheduler.Enqueue(&task1);
408 auto* picked1 = scheduler.PickNext();
409 EXPECT_EQ(picked1, &task1, "Should pick task1");
410
411 scheduler.Enqueue(&task2);
412 scheduler.Enqueue(&task3);
413 auto* picked2 = scheduler.PickNext();
414 EXPECT_EQ(picked2, &task2, "Should pick task2");
415
416 scheduler.Enqueue(&task1);
417 auto* picked3 = scheduler.PickNext();
418 EXPECT_EQ(picked3, &task3, "Should pick task3");
419
420 auto* picked4 = scheduler.PickNext();
421 EXPECT_EQ(picked4, &task1, "Should pick task1 again");
422
423 EXPECT_TRUE(scheduler.IsEmpty(), "Scheduler should be empty");
424
425 klog::Info("test_rr_interleaved_operations passed");
426 return true;
427}
428
429} // namespace
430
431auto rr_scheduler_test() -> bool {
432 klog::Info("\n=== Round-Robin Scheduler System Tests ===\n");
433
434 if (!test_rr_basic_functionality()) {
435 return false;
436 }
437
438 if (!test_rr_round_robin_behavior()) {
439 return false;
440 }
441
442 if (!test_rr_time_slice_management()) {
443 return false;
444 }
445
446 if (!test_rr_dequeue()) {
447 return false;
448 }
449
450 if (!test_rr_statistics()) {
451 return false;
452 }
453
454 if (!test_rr_fairness()) {
455 return false;
456 }
457
458 if (!test_rr_mixed_operations()) {
459 return false;
460 }
461
462 if (!test_rr_multiple_rounds()) {
463 return false;
464 }
465
466 if (!test_rr_hooks()) {
467 return false;
468 }
469
470 if (!test_rr_robustness()) {
471 return false;
472 }
473
474 if (!test_rr_interleaved_operations()) {
475 return false;
476 }
477
478 klog::Info("=== All Round-Robin Scheduler Tests Passed ===\n");
479 return true;
480}
Round-Robin 调度器
auto OnPreempted(TaskControlBlock *task) -> void override
任务被抢占时调用
auto Dequeue(TaskControlBlock *task) -> void override
从就绪队列中移除指定任务
auto OnTimeSliceExpired(TaskControlBlock *task) -> bool override
时间片耗尽处理
auto PickNext() -> TaskControlBlock *override
选择下一个要运行的任务
auto Enqueue(TaskControlBlock *task) -> void override
将任务加入就绪队列尾部
auto GetQueueSize() const -> size_t override
获取就绪队列大小
auto IsEmpty() const -> bool override
判断队列是否为空
virtual auto OnTick(TaskControlBlock *current) -> bool
Tick 更新:每个时钟中断时调用,用于更新调度器状态
virtual auto BoostPriority(TaskControlBlock *task, int new_priority) -> void
优先级提升:当任务持有资源时被更高优先级任务等待,提升其优先级
virtual auto RestorePriority(TaskControlBlock *task) -> void
优先级恢复:当任务释放资源后,恢复其原始优先级
virtual auto GetStats() const -> const Stats &
获取调度器统计信息
virtual auto OnScheduled(TaskControlBlock *task) -> void
任务开始运行时调用 (从 Ready 变为 Running)
virtual auto ResetStats() -> void
重置统计信息
auto Receive(const etl::imessage &msg) -> void
向 FSM 发送消息
Definition task_fsm.hpp:163
auto Info(etl::format_string< Args... > fmt, Args &&... args) -> void
以 INFO 级别记录日志
调度消息(无负载,用作事件)
任务控制块,管理进程/线程的核心数据结构
TaskFsm fsm
任务状态机
auto rr_scheduler_test() -> bool
#define EXPECT_TRUE(cond, msg)
#define EXPECT_NE(val1, val2, msg)
#define EXPECT_EQ(val1, val2, msg)