SuperTinyKernel™ RTOS 1.05.3
Lightweight, high-performance, deterministic, bare-metal C++ RTOS for resource-constrained embedded systems. MIT Open Source License.
Loading...
Searching...
No Matches
stktest_switchstrategyedf.cpp
Go to the documentation of this file.
1/*
2 * SuperTinyKernel(TM) RTOS: Lightweight High-Performance Deterministic C++ RTOS for Embedded Systems.
3 *
4 * Source: https://github.com/SuperTinyKernel-RTOS
5 *
6 * Copyright (c) 2022-2026 Neutron Code Limited <stk@neutroncode.com>. All Rights Reserved.
7 * License: MIT License, see LICENSE for a full text.
8 */
9
10#include "stktest.h"
11
12namespace stk {
13namespace test {
14
15// ============================================================================ //
16// ============================ SwitchStrategyEDF ====================== //
17// ============================================================================ //
18
20{
21 void setup() {}
22 void teardown()
23 {
24 g_TestContext.ExpectAssert(false);
25 g_TestContext.RethrowAssertException(true);
26 }
27};
28
29TEST(SwitchStrategyEDF, GetFirstEmpty)
30{
32
33 try
34 {
35 g_TestContext.ExpectAssert(true);
36 rr.GetFirst();
37 CHECK_TEXT(false, "expecting assertion when empty");
38 }
39 catch (TestAssertPassed &pass)
40 {
41 CHECK(true);
42 g_TestContext.ExpectAssert(false);
43 }
44}
45
46TEST(SwitchStrategyEDF, GetNextEmpty)
47{
50 ITaskSwitchStrategy *strategy = kernel.GetSwitchStrategy();
51
52 kernel.Initialize();
53
54 kernel.AddTask(&task1);
55 kernel.RemoveTask(&task1);
56 CHECK_EQUAL(0, strategy->GetSize());
57
58 // expect to return NULL which puts core into a sleep mode, current is ignored by this strategy
59 CHECK_EQUAL(0, strategy->GetNext());
60}
61
62TEST(SwitchStrategyEDF, OnTaskDeadlineMissedNotSupported)
63{
66 ITaskSwitchStrategy *strategy = kernel.GetSwitchStrategy();
67
68 kernel.Initialize();
69 kernel.AddTask(&task1);
70
71 try
72 {
73 g_TestContext.ExpectAssert(true);
74 strategy->OnTaskDeadlineMissed(strategy->GetFirst());
75 CHECK_TEXT(false, "expecting assertion - OnTaskDeadlineMissed not supported");
76 }
77 catch (TestAssertPassed &pass)
78 {
79 CHECK(true);
80 g_TestContext.ExpectAssert(false);
81 }
82
83 // we need this workaround to pass 100% coverage test by blocking the exception
84 g_TestContext.ExpectAssert(true);
85 g_TestContext.RethrowAssertException(false);
86 strategy->OnTaskDeadlineMissed(strategy->GetFirst());
87}
88
89TEST(SwitchStrategyEDF, PriorityNext)
90{
92 TaskMock<ACCESS_USER> task1, task2, task3;
93 ITaskSwitchStrategy *strategy = kernel.GetSwitchStrategy();
94
95 kernel.Initialize();
96
97 // periodicity, deadline, start_delay
98 kernel.AddTask(&task1, 300, 300, 0); // latest deadline
99 kernel.AddTask(&task2, 200, 200, 0);
100 kernel.AddTask(&task3, 100, 100, 0); // earliest deadline
101
102 // EDF must select the task with the earliest relative deadline
103 IKernelTask *next = strategy->GetNext();
104
105 CHECK_EQUAL_TEXT(&task3, next->GetUserTask(), "EDF must select task with earliest relative deadline");
106
107 // Repeated GetNext must still select the same task
108 next = strategy->GetNext();
109 CHECK_EQUAL_TEXT(&task3, next->GetUserTask(), "EDF must continue selecting earliest-deadline task");
110
111 // Remove earliest-deadline task
112 kernel.RemoveTask(&task3);
113
114 next = strategy->GetNext();
115 CHECK_EQUAL_TEXT(&task2, next->GetUserTask(), "after removal, task2 has earliest relative deadline");
116
117 // Remove next earliest
118 kernel.RemoveTask(&task2);
119
120 next = strategy->GetNext();
121 CHECK_EQUAL_TEXT(&task1, next->GetUserTask(), "only remaining task must be selected");
122}
123
125{
127 TaskMock<ACCESS_USER> task1, task2, task3;
128 ITaskSwitchStrategy *strategy = kernel.GetSwitchStrategy();
129
130 kernel.Initialize();
131
132 // --- Stage 1: Add first task -----------------------------------------
133
134 kernel.AddTask(&task1, 300, 300, 0);
135
136 IKernelTask *next = strategy->GetFirst();
137
138 // Single task -> always returned
139 for (int32_t i = 0; i < 5; i++)
140 {
141 next = strategy->GetNext();
142 CHECK_EQUAL_TEXT(&task1, next->GetUserTask(), "single task must always be selected");
143 }
144
145 // --- Stage 2: Add second task ----------------------------------------
146
147 kernel.AddTask(&task2, 200, 200, 0); // earlier deadline than task1
148
149 // EDF must pick the task with earliest relative deadline
150 next = strategy->GetNext();
151 CHECK_EQUAL_TEXT(&task2, next->GetUserTask(), "task2 with earlier deadline should preempt task1");
152
153 // Still highest-priority task keeps running until removed or asleep
154 next = strategy->GetNext();
155 CHECK_EQUAL_TEXT(&task2, next->GetUserTask(), "task2 remains earliest-deadline task");
156
157 // --- Stage 3: Add third task -----------------------------------------
158
159 kernel.AddTask(&task3, 100, 100, 0); // earliest deadline
160
161 next = strategy->GetNext();
162 CHECK_EQUAL_TEXT(&task3, next->GetUserTask(), "task3 with earliest deadline should run first");
163
164 next = strategy->GetNext();
165 CHECK_EQUAL_TEXT(&task3, next->GetUserTask(), "task3 continues to run as earliest-deadline task");
166
167 // --- Stage 4: Remove tasks -------------------------------------------
168
169 kernel.RemoveTask(&task3);
170
171 next = strategy->GetNext();
172 CHECK_EQUAL_TEXT(&task2, next->GetUserTask(), "task2 becomes earliest-deadline after task3 removal");
173
174 next = strategy->GetNext();
175 CHECK_EQUAL_TEXT(&task2, next->GetUserTask(), "task2 continues as earliest-deadline task");
176
177 kernel.RemoveTask(&task2);
178
179 next = strategy->GetNext();
180 CHECK_EQUAL_TEXT(&task1, next->GetUserTask(), "task1 remains as only task");
181}
182
183TEST(SwitchStrategyEDF, RelativeDeadlineEvolution)
184{
187 PlatformTestMock *platform = static_cast<PlatformTestMock *>(kernel.GetPlatform());
188
189 kernel.Initialize();
190
191 // period = 10, deadline = 5, start_delay = 0
192 kernel.AddTask(&task, 10, 5, 0);
193
194 kernel.Start();
195
196 // Obtain kernel task
197 IKernelTask *ktask = kernel.GetSwitchStrategy()->GetFirst();
198 CHECK_TRUE_TEXT(ktask != nullptr, "Kernel task must exist");
199
200 // --- At release -------------------------------------------------------
201
202 // duration = 0
203 // relative_deadline = deadline
204 CHECK_EQUAL_TEXT(5, ktask->GetHrtRelativeDeadline(), "at release: relative deadline must equal deadline");
205
206 // --- Tick 1 -----------------------------------------------------------
207
208 platform->ProcessTick(); // duration = 1
209 CHECK_EQUAL_TEXT(4, ktask->GetHrtRelativeDeadline(), "after 1 tick: relative deadline must decrease by 1");
210
211 // --- Tick 2 -----------------------------------------------------------
212
213 platform->ProcessTick(); // duration = 2
214 CHECK_EQUAL_TEXT(3, ktask->GetHrtRelativeDeadline(), "after 2 ticks: relative deadline must decrease by 2");
215
216 // --- Tick 3 -----------------------------------------------------------
217
218 platform->ProcessTick(); // duration = 3
219 CHECK_EQUAL_TEXT(2, ktask->GetHrtRelativeDeadline(), "after 3 ticks: relative deadline must decrease by 3");
220
221 // --- Tick 4 -----------------------------------------------------------
222
223 platform->ProcessTick(); // duration = 4
224 CHECK_EQUAL_TEXT(1, ktask->GetHrtRelativeDeadline(), "after 4 ticks: relative deadline must be 1");
225
226 // --- Tick 5 (deadline boundary) --------------------------------------
227
228 platform->ProcessTick(); // duration = 5
229 CHECK_EQUAL_TEXT(0, ktask->GetHrtRelativeDeadline(), "at deadline: relative deadline must be zero");
230}
231
233{
235 {
236 counter = 0;
237 checked = 0;
238 platform = NULL;
239 task1 = NULL;
240 task2 = NULL;
241 task3 = NULL;
242 }
243
244 uint32_t counter, checked;
247
248 void Process()
249 {
250 platform->ProcessTick();
251 ++counter;
252
253 // active task after this tick
254 Stack *active = platform->m_stack_active;
255
256 if (counter == 1)
257 {
258 CHECK_EQUAL_TEXT((size_t)task3->GetStack(), active->SP, "tick 0: task3 earliest deadline");
259 ++checked;
260 Yield();
261 }
262 else
263 if (counter == 2)
264 {
265 CHECK_EQUAL_TEXT((size_t)task2->GetStack(), active->SP, "tick 1: task2 earliest deadline");
266 ++checked;
267 Yield();
268 }
269 else
270 if (counter == 3)
271 {
272 CHECK_EQUAL_TEXT((size_t)task1->GetStack(), active->SP, "tick 3+: task1 earliest deadline");
273 ++checked;
274 Yield();
275 }
276 }
277}
279
284
285TEST(SwitchStrategyEDF, DynamicScheduling)
286{
288 TaskMock<ACCESS_USER> task1, task2, task3;
289 PlatformTestMock *platform = static_cast<PlatformTestMock *>(kernel.GetPlatform());
290
291 kernel.Initialize();
292
293 // Add periodic tasks: (periodicity, deadline, start_delay)
294 kernel.AddTask(&task1, 4, 4, 0); // task1: period 5
295 kernel.AddTask(&task2, 3, 3, 0); // task2: period 3
296 kernel.AddTask(&task3, 2, 2, 0); // task3: period 2
297
298 kernel.Start();
299
300 g_EDFDynamicSchedulingContext.platform = platform;
301 g_EDFDynamicSchedulingContext.task1 = &task1;
302 g_EDFDynamicSchedulingContext.task2 = &task2;
303 g_EDFDynamicSchedulingContext.task3 = &task3;
305
306 // simulate ticks
308
309 CHECK_EQUAL_TEXT(3, g_EDFDynamicSchedulingContext.checked, "all 3 tasks must be switched");
310 CHECK_FALSE(platform->m_hard_fault);
311 CHECK_EQUAL(0, task1.m_deadline_missed);
312 CHECK_EQUAL(0, task2.m_deadline_missed);
313 CHECK_EQUAL(0, task3.m_deadline_missed);
314}
315
316} // namespace stk
317} // namespace test
void(* g_RelaxCpuHandler)()
__stk_relax_cpu handler.
Definition stktest.cpp:17
Namespace of STK package.
void Yield()
Notify scheduler to switch to the next runnable task.
Definition stk_helper.h:331
Namespace of the test inventory.
TestContext g_TestContext
Global instance of the TestContext.
Definition stktest.cpp:16
static void g_EDFDynamicSchedulingContextProcess()
static struct stk::test::EDFDynamicSchedulingContext g_EDFDynamicSchedulingContext
TEST_GROUP(Kernel)
TEST(Kernel, MaxTasks)
Concrete implementation of IKernel.
Definition stk.h:83
void Initialize(uint32_t resolution_us=PERIODICITY_DEFAULT)
Prepare kernel for use: reset state, configure the platform, and register the service singleton.
Definition stk.h:805
ITaskSwitchStrategy * GetSwitchStrategy()
Get task-switching strategy instance owned by this kernel.
Definition stk.h:959
void Start()
Start the scheduler. This call does not return until all tasks have exited (KERNEL_DYNAMIC mode) or i...
Definition stk.h:921
void RemoveTask(ITask *user_task)
Remove a previously added task from the kernel before Start().
Definition stk.h:895
IPlatform * GetPlatform()
Get platform driver instance owned by this kernel.
Definition stk.h:954
void AddTask(ITask *user_task)
Register task for a soft real-time (SRT) scheduling.
Definition stk.h:832
Stack descriptor.
Definition stk_common.h:181
Word SP
Stack Pointer (SP) register (note: must be the first entry in this struct).
Definition stk_common.h:182
Scheduling-strategy-facing interface for a kernel task slot.
Definition stk_common.h:493
virtual Timeout GetHrtRelativeDeadline() const =0
Get HRT task's relative deadline.
virtual ITask * GetUserTask()=0
Get user task.
Interface for a task switching strategy implementation.
Definition stk_common.h:782
virtual size_t GetSize() const =0
Get number of tasks currently managed by this strategy.
virtual IKernelTask * GetNext()=0
Advance the internal iterator and return the next runnable task.
virtual IKernelTask * GetFirst() const =0
Get first task.
virtual bool OnTaskDeadlineMissed(IKernelTask *task)=0
Notification that a task has exceeded its HRT deadline; returns whether the strategy can recover with...
Earliest Deadline First (EDF) scheduling strategy: always selects the runnable task with the least ti...
IKernelTask * GetFirst() const
Get first task in the managed set (used by the kernel for initial scheduling).
Throwable class for catching assertions from STK_ASSERT_HANDLER().
Definition stktest.h:67
IPlatform mock.
Definition stktest.h:75
void ProcessTick()
Process one tick.
Definition stktest.h:180
uint32_t m_deadline_missed
duration of workload if deadline is missed in HRT mode
Definition stktest.h:337