SuperTinyKernel™ RTOS 1.08.x
Lightweight, high-performance, deterministic, bare-metal C++ RTOS for resource-constrained embedded systems. MIT Open Source License.
Loading...
Searching...
No Matches
stk_strategy_monotonic.h
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#ifndef STK_STRATEGY_MONOTONIC_H_
11#define STK_STRATEGY_MONOTONIC_H_
12
18
19#include "stk_common.h"
20
21namespace stk {
22
32
69template <EMonotonicSwitchStrategyType TStrategyType>
71{
72public:
85
89 {}
90
95
112 void AddTask(IKernelTask *task) override
113 {
114 STK_ASSERT(task != nullptr);
115 STK_ASSERT(task->GetHead() == nullptr);
116
117 if (m_tasks.IsEmpty())
118 {
119 m_tasks.LinkFront(task);
120 }
121 else
122 {
123 IKernelTask *itr = (*m_tasks.GetFirst()), *const start = itr;
124
125 for (;;)
126 {
127 bool higher_priority = false;
128 switch (TStrategyType)
129 {
130 case MSS_TYPE_RATE:
131 higher_priority = (task->GetHrtPeriodicity() < itr->GetHrtPeriodicity());
132 break;
134 higher_priority = (task->GetHrtDeadline() < itr->GetHrtDeadline());
135 break;
136 default:
137 STK_ASSERT(false);
138 break;
139 }
140
141 if (higher_priority)
142 {
143 if (itr == start)
144 {
145 m_tasks.LinkFront(task);
146 break;
147 }
148
149 m_tasks.Link(task, itr, itr->GetPrev());
150 break;
151 }
152
153 // end of the list
154 itr = (*itr->GetNext());
155 if (itr == start)
156 {
157 m_tasks.LinkBack(task);
158 break;
159 }
160 }
161 }
162 }
163
170 void RemoveTask(IKernelTask *task) override
171 {
172 STK_ASSERT(task != nullptr);
173 STK_ASSERT(GetSize() != 0U);
174 STK_ASSERT(task->GetHead() == &m_tasks);
175
176 m_tasks.Unlink(task);
177 }
178
189 {
190 IKernelTask *next = nullptr;
191
192 if (!m_tasks.IsEmpty())
193 {
194 IKernelTask *itr = (*m_tasks.GetFirst()), *const start = itr;
195
196 // scan from head (highest priority); skip tasks that are sleeping between
197 // periodic activations; return the first task ready to run
198 do
199 {
200 if (!itr->IsSleeping())
201 {
202 next = itr;
203 break; // list is sorted by priority; no need to continue
204 }
205
206 itr = (*itr->GetNext());
207 }
208 while (itr != start);
209 }
210
211 // nullptr means all tasks are sleeping, return idle signal to kernel
212 return next;
213 }
214
224 {
225 STK_ASSERT(m_tasks.GetSize() != 0U);
226
227 return (*m_tasks.GetFirst());
228 }
229
234 size_t GetSize() const override
235 {
236 return m_tasks.GetSize();
237 }
238
244 void OnTaskSleep(IKernelTask */*task*/) override
245 {
246 // Sleep API unsupported: RM/DM keeps a single sorted non-volatile list.
247 STK_ASSERT(false);
248 }
249
253 void OnTaskWake(IKernelTask */*task*/) override
254 {
255 // Sleep API unsupported: RM/DM keeps a single sorted non-volatile list.
256 STK_ASSERT(false);
257 }
258
259private:
261
263};
264
278{
279public:
294 {
295 uint32_t duration;
296 uint32_t period;
297 };
298
303 {
304 uint16_t task;
305 uint16_t total;
306 };
307
311 struct TaskInfo
312 {
314 uint32_t wcrt;
315 };
316
332 template <uint32_t TTaskCount>
334 {
336 TaskInfo info[TTaskCount];
337
341 operator bool() const { return schedulable; }
342 };
343
359 template <uint32_t TTaskCount>
361 {
362 STK_ASSERT(strategy != nullptr);
363 STK_ASSERT(const_cast<ITaskSwitchStrategy *>(strategy)->GetFirst() != nullptr);
364
365 const IKernelTask::ListHeadType *const ktasks = const_cast<ITaskSwitchStrategy *>(strategy)->GetFirst()->GetHead();
366
367 STK_ASSERT(ktasks != nullptr);
368 STK_ASSERT(ktasks->GetSize() <= TTaskCount);
369
371 TaskTiming tasks[TTaskCount] = {};
372
373 // fill tasks timing (bounded: never write past the array even if asserts are disabled)
374 const IKernelTask *itr = (*ktasks->GetFirst()), * const start = itr;
375 uint32_t idx = 0U;
376 do
377 {
378 STK_ASSERT(idx < TTaskCount);
379
380 if (idx < TTaskCount)
381 {
382 tasks[idx].period = static_cast<uint32_t>(itr->GetHrtPeriodicity());
383 tasks[idx].duration = static_cast<uint32_t>(itr->GetHrtDeadline());
384 ++idx;
385 }
386
387 itr = (*itr->GetNext());
388 }
389 while (itr != start);
390
391 STK_ASSERT(idx == TTaskCount);
392
393 // calculate CPU load, only for the tasks actually read
394 GetTaskCpuLoad(tasks, idx, ret.info);
395
396 // run the WCRT schedulability analysis
397 ret.schedulable = CalculateWCRT(tasks, idx, ret.info);
398
399 return ret;
400 }
401
434 static inline bool CalculateWCRT(const TaskTiming tasks[], const uint32_t count, TaskInfo info[])
435 {
436 bool schedulable = true;
437
438 if (count > 0U)
439 {
440 info[0].wcrt = tasks[0].duration;
441
442 for (uint32_t t = 1U; t < count; ++t)
443 {
444 const uint32_t Cx = tasks[t].duration;
445 const uint32_t Tx = tasks[t].period;
446
447 uint32_t w0 = Cx;
448 uint32_t w = Cx;
449 bool converged = false;
450
451 while (!converged)
452 {
453 w = Cx;
454 for (uint32_t i = 0U; i < t; ++i)
455 {
456 w += idiv_ceil(w0, tasks[i].period) * tasks[i].duration;
457 }
458
459 if ((w != w0) && (w <= Tx))
460 {
461 w0 = w;
462 }
463 else
464 {
465 converged = true;
466 }
467 }
468
469 if (w > Tx)
470 {
471 schedulable = false;
472 }
473
474 info[t].wcrt = w;
475 }
476 }
477
478 return schedulable;
479 }
480
490 static inline void GetTaskCpuLoad(const TaskTiming tasks[], const uint32_t count, TaskInfo info[])
491 {
492 uint16_t total = 0U;
493
494 for (uint32_t i = 0U; i < count; ++i)
495 {
496 uint16_t task_load = 0U;
497
498 // guard against a zero period (e.g. HRT parameters not populated)
499 if (tasks[i].period != 0U)
500 {
501 task_load = static_cast<uint16_t>(tasks[i].duration * 100U / tasks[i].period);
502 }
503
504 total += task_load;
505
506 info[i].cpu_load.task = task_load;
507 info[i].cpu_load.total = total;
508 }
509 }
510
511private:
517 static inline uint32_t idiv_ceil(uint32_t x, uint32_t y)
518 {
519 uint32_t result = 0U;
520
521 if (y != 0U)
522 {
523 result = x / y;
524
525 if ((x % y) > 0U)
526 {
527 result++;
528 }
529 }
530
531 return result;
532 }
533};
534
541
548
549} // namespace stk
550
551#endif /* STK_STRATEGY_MONOTONIC_H_ */
Contains interface definitions of the library.
#define STK_ASSERT(e)
Runtime assertion. Halts execution if the expression e evaluates to false.
Definition stk_defs.h:516
#define STK_VIRT_DTOR
Makes destructors virtual and compliant to strict rules if STK_STRICT_COMPLIANCY=0.
Definition stk_defs.h:261
Namespace of STK package.
SwitchStrategyMonotonic< MSS_TYPE_RATE > SwitchStrategyRM
Shorthand alias for SwitchStrategyMonotonic<MSS_TYPE_RATE>: Rate-Monotonic scheduling (shorter schedu...
SwitchStrategyMonotonic< MSS_TYPE_DEADLINE > SwitchStrategyDM
Shorthand alias for SwitchStrategyMonotonic<MSS_TYPE_DEADLINE>: Deadline-Monotonic scheduling (shorte...
EMonotonicSwitchStrategyType
Policy selector for SwitchStrategyMonotonic: determines the timing attribute used to assign fixed pri...
@ MSS_TYPE_DEADLINE
Deadline-Monotonic (DM): shorter execution deadline -> higher priority. Priority is derived from GetH...
@ MSS_TYPE_RATE
Rate-Monotonic (RM): shorter scheduling period -> higher priority. Priority is derived from GetHrtPer...
Scheduling-strategy-facing interface for a kernel task slot.
Definition stk_common.h:919
virtual Timeout GetHrtDeadline() const =0
Get HRT task deadline (max allowed task execution time).
DLHeadType ListHeadType
List head type for IKernelTask elements.
Definition stk_common.h:924
virtual bool IsSleeping() const =0
Check whether the task is currently sleeping.
virtual Timeout GetHrtPeriodicity() const =0
Get HRT task execution periodicity.
Interface for a task switching strategy implementation.
size_t GetSize() const
Get the number of entries currently in the list.
DLEntryType * GetFirst()
Get the first (front) entry without removing it.
DLEntryType * GetNext()
Get the next entry in the list.
DLHeadType * GetHead()
Get the list head this entry currently belongs to.
DLEntryType * GetPrev()
Get the previous entry in the list.
Monotonic scheduling strategy: Rate-Monotonic (RM) or Deadline-Monotonic (DM), selected at compile ti...
EConfig
Compile-time capability flags reported to the kernel.
void RemoveTask(IKernelTask *task) override
Remove a task from the list.
SwitchStrategyMonotonic()
Construct an empty strategy with no tasks.
void OnTaskWake(IKernelTask *) override
Not supported, asserts unconditionally.
void OnTaskSleep(IKernelTask *) override
Not supported, asserts unconditionally.
STK_NONCOPYABLE_CLASS(SwitchStrategyMonotonic)
STK_VIRT_DTOR ~SwitchStrategyMonotonic()=default
Destructor.
size_t GetSize() const override
Get the total number of tasks managed by this strategy.
IKernelTask * GetNext() override
Select and return the highest-priority non-sleeping task.
void AddTask(IKernelTask *task) override
Add a task to the priority-sorted runnable list.
IKernelTask * GetFirst() override
Get the first task in the managed set (used by the kernel for initial scheduling).
Utility class providing static methods for Worst-Case Response Time (WCRT) schedulability analysis of...
static bool CalculateWCRT(const TaskTiming tasks[], const uint32_t count, TaskInfo info[])
Compute the Worst-Case Response Time (WCRT) for each task in a fixed-priority periodic task set and d...
static void GetTaskCpuLoad(const TaskTiming tasks[], const uint32_t count, TaskInfo info[])
Compute per-task and cumulative CPU utilization, in whole percent.
static SchedulabilityCheckResult< TTaskCount > IsSchedulableWCRT(const ITaskSwitchStrategy *strategy)
Perform WCRT schedulability analysis on the task set registered with strategy.
static uint32_t idiv_ceil(uint32_t x, uint32_t y)
Compute the ceiling of an integer division: ceil(x / y).
Execution deadline and scheduling period for a single periodic HRT task, used as input to CalculateWC...
uint32_t period
Scheduling period T of the task (ticks), from GetHrtPeriodicity(). Used as the interference divisor T...
uint32_t duration
Worst-Case Execution Time C of the task (ticks), from GetHrtDeadline(). Used as the base cost and int...
CPU utilisation values for a single task, in whole percent.
uint16_t task
CPU load contributed by this task alone, in percent: floor(C / T * 100) = floor(duration * 100 / peri...
uint16_t total
Cumulative CPU load of this task plus all higher-priority tasks, in percent (running sum from index 0...
Analysis results for a single task: CPU load and computed WCRT.
uint32_t wcrt
Worst-Case Response Time in ticks. Task is schedulable if wcrt <= period (T).
TaskCpuLoad cpu_load
Per-task and cumulative CPU utilisation (see TaskCpuLoad).
Result of a WCRT schedulability test: overall verdict plus per-task details.
TaskInfo info[TTaskCount]
Per-task analysis results, ordered highest priority first (index 0 = highest priority task).
bool schedulable
true if every task's WCRT <= its period (T); false if any task misses its deadline.