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:
89
94
99
111 void AddTask(IKernelTask *task) override
112 {
113 STK_ASSERT(task != nullptr);
114 STK_ASSERT((task->GetWeight() > 0) && (task->GetWeight() <= 0x7FFFFF)); // must not be negative, max 24-bit number
115
117
118 AddActive(task);
119 }
120
129 void RemoveTask(IKernelTask *task) override
130 {
131 STK_ASSERT(task != nullptr);
132 STK_ASSERT((task->GetHead() == &m_tasks) || (task->GetHead() == &m_sleep));
133
134 if (task->GetHead() == &m_tasks)
135 {
136 RemoveActive(task);
137 }
138 else
139 {
140 m_sleep.Unlink(task);
141 }
142 }
143
160 {
161 IKernelTask *next = nullptr;
162
163 if (!m_tasks.IsEmpty())
164 {
165 IKernelTask *skip = nullptr;
166
167 if __stk_constexpr_cpp17 (NoSleepYield)
168 {
169 skip = m_yielded[0]; // one-shot: excluded from this selection only
170 m_yielded[0] = nullptr;
171 }
172 else
173 {
174 STK_UNUSED(skip);
175 }
176
177 IKernelTask *itr = (*m_tasks.GetFirst());
178 IKernelTask *const start = itr;
179 int32_t max_weight = INT32_MIN;
180
181 do
182 {
183 const int32_t candidate_weight = itr->GetCurrentWeight() + itr->GetWeight();
184 itr->SetCurrentWeight(candidate_weight); // yielding task still accrues weight
185
186 if __stk_constexpr_cpp17 (NoSleepYield)
187 {
188 if ((itr != skip) && (candidate_weight > max_weight))
189 {
190 max_weight = candidate_weight;
191 next = itr;
192 }
193 }
194 else
195 {
196 if (candidate_weight > max_weight)
197 {
198 max_weight = candidate_weight;
199 next = itr;
200 }
201 }
202
203 itr = (*itr->GetNext());
204 }
205 while (itr != start);
206
207 if __stk_constexpr_cpp17 (NoSleepYield)
208 {
209 // yielding task is the only runnable task: nobody to hand over to
210 if (next == nullptr)
211 {
212 STK_ASSERT(skip != nullptr);
213
214 next = skip;
215 max_weight = skip->GetCurrentWeight();
216 }
217 }
218
219 STK_ASSERT(next != nullptr);
220
221 next->SetCurrentWeight(max_weight - m_total_weight);
222 }
223
224 return next;
225 }
226
234 {
235 STK_ASSERT(GetSize() != 0U);
236
237 return (*(!m_tasks.IsEmpty() ? m_tasks.GetFirst() : m_sleep.GetFirst()));
238 }
239
243 size_t GetSize() const override
244 {
245 return m_tasks.GetSize() + m_sleep.GetSize();
246 }
247
254 void OnTaskSleep(IKernelTask *task) override
255 {
256 STK_ASSERT(task != nullptr);
257 STK_ASSERT(task->IsSleeping());
258 STK_ASSERT(task->GetHead() == &m_tasks);
259
260 RemoveActive(task);
261 m_sleep.LinkBack(task);
262 }
263
276 void OnTaskWake(IKernelTask *task) override
277 {
278 STK_ASSERT(task != nullptr);
279 STK_ASSERT(!task->IsSleeping());
280 STK_ASSERT(task->GetHead() == &m_sleep);
281
282 m_sleep.Unlink(task);
283
284 // boost priority of the previously sleeping task, this resembles to a RR pattern
285 // with tasks having equal weights
287
288 AddActive(task);
289 }
290
300 bool OnTaskYield(IKernelTask *task) override
301 {
302 if __stk_constexpr_cpp17 (NoSleepYield)
303 {
304 STK_ASSERT(task != nullptr);
305
306 bool handled = false;
307
308 if (task->GetHead() == &m_tasks)
309 {
310 m_yielded[0] = task; // excluded from the next selection only
311 handled = true;
312 }
313
314 return handled;
315 }
316 else
317 {
318 STK_UNUSED(task);
319 return false;
320 }
321 }
322
323private:
325
332 {
333 m_tasks.LinkBack(task);
334 m_total_weight += task->GetWeight();
335 }
336
345 {
346 if __stk_constexpr_cpp17 (NoSleepYield)
347 {
348 // do not leave a dangling pointer to a task that is leaving the runnable set
349 if (m_yielded[0] == task)
350 {
351 m_yielded[0] = nullptr;
352 }
353 }
354
355 m_tasks.Unlink(task);
356 m_total_weight -= task->GetWeight();
357 }
358
363};
364
370
376
382
388
389} // namespace stk
390
391#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:239
Scheduling-strategy-facing interface for a kernel task slot.
Definition stk_common.h:963
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:968
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.