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_swrrobin.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_SWRROBIN_H_
11#define STK_STRATEGY_SWRROBIN_H_
12
18
19#include "stk_common.h"
20
21namespace stk {
22
74template <bool NoSleepYield> class SwitchStrategySmoothWeightedRoundRobinT final : public ITaskSwitchStrategy
75{
76public:
88
93
98
110 void AddTask(IKernelTask *task) override
111 {
112 STK_ASSERT(task != nullptr);
113 STK_ASSERT((task->GetWeight() > 0) && (task->GetWeight() <= 0x7FFFFF)); // must not be negative, max 24-bit number
114
116
117 AddActive(task);
118 }
119
128 void RemoveTask(IKernelTask *task) override
129 {
130 STK_ASSERT(task != nullptr);
131 STK_ASSERT((task->GetHead() == &m_tasks) || (task->GetHead() == &m_sleep));
132
133 if (task->GetHead() == &m_tasks)
134 {
135 RemoveActive(task);
136 }
137 else
138 {
139 m_sleep.Unlink(task);
140 }
141 }
142
159 {
160 IKernelTask *next = nullptr;
161
162 if (!m_tasks.IsEmpty())
163 {
164 IKernelTask *skip = nullptr;
165
166 if __stk_constexpr_cpp17 (NoSleepYield)
167 {
168 skip = m_yielded[0]; // one-shot: excluded from this selection only
169 m_yielded[0] = nullptr;
170 }
171 else
172 {
173 STK_UNUSED(skip);
174 }
175
176 IKernelTask *itr = (*m_tasks.GetFirst());
177 IKernelTask *const start = itr;
178 int32_t max_weight = INT32_MIN;
179
180 do
181 {
182 const int32_t candidate_weight = itr->GetCurrentWeight() + itr->GetWeight();
183 itr->SetCurrentWeight(candidate_weight); // yielding task still accrues weight
184
185 if __stk_constexpr_cpp17 (NoSleepYield)
186 {
187 if ((itr != skip) && (candidate_weight > max_weight))
188 {
189 max_weight = candidate_weight;
190 next = itr;
191 }
192 }
193 else
194 {
195 if (candidate_weight > max_weight)
196 {
197 max_weight = candidate_weight;
198 next = itr;
199 }
200 }
201
202 itr = (*itr->GetNext());
203 }
204 while (itr != start);
205
206 if __stk_constexpr_cpp17 (NoSleepYield)
207 {
208 // yielding task is the only runnable task: nobody to hand over to
209 if (next == nullptr)
210 {
211 STK_ASSERT(skip != nullptr);
212
213 next = skip;
214 max_weight = skip->GetCurrentWeight();
215 }
216 }
217
218 STK_ASSERT(next != nullptr);
219
220 next->SetCurrentWeight(max_weight - m_total_weight);
221 }
222
223 return next;
224 }
225
233 {
234 STK_ASSERT(GetSize() != 0U);
235
236 return (*(!m_tasks.IsEmpty() ? m_tasks.GetFirst() : m_sleep.GetFirst()));
237 }
238
242 size_t GetSize() const override
243 {
244 return m_tasks.GetSize() + m_sleep.GetSize();
245 }
246
253 void OnTaskSleep(IKernelTask *task) override
254 {
255 STK_ASSERT(task != nullptr);
256 STK_ASSERT(task->IsSleeping());
257 STK_ASSERT(task->GetHead() == &m_tasks);
258
259 RemoveActive(task);
260 m_sleep.LinkBack(task);
261 }
262
275 void OnTaskWake(IKernelTask *task) override
276 {
277 STK_ASSERT(task != nullptr);
278 STK_ASSERT(!task->IsSleeping());
279 STK_ASSERT(task->GetHead() == &m_sleep);
280
281 m_sleep.Unlink(task);
282
283 // boost priority of the previously sleeping task, this resembles to a RR pattern
284 // with tasks having equal weights
286
287 AddActive(task);
288 }
289
299 bool OnTaskYield(IKernelTask *task) override
300 {
301 if __stk_constexpr_cpp17 (NoSleepYield)
302 {
303 STK_ASSERT(task != nullptr);
304
305 bool handled = false;
306
307 if (task->GetHead() == &m_tasks)
308 {
309 m_yielded[0] = task; // excluded from the next selection only
310 handled = true;
311 }
312
313 return handled;
314 }
315 else
316 {
317 STK_UNUSED(task);
318 return false;
319 }
320 }
321
322private:
324
331 {
332 m_tasks.LinkBack(task);
333 m_total_weight += task->GetWeight();
334 }
335
344 {
345 if __stk_constexpr_cpp17 (NoSleepYield)
346 {
347 // do not leave a dangling pointer to a task that is leaving the runnable set
348 if (m_yielded[0] == task)
349 {
350 m_yielded[0] = nullptr;
351 }
352 }
353
354 m_tasks.Unlink(task);
355 m_total_weight -= task->GetWeight();
356 }
357
362};
363
369
375
381
387
388} // namespace stk
389
390#endif /* STK_STRATEGY_SWRROBIN_H_ */
Contains interface definitions of the library.
#define STK_UNUSED(X)
Explicitly marks a variable as unused to suppress compiler warnings.
Definition stk_defs.h:715
#define STK_ASSERT(e)
Runtime assertion. Halts execution if the expression e evaluates to false.
Definition stk_defs.h:516
#define __stk_constexpr_cpp17
constexpr definition for C++17 and above.
Definition stk_defs.h:489
#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.
SwitchStrategySmoothWeightedRoundRobinT< true > SwitchStrategySmoothWeightedRoundRobinNoSleepYield
Smooth Weighted Round-Robin strategy where Yield() keeps the task runnable (NOSLEEP_YIELD_API = 1).
SwitchStrategySmoothWeightedRoundRobinT< false > SwitchStrategySmoothWeightedRoundRobin
Shorthand alias for SwitchStrategySmoothWeightedRoundRobinT<false>.
SwitchStrategySmoothWeightedRoundRobinT< true > SwitchStrategySWRR_NSY
Shorthand for SwitchStrategySmoothWeightedRoundRobinNoSleepYield.
SwitchStrategySmoothWeightedRoundRobinT< false > SwitchStrategySWRR
Shorthand alias for SwitchStrategySmoothWeightedRoundRobin.
static constexpr Weight NO_WEIGHT
Weight value: weight is not set.
Definition stk_common.h:223
Scheduling-strategy-facing interface for a kernel task slot.
Definition stk_common.h:902
virtual Weight GetCurrentWeight() const =0
Get the current dynamic weight value of this task.
virtual Weight GetWeight() const =0
Get static base weight assigned to the task.
DLHeadType ListHeadType
List head type for IKernelTask elements.
Definition stk_common.h:907
virtual bool IsSleeping() const =0
Check whether the task is currently sleeping.
virtual void SetCurrentWeight(Weight weight)=0
Set the current dynamic weight value used by the scheduling strategy.
Interface for a task switching strategy implementation.
static constexpr size_t Value
Definition stk_defs.h:675
DLEntryType * GetNext()
Get the next entry in the list.
DLHeadType * GetHead()
Get the list head this entry currently belongs to.
Smooth Weighted Round-Robin (SWRR) task-switching strategy: distributes CPU time proportionally to pe...
IKernelTask * m_yielded[STK_ALLOCATE_COUNT< NoSleepYield, 1U, 1U, 0U >::Value]
STK_NONCOPYABLE_CLASS(SwitchStrategySmoothWeightedRoundRobinT)
void AddTask(IKernelTask *task) override
Add task to the runnable set.
SwitchStrategySmoothWeightedRoundRobinT()
Construct an empty strategy with no tasks and a zero total weight.
EConfig
Compile-time capability flags reported to the kernel.
void RemoveActive(IKernelTask *task)
Remove task from m_tasks and update the total weight.
IKernelTask * GetNext() override
Select and return the next task to run, applying one step of the SWRR algorithm.
size_t GetSize() const override
Get the total number of tasks managed by this strategy.
void OnTaskWake(IKernelTask *task) override
Notification that a task has become runnable again.
void RemoveTask(IKernelTask *task) override
Remove task from whichever list it currently occupies.
bool OnTaskYield(IKernelTask *task) override
Notification that the running task called Yield() (non-HRT kernel modes only).
IKernelTask * GetFirst() override
Get first task in the managed set (used by the kernel for initial scheduling).
void AddActive(IKernelTask *task)
Append task to m_tasks and update the total weight.
void OnTaskSleep(IKernelTask *task) override
Notification that a task has entered the sleeping state.
STK_VIRT_DTOR ~SwitchStrategySmoothWeightedRoundRobinT()=default
Destructor.