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.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_H_
11#define STK_H_
12
13#include "stk_helper.h"
20
36
37namespace stk {
38
39// Helper function for Kernel::UpdateTaskState.
40template <bool TicklessMode> __stk_forceinline Timeout GetInitialSleepTicks();
43
86template <uint8_t TMode, uint32_t TSize, class TStrategy, class TPlatform>
87class Kernel
88#ifndef _STK_UNDER_TEST
89final
90#endif
91: public IKernel, private IPlatform::IEventHandler
92{
93protected:
99
105
110 enum ERequest : uint8_t
111 {
113 REQ_ADD_TASK = (1 << 0)
114 };
115
127 class KernelTask final : public IKernelTask
128 {
129 friend class Kernel;
130
135 enum EStateFlags : uint32_t
136 {
140 STATE_WAKE_PENDING = (1U << 2),
141 STATE_SUSPENDED = (1U << 3),
143 };
144
145 public:
154 {
156 };
157
164 {
165 // bind to wait object
167 {
168 m_wait_obj->m_task = this;
169 }
170 }
171
175 ITask *GetUserTask() override { return m_user; }
176
180 Stack GetUserStack() const override { return m_stack;}
181
185 bool IsBusy() const { return (m_user != nullptr); }
186
190 __stk_forceinline bool IsSleeping() const override { return (m_time_sleep < 0); }
191
196
200 TId GetTid() const { return GetTidFromUserTask(m_user); }
201
206 void Wake() override
207 {
209
210 // wakeup on a next cycle
211 m_time_sleep = -1;
212
213 // notify kernel that this task, even if was going to sleep will be woken on a next tick
214 if ((m_state & STATE_SLEEP_PENDING) != 0U)
215 {
217 }
218 }
219
223 void SetCurrentWeight(Weight weight) override
224 {
225 if __stk_constexpr_cpp17 (TStrategy::WEIGHT_API)
226 {
227 m_rt_weight[0] = weight;
228 }
229 }
230
234 Weight GetWeight() const override
235 {
236 Weight static_weight;
237
238 if __stk_constexpr_cpp17 (TStrategy::PRIORITY_INHERITANCE_API)
239 {
240 if (m_rt_weight[0] != NO_WEIGHT)
241 {
242 static_weight = m_rt_weight[0];
243 }
244 else
245 {
246 static_weight = GetBaseWeight();
247 }
248 }
249 else
250 {
251 static_weight = GetBaseWeight();
252 }
253
254 return static_weight;
255 }
256
260 Weight GetBaseWeight() const override
261 {
262 Weight base_weight;
263
264 if __stk_constexpr_cpp17 (TStrategy::WEIGHT_API)
265 {
266 // run-time override set via IKernelService::SetWeight() takes precedence
267 base_weight = (m_base_weight[0] != NO_WEIGHT ? m_base_weight[0] : m_user->GetWeight());
268 }
269 else
270 {
271 base_weight = DEFAULT_WEIGHT;
272 }
273
274 return base_weight;
275 }
276
281 {
282 if __stk_constexpr_cpp17 (TStrategy::WEIGHT_API)
283 {
284 m_base_weight[0] = weight;
285 }
286 else
287 {
288 STK_UNUSED(weight);
289 }
290 }
291
297 Weight GetCurrentWeight() const override
298 {
299 Weight cur_weight;
300
301 if __stk_constexpr_cpp17 (TStrategy::WEIGHT_API)
302 {
303 cur_weight = m_rt_weight[0];
304 }
305 else
306 {
307 cur_weight = DEFAULT_WEIGHT;
308 }
309
310 return cur_weight;
311 }
312
317 Timeout GetHrtPeriodicity() const override
318 {
320
321 Timeout to;
322
324 {
325 to = m_hrt[0].periodicity;
326 }
327 else
328 {
329 to = 0;
330 }
331
332 return to;
333 }
334
340 Timeout GetHrtDeadline() const override
341 {
343
344 Timeout deadline;
345
347 {
348 deadline = m_hrt[0].deadline;
349 }
350 else
351 {
352 deadline = 0;
353 }
354
355 return deadline;
356 }
357
366 {
369
370 Timeout relative_deadline;
371
373 {
374 relative_deadline = (m_hrt[0].deadline - m_hrt[0].duration);
375 }
376 else
377 {
378 relative_deadline = 0;
379 }
380
381 return relative_deadline;
382 }
383
385 {
386 // note: task sleep time is negative
388
390 {
391 // likely task is sleeping during sync operation (see Wait)
392 if (m_wait_obj->IsWaiting())
393 {
394 // note: sync wait time is positive
395 task_sleep = m_wait_obj->m_time_wait;
396
397 // we shall account for only valid time (when task is waiting during sync operation)
398 if (task_sleep > NO_WAIT)
399 {
400 sleep_ticks = Min(sleep_ticks, task_sleep);
401 }
402 }
403 else
404 {
405 sleep_ticks = Min(sleep_ticks, task_sleep);
406 }
407 }
408 else
409 {
410 sleep_ticks = Min(sleep_ticks, task_sleep);
411 }
412
413 // clamp to [1, STK_TICKLESS_TICKS_MAX] range
414 return Max<Timeout>(1, sleep_ticks);
415 }
416
417 protected:
422
428 struct SrtInfo
429 {
431 {}
432
435 void Clear()
436 {
437 add_task_req = nullptr;
438 }
439
446 };
447
452 struct HrtInfo
453 {
454 HrtInfo() : periodicity(0), deadline(0), duration(0), done(false)
455 {}
456
459 void Clear()
460 {
461 periodicity = 0;
462 deadline = 0;
463 duration = 0;
464 done = false;
465 }
466
470 volatile bool done;
471 };
472
479 struct WaitObject final : public IWaitObject
480 {
481 explicit WaitObject() : m_task(nullptr), m_sync_obj(nullptr), m_time_wait(NO_WAIT),
483 {}
484
489
496 {
498 };
499
503 TId GetTid() const override { return m_task->GetTid(); }
504
509 EState GetState() const override { return m_state; }
510
514 bool IsWaiting() const { return (m_sync_obj != nullptr); }
515
521 {
523
524 switch (GetState())
525 {
526 case STATE_TIMEOUT:
527 return WAIT_RESULT_TIMEOUT;
528 case STATE_CANCELLED:
530 default: {
534 return WAIT_RESULT_SIGNAL; }
535 }
536 }
537
542 {
544
546 }
547
553 void Wake(bool timeout) override
554 {
556
557 m_state = (timeout ? STATE_TIMEOUT : STATE_SIGNALED);
559
560 m_sync_obj->RemoveWaitObject(this);
561 m_sync_obj = nullptr;
562
563 return m_task->Wake();
564 }
565
572 bool Tick(Timeout elapsed_ticks) override
573 {
575 {
576 if (m_state == STATE_WAIT)
577 {
578 m_time_wait -= elapsed_ticks;
579
580 if (m_time_wait <= NO_WAIT)
581 {
583 }
584 }
585 }
586
587 return (m_state == STATE_WAIT);
588 }
589
597 void SetupWait(ISyncObject *sync_obj, Timeout timeout)
598 {
600
601 m_sync_obj = sync_obj;
602 m_time_wait = timeout;
604
605 sync_obj->AddWaitObject(this);
606 }
607
616 {
617 if (IsWaiting())
618 {
620
621 m_sync_obj->RemoveWaitObject(this);
622 m_sync_obj = nullptr;
623 }
624 }
625
637 void Cancel()
638 {
640
643
644 m_sync_obj->RemoveWaitObject(this);
645 m_sync_obj = nullptr;
646
647 m_task->Wake();
648 }
649
653 volatile EState m_state;
654 };
655
660 void Bind(TPlatform *platform, ITask *user_task)
661 {
662 // bind user task (GetTid depends on m_user)
663 m_user = user_task;
664
665 // set access mode for this stack
666 m_stack.access_mode = user_task->GetAccessMode();
667
668 // set task id for tracking purpose
669 #if STK_STACK_NEEDS_TASK_ID
670 m_stack.tid = GetTid();
672 #endif
673
674 // init stack of the user task
675 platform->InitStack(STACK_USER_TASK, &m_stack, user_task, user_task);
676
677 // initialize current weight to NO_WEIGHT for priority inheritance mechanism
678 if __stk_constexpr_cpp17 (TStrategy::PRIORITY_INHERITANCE_API)
679 {
681 }
682
683 // no run-time override of the base weight: ITask::GetWeight() is used
684 if __stk_constexpr_cpp17 (TStrategy::WEIGHT_API)
685 {
687 }
688 }
689
693 void Unbind()
694 {
696 {
697 // should be freed from waiting on task exit
698 STK_ASSERT(!m_wait_obj->IsWaiting());
699 }
700
701 m_user = nullptr;
702 m_stack = {};
704 m_time_sleep = 0;
705
707 {
708 m_hrt[0].Clear();
709 }
710 else
711 {
712 m_srt->Clear();
713 }
714 }
715
719 {
720 // if blocked on a sync object, detach first - otherwise the WaitObject stays linked
721 // in the sync object's wait list after Unbind() frees this slot, and a later
722 // Wake()/WakeAll() would corrupt whatever task the slot gets rebound to
724 {
725 m_wait_obj->CancelNoWake();
726 }
727
728 // make this task sleeping to switch it out from scheduling process
730
731 // mark it as done HRT task
733 {
735 }
736
737 // mark it as pending for removal
739 }
740
743 bool IsPendingRemoval() const { return ((m_state & STATE_REMOVE_PENDING) != 0U); }
744
748 bool IsMemoryOfSP(Word SP) const
749 {
750 bool is_match = false;
751
752 const Word start = hw::PtrToWord(m_user->GetStack());
753 const Word end = start + (m_user->GetStackSize() * sizeof(Word));
754
755 if ((SP >= start) && (SP <= end))
756 {
757 is_match = true;
758 }
759 #if STK_TZ_SECURE // lookup Secure memory region too when on a Secure side
760 else
761 {
762 IStackMemory *const secure_mem = m_user->GetSecureStackMemory();
763
764 if (secure_mem != nullptr)
765 {
766 const Word s_start = hw::PtrToWord(secure_mem->GetStack());
767 const Word s_end = s_start + (secure_mem->GetStackSize() * sizeof(Word));
768
769 if ((SP >= s_start) && (SP <= s_end))
770 {
771 is_match = true;
772 }
773 }
774 }
775 #endif
776
777 return is_match;
778 }
779
786 void HrtInit(Timeout periodicity_tc, Timeout deadline_tc, Timeout start_delay_tc)
787 {
788 STK_ASSERT(periodicity_tc > 0);
789 STK_ASSERT(deadline_tc > 0);
790 STK_ASSERT(start_delay_tc >= 0);
791 STK_ASSERT(periodicity_tc < INT32_MAX);
792 STK_ASSERT(deadline_tc < INT32_MAX);
793
794 m_hrt[0].periodicity = periodicity_tc;
795 m_hrt[0].deadline = deadline_tc;
796
797 if (start_delay_tc > 0)
798 {
799 ScheduleSleep(start_delay_tc);
800 }
801 }
802
807
812 {
813 const Timeout duration = m_hrt[0].duration;
814
815 STK_ASSERT(duration >= 0);
816
817 const Timeout sleep = m_hrt[0].periodicity - duration;
818 if (sleep > 0)
819 {
820 ScheduleSleep(sleep);
821 }
822
823 m_hrt[0].duration = 0;
824 m_hrt[0].done = false;
825 }
826
832 {
833 const Timeout duration = m_hrt[0].duration;
834
835 STK_ASSERT(duration >= 0);
837
838 m_user->OnDeadlineMissed(duration);
839 platform->ProcessHardFault();
840 }
841
846 {
847 m_hrt[0].done = true;
848 __stk_full_memfence();
849 }
850
854 bool HrtIsDeadlineMissed(Timeout duration) const
855 {
856 return (duration > m_hrt[0].deadline);
857 }
858
869 {
870 STK_ASSERT(ticks > 0);
871
872 // set state first as kernel checks it when task IsSleeping
873 if (!IsSleeping())
874 {
876 }
877
878 m_time_sleep = -ticks;
879
880 __stk_full_memfence();
881 }
882
886 {
887 if __stk_constexpr_cpp17 (TStrategy::NOSLEEP_YIELD_API)
888 {
890
891 __stk_full_memfence();
892 }
893 }
894
897 void BusyWaitWhileYielding(Kernel *kernel) const
898 {
899 if __stk_constexpr_cpp17 (TStrategy::NOSLEEP_YIELD_API)
900 {
901 kernel->m_platform.ForceContextSwitch(GetTid());
902
903 while ((m_state & STATE_YIELD_PENDING) != 0U)
904 {
905 __stk_relax_cpu();
906 }
907 }
908 }
909
912 void BusyWaitWhileSleeping(Kernel *kernel) const
913 {
914 kernel->m_platform.ForceContextSwitch(GetTid());
915
916 while (IsSleeping())
917 {
918 __stk_relax_cpu();
919 }
920 }
921
926
929 volatile uint32_t m_state;
936 };
937
945 class KernelService final : public IKernelService
946 {
947 friend class Kernel;
948
949 public:
950 TId GetTid() const override { return m_kernel->m_platform.GetTid(); }
951
952 Ticks GetTicks() const override { return hw::ReadVolatile64(&m_ticks); }
953
954 uint32_t GetTickResolution() const override { return m_kernel->m_platform.GetTickResolution(); }
955
956 Cycles GetSysTimerCount() const override { return m_kernel->m_platform.GetSysTimerCount(); }
957
958 uint32_t GetSysTimerFrequency() const override { return m_kernel->m_platform.GetSysTimerFrequency(); }
959
960 void Delay(Timeout ticks) override
961 {
963 STK_ASSERT(ticks >= 0);
964
965 Ticks now = GetTicks();
966 const Ticks deadline = now + ticks;
967 STK_ASSERT(deadline >= now);
968
969 for (; now < deadline; now = GetTicks())
970 {
971 __stk_relax_cpu();
972 }
973 }
974
975 void Sleep(Timeout ticks) override
976 {
978 STK_ASSERT(ticks >= 0);
979
981 {
982 m_kernel->m_platform.Sleep(ticks);
983 }
984 else
985 {
986 // sleeping is not supported in HRT mode, task will sleep according to its periodicity and workload
987 STK_ASSERT(false);
988 }
989 }
990
991 bool SleepUntil(Ticks timestamp) override
992 {
994
996 {
997 return m_kernel->m_platform.SleepUntil(timestamp);
998 }
999 else
1000 {
1001 // sleeping is not supported in HRT mode, task will sleep according to its periodicity and workload
1002 STK_ASSERT(false);
1003 return false;
1004 }
1005 }
1006
1007 void SleepCancel(TId task_id) override
1008 {
1010 {
1011 m_kernel->OnTaskSleepCancel(task_id);
1012 }
1013 }
1014
1015 void SwitchToNext() override
1016 {
1018
1019 m_kernel->m_platform.SwitchToNext();
1020 }
1021
1022 EWaitResult Wait(ISyncObject *sobj, IMutex *mutex, Timeout ticks) override
1023 {
1025 {
1026 return m_kernel->m_platform.Wait(sobj, mutex, ticks);
1027 }
1028 else
1029 {
1030 STK_ASSERT(false);
1031 return WAIT_RESULT_FAIL;
1032 }
1033 }
1034
1035 void Wake(ISyncObject *sobj, bool all) override
1036 {
1038 {
1039 if (all)
1040 {
1042 }
1043 else
1044 {
1046 }
1047 }
1048 else
1049 {
1050 STK_ASSERT(false);
1051 }
1052 }
1053
1054 Timeout Suspend() override
1055 {
1057 {
1058 return m_kernel->m_platform.Suspend();
1059 }
1060 else
1061 {
1062 STK_ASSERT(false);
1063 return 0;
1064 }
1065 }
1066
1067 void Resume(Timeout elapsed_ticks) override
1068 {
1070 {
1071 return m_kernel->m_platform.Resume(elapsed_ticks);
1072 }
1073 else
1074 {
1075 STK_ASSERT(false);
1076 }
1077 }
1078
1079 void InheritWeight(TId tid, Weight weight) override
1080 {
1081 if __stk_constexpr_cpp17 (TStrategy::WEIGHT_API && TStrategy::PRIORITY_INHERITANCE_API)
1082 {
1083 m_kernel->OnInheritWeight(tid, weight);
1084 }
1085 }
1086
1087 void RestoreWeight(TId tid, ISyncObject *sobj) override
1088 {
1089 if __stk_constexpr_cpp17 (TStrategy::WEIGHT_API && TStrategy::PRIORITY_INHERITANCE_API)
1090 {
1091 m_kernel->OnRestoreWeight(tid, sobj);
1092 }
1093 }
1094
1095 Weight SetWeight(TId tid, Weight weight) override
1096 {
1097 if __stk_constexpr_cpp17 (TStrategy::WEIGHT_API && TStrategy::PRIORITY_INHERITANCE_API)
1098 {
1100
1101 return m_kernel->OnSetWeight(tid, weight);
1102 }
1103 else
1104 {
1105 // strategy must handle OnTaskWeightChange() events
1106 STK_UNUSED(tid);
1107 STK_UNUSED(weight);
1108 STK_ASSERT(false);
1109 return NO_WEIGHT;
1110 }
1111 }
1112
1113 private:
1117 explicit KernelService() : m_kernel(nullptr), m_ticks(0)
1118 {}
1119
1124
1130 void Initialize(Kernel *kernel)
1131 {
1132 m_kernel = kernel;
1133 }
1134
1138 void IncrementTicks(Ticks advance)
1139 {
1140 // using WriteVolatile64() to guarantee correct lockless reading order by ReadVolatile64
1141 hw::WriteVolatile64(&m_ticks, m_ticks + advance);
1142 }
1143
1145 volatile Ticks m_ticks;
1146 };
1147
1148public:
1151 static constexpr size_t TASKS_MAX = TSize;
1152
1163 {
1164 #ifdef _DEBUG
1165 // TPlatform must inherit IPlatform
1166 IPlatform *platform = &m_platform;
1167 STK_UNUSED(platform);
1168
1169 // TStrategy must inherit ITaskSwitchStrategy
1170 ITaskSwitchStrategy *strategy = &m_strategy;
1171 STK_UNUSED(strategy);
1172 #endif
1173
1174 #if !STK_TICKLESS_IDLE
1175 STK_STATIC_ASSERT_DESC(((TMode & KERNEL_TICKLESS) == 0U),
1176 "STK_TICKLESS_IDLE must be defined to 1 for KERNEL_TICKLESS");
1177 #endif
1178 }
1179
1184
1194 __stk_attr_noinline void Initialize(uint32_t resolution_us = PERIODICITY_DEFAULT) override
1195 {
1196 STK_ASSERT(resolution_us != 0);
1197 STK_ASSERT(resolution_us <= PERIODICITY_MAX);
1199
1200 // reinitialize key state variables
1201 m_task_now = nullptr;
1204
1205 // exit trap is required only for KERNEL_DYNAMIC mode
1206 Stack *exit_trap;
1208 {
1209 exit_trap = &m_exit_trap[0].stack;
1210 }
1211 else
1212 {
1213 exit_trap = nullptr;
1214 }
1215
1216 m_service.Initialize(this);
1217 m_platform.Initialize(this, &m_service, resolution_us, exit_trap);
1218
1219 // now ready to Start()
1221 }
1222
1231 __stk_attr_noinline void AddTask(ITask *user_task) override
1232 {
1234 {
1235 STK_ASSERT(user_task != nullptr);
1237
1238 // when started the operation must be serialized by switching out from processing until
1239 // kernel processes this request
1240 if (IsStarted())
1241 {
1243 {
1244 RequestAddTask(user_task);
1245 }
1246 else
1247 {
1248 STK_ASSERT(false);
1249 }
1250 }
1251 else
1252 {
1253 AllocateAndAddNewTask(user_task);
1254 }
1255 }
1256 else
1257 {
1258 STK_ASSERT(false);
1259 }
1260 }
1261
1270 __stk_attr_noinline void AddTask(ITask *user_task, Timeout periodicity_tc, Timeout deadline_tc,
1271 Timeout start_delay_tc) override
1272 {
1274 {
1275 STK_ASSERT(user_task != nullptr);
1278
1279 HrtAllocateAndAddNewTask(user_task, periodicity_tc, deadline_tc, start_delay_tc);
1280 }
1281 else
1282 {
1283 STK_ASSERT(false);
1284 }
1285 }
1286
1295 __stk_attr_noinline void RemoveTask(ITask *user_task) override
1296 {
1298 {
1299 STK_ASSERT(user_task != nullptr);
1301
1302 KernelTask *const task = FindTaskByUserTask(user_task);
1303 if (task != nullptr)
1304 {
1305 RemoveTask(task);
1306 }
1307 }
1308 else
1309 {
1310 // kernel operating mode must be KERNEL_DYNAMIC for tasks to be able to be removed
1311 STK_ASSERT(false);
1312 }
1313 }
1314
1321 {
1323 {
1324 STK_ASSERT(user_task != nullptr);
1326
1328
1329 KernelTask *const task = FindTaskByUserTask(user_task);
1330 if (task != nullptr)
1331 {
1332 task->ScheduleRemoval();
1333 }
1334 }
1335 else
1336 {
1337 // kernel operating mode must be KERNEL_DYNAMIC for tasks to be able to be removed
1338 STK_ASSERT(false);
1339 }
1340 }
1341
1348 void SuspendTask(ITask *user_task, bool &suspended) override
1349 {
1350 STK_ASSERT(user_task != nullptr);
1351
1352 bool self = false;
1353
1354 // avoid race with OnTick
1355 {
1357
1358 KernelTask *const task = FindTaskByUserTask(user_task);
1359 STK_ASSERT(task != nullptr);
1360
1361 // nesting not supported, must not be suspended by SuspendTask
1362 STK_ASSERT((task->m_state & KernelTask::STATE_SUSPENDED) == 0U);
1363
1364 // only suspend if the task is currently awake: if it is already sleeping
1365 // (e.g. blocked on a mutex or timed Sleep), do not overwrite m_time_sleep,
1366 // that would corrupt the original sleep state and, for sync-object waits,
1367 // would interfere with WaitObject::Tick()
1368 suspended = !task->IsSleeping();
1369 if (suspended == true)
1370 {
1371 task->ScheduleSleep(WAIT_INFINITE);
1372
1373 // check if suspending self
1374 self = (task == m_task_now);
1375
1376 // trace blocked via Suspend
1377 if (self)
1378 {
1379 #if STK_SEGGER_SYSVIEW
1380 SEGGER_SYSVIEW_OnTaskStopReady(task->GetUserStackPtr()->tid, TRACE_EVENT_SUSPEND);
1381 #endif
1382 }
1383
1384 // set state
1385 task->m_state |= KernelTask::STATE_SUSPENDED;
1386 }
1387 }
1388
1389 // note: we do not spin long here, kernel will switch this task out from scheduling on the next tick
1390 if (self)
1391 {
1392 m_task_now->BusyWaitWhileSleeping(this);
1393 }
1394 }
1395
1401 void ResumeTask(ITask *user_task) override
1402 {
1403 STK_ASSERT(user_task != nullptr);
1404
1405 // avoid race with OnTick
1407
1408 KernelTask *const task = FindTaskByUserTask(user_task);
1409 STK_ASSERT(task != nullptr);
1410
1411 // must be suspended by SuspendTask, otherwise noop
1412 if ((task->m_state & KernelTask::STATE_SUSPENDED) != 0U)
1413 {
1414 // clear state
1415 task->m_state &= ~KernelTask::STATE_SUSPENDED;
1416
1417 // wake up
1418 if (task->IsSleeping())
1419 {
1420 task->Wake();
1421 }
1422 }
1423 }
1424
1428 void CancelTaskWait(ITask *user_task) override
1429 {
1430 STK_ASSERT(user_task != nullptr);
1431
1433 {
1434 // avoid race with OnTick
1436
1437 KernelTask *const task = FindTaskByUserTask(user_task);
1438 STK_ASSERT(task != nullptr);
1439
1440 // only cancel if actually blocked in OnTaskWait() on a sync object; no-op otherwise
1441 // (e.g. task is running, suspended via SuspendTask(), or plainly sleeping)
1442 if (task->m_wait_obj->IsWaiting())
1443 {
1444 task->m_wait_obj->Cancel();
1445 }
1446 }
1447 else
1448 {
1449 // kernel operating mode must include KERNEL_SYNC for tasks to be able to wait
1450 STK_ASSERT(false);
1451 }
1452 }
1453
1459 {
1460 size_t count = 0U;
1461 const size_t limit = Min(tasks.GetSize(), TASKS_MAX);
1462
1463 // avoid race with OnTick
1465
1466 for (size_t i = 0U; i < limit; ++i)
1467 {
1468 KernelTask *const task = &m_task_storage[i];
1469 if (task->IsBusy())
1470 {
1471 tasks[count++] = task;
1472 }
1473 }
1474
1475 return count;
1476 }
1477
1482 size_t EnumerateTasks(ArrayView<ITask *> user_tasks) override
1483 {
1484 size_t count = 0U;
1485 const size_t limit = Min(user_tasks.GetSize(), TASKS_MAX);
1486
1487 // avoid race with OnTick
1489
1490 for (size_t i = 0U; i < limit; ++i)
1491 {
1492 KernelTask *const task = &m_task_storage[i];
1493 if (task->IsBusy())
1494 {
1495 user_tasks[count++] = task->GetUserTask();
1496 }
1497 }
1498
1499 return count;
1500 }
1501
1511 {
1513
1514 // stacks of the traps must be re-initilized on every subsequent Start
1515 InitTraps();
1516
1517 // start tracing
1518 #if STK_SEGGER_SYSVIEW
1519 SEGGER_SYSVIEW_Start();
1520 for (size_t i = 0U; i < TASKS_MAX; ++i)
1521 {
1522 KernelTask *task = &m_task_storage[i];
1523 if (task->IsBusy())
1524 {
1525 SendTaskTraceInfo(task);
1526 }
1527 }
1528 #endif
1529
1530 m_platform.Start();
1531 }
1532
1537 bool IsStarted() const
1538 {
1539 return (m_task_now != nullptr);
1540 }
1541
1545 IPlatform *GetPlatform() override { return &m_platform; }
1546
1551
1554 EKernelState GetState() const override { return m_kstate; }
1555
1556 // Used by tests only, not Public API:
1557#ifdef _STK_UNDER_TEST
1558 void ScheduleSleepOnActiveTask(Timeout ticks)
1559 {
1560 m_task_now->ScheduleSleep(ticks);
1561 }
1562#endif
1563
1564protected:
1578
1591
1598 static constexpr Timeout YIELD_TICKS = 1;
1599
1603 {
1604 return ((state > FSM_STATE_NONE) && (state < FSM_STATE_MAX));
1605 }
1606
1610 {
1611 // init stack for a Sleep trap
1612 {
1613 SleepTrapStack &sleep = m_sleep_trap[0];
1614
1615 SleepTrapStackMemory wrapper(&sleep.memory);
1616 sleep.stack.access_mode = ACCESS_PRIVILEGED;
1617 #if STK_STACK_NEEDS_TASK_ID
1618 sleep.stack.tid = SYS_TASK_ID_SLEEP;
1619 #endif
1620
1621 STK_UNUSED(m_platform.InitStack(STACK_SLEEP_TRAP, &sleep.stack, &wrapper, nullptr));
1622 }
1623
1624 // init stack for an Exit trap
1626 {
1627 ExitTrapStack &exit = m_exit_trap[0];
1628
1629 ExitTrapStackMemory wrapper(&exit.memory);
1630 exit.stack.access_mode = ACCESS_PRIVILEGED;
1631 #if STK_STACK_NEEDS_TASK_ID
1632 exit.stack.tid = SYS_TASK_ID_EXIT;
1633 #endif
1634
1635 STK_UNUSED(m_platform.InitStack(STACK_EXIT_TRAP, &exit.stack, &wrapper, nullptr));
1636 }
1637 }
1638
1643 KernelTask *AllocateNewTask(ITask *user_task)
1644 {
1645 // look for a free kernel task
1646 KernelTask *new_task = nullptr;
1647 for (size_t i = 0U; i < TASKS_MAX; ++i)
1648 {
1649 KernelTask *const task = &m_task_storage[i];
1650 if (task->IsBusy())
1651 {
1652 // avoid task collision
1653 STK_ASSERT(task->m_user != user_task);
1654
1655 // avoid stack collision
1656 STK_ASSERT(task->m_user->GetStack() != user_task->GetStack());
1657 }
1658 else
1659 if (new_task == nullptr)
1660 {
1661 new_task = task;
1662 #if defined(NDEBUG) && !defined(_STK_ASSERT_REDIRECT)
1663 break; // break if assertions are inactive and do not try to validate collision with existing tasks
1664 #endif
1665 }
1666 else
1667 {
1668 // noop, continue to the next slot
1669 }
1670 }
1671
1672 // if nullptr - exceeded max supported kernel task count, application design failure
1673 STK_ASSERT(new_task != nullptr);
1674
1675 new_task->Bind(&m_platform, user_task);
1676
1677 return new_task;
1678 }
1679
1683 void AddKernelTask(KernelTask *task)
1684 {
1685 #if STK_SEGGER_SYSVIEW
1686 // start tracing new task
1687 SEGGER_SYSVIEW_OnTaskCreate(task->GetUserStackPtr()->tid);
1688 if (IsStarted())
1689 {
1690 SendTaskTraceInfo(task);
1691 }
1692 #endif
1693
1694 m_strategy.AddTask(task);
1695 }
1696
1701 {
1702 KernelTask *const task = AllocateNewTask(user_task);
1703 STK_ASSERT(task != nullptr);
1704
1705 AddKernelTask(task);
1706 }
1707
1715 void HrtAllocateAndAddNewTask(ITask *user_task, Timeout periodicity_tc, Timeout deadline_tc, Timeout start_delay_tc)
1716 {
1717 KernelTask *const task = AllocateNewTask(user_task);
1718 STK_ASSERT(task != nullptr);
1719
1720 task->HrtInit(periodicity_tc, deadline_tc, start_delay_tc);
1721
1722 AddKernelTask(task);
1723 }
1724
1730 {
1731 KernelTask *const caller = FindTaskBySP(m_platform.GetCallerSP());
1732 STK_ASSERT(caller != nullptr);
1733
1734 typename KernelTask::AddTaskRequest req = { .user_task = user_task };
1735 caller->m_srt[0].add_task_req = &req;
1736
1737 // notify kernel
1739
1740 // switch out and wait for completion (due to context switch request could be processed here)
1741 if (caller->m_srt[0].add_task_req != nullptr)
1742 {
1743 m_service.SwitchToNext();
1744 }
1745
1746 // wait until request is consumed
1747 if __stk_constexpr_cpp17 (TStrategy::NOSLEEP_YIELD_API)
1748 {
1749 while (caller->m_srt[0].add_task_req != nullptr)
1750 {
1751 __stk_relax_cpu();
1752 }
1753 }
1754
1755 STK_ASSERT(caller->m_srt[0].add_task_req == nullptr);
1756 }
1757
1762 __stk_attr_noinline KernelTask *FindTaskByUserTask(const ITask *user_task)
1763 {
1764 KernelTask *found_task = nullptr;
1765
1766 for (size_t i = 0U; i < TASKS_MAX; ++i)
1767 {
1768 KernelTask *const task = &m_task_storage[i];
1769 if (task->GetUserTask() == user_task)
1770 {
1771 found_task = task;
1772 break;
1773 }
1774 }
1775
1776 return found_task;
1777 }
1778
1783 KernelTask *FindTaskByStack(const Stack *stack)
1784 {
1785 KernelTask *found_task = nullptr;
1786
1787 for (size_t i = 0U; i < TASKS_MAX; ++i)
1788 {
1789 KernelTask *const task = &m_task_storage[i];
1790 if (task->GetUserStackPtr() == stack)
1791 {
1792 found_task = task;
1793 break;
1794 }
1795 }
1796
1797 return found_task;
1798 }
1799
1805 {
1806 STK_ASSERT(m_task_now != nullptr);
1807
1808 KernelTask *found_task = nullptr;
1809
1810 if (m_task_now->IsMemoryOfSP(SP))
1811 {
1812 found_task = m_task_now;
1813 }
1814 else
1815 {
1816 for (size_t i = 0U; i < TASKS_MAX; ++i)
1817 {
1818 KernelTask *const task = &m_task_storage[i];
1819
1820 // skip finished tasks (applicable only for KERNEL_DYNAMIC mode)
1822 {
1823 if (!task->IsBusy())
1824 {
1825 continue;
1826 }
1827 }
1828
1829 if (task->IsMemoryOfSP(SP))
1830 {
1831 found_task = task;
1832 break;
1833 }
1834 }
1835 }
1836
1837 return found_task;
1838 }
1839
1844 void RemoveTask(KernelTask *task)
1845 {
1846 STK_ASSERT(task != nullptr);
1847
1848 #if STK_SEGGER_SYSVIEW
1849 SEGGER_SYSVIEW_OnTaskTerminate(task->GetUserStackPtr()->tid);
1850 #endif
1851
1852 // notify task about pending exit
1853 task->GetUserTask()->OnExit();
1854
1855 m_strategy.RemoveTask(task);
1856 task->Unbind();
1857 }
1858
1869 __stk_attr_noinline void OnStart(Stack *&active) override
1870 {
1871 STK_ASSERT(m_strategy.GetSize() != 0);
1872
1873 // iterate tasks and generate OnTaskSleep for a strategy for all initially sleeping tasks
1874 for (size_t i = 0U; i < TASKS_MAX; ++i)
1875 {
1876 KernelTask *const task = &m_task_storage[i];
1877
1878 if ((task->m_state & KernelTask::STATE_SLEEP_PENDING) != 0U)
1879 {
1880 STK_ASSERT(task->IsSleeping());
1881
1882 task->m_state &= ~KernelTask::STATE_SLEEP_PENDING;
1883
1884 // notify strategy that task is sleeping
1885 if __stk_constexpr_cpp17 (TStrategy::SLEEP_EVENT_API)
1886 {
1887 m_strategy.OnTaskSleep(task);
1888 }
1889 }
1890 }
1891
1892 // get initial state and first task
1893 {
1895
1896 KernelTask *next = nullptr;
1898
1899 // expecting only SLEEPING or SWITCHING states
1901
1903 {
1904 m_task_now = next;
1905 active = next->GetUserStackPtr();
1906
1908 {
1909 next->HrtOnSwitchedIn();
1910 }
1911 }
1912 else
1914 {
1916 active = &m_sleep_trap[0].stack;
1917 }
1918 else
1919 {
1920 // unexpected state
1922 }
1923 }
1924
1925 // is in running state
1927 }
1928
1935 {
1937 {
1939
1940 // is in stopped state, i.e. is ready to Start() again
1942 }
1943 }
1944
1961 bool OnTick(Stack *&idle, Stack *&active
1962 #if STK_TICKLESS_IDLE
1963 , Timeout &ticks
1964 #endif
1965 ) override
1966 {
1967 #if !STK_TICKLESS_IDLE
1968 // in non-tickless mode kernel is advancing strictly by 1 tick on every OnTick call
1969 enum { ticks = 1 };
1970 #endif
1971
1972 // advance internal timestamp
1973 m_service.IncrementTicks(ticks);
1974
1975 // consume elapsed and update to ticks to sleep
1976 #if STK_TICKLESS_IDLE
1977 ticks = (
1978 #else
1979 // notify compiler that we ignore a return value of UpdateTasks
1980 STK_UNUSED(
1981 #endif
1982 UpdateTasks(ticks));
1983
1984 // decide on a context switch
1985 return UpdateFsmState(idle, active);
1986 }
1987
1988 bool OnForceContextSwitch(TId id, Stack *&idle, Stack *&active)
1989 {
1990 bool switch_context = false;
1991 KernelTask *const task = m_task_now;
1992
1993 STK_ASSERT(task != nullptr);
1994
1995 // note: called from inside ISR, therefore protection by critical section is not needed
1996
1997 if (task->GetTid() == id)
1998 {
1999 // current task is busy-waiting to be de-scheduled
2000 if (task->IsSleeping())
2001 {
2002 if ((task->m_state & KernelTask::STATE_SLEEP_PENDING) != 0U)
2003 {
2005
2006 switch_context = UpdateFsmState(idle, active);
2007 }
2008 }
2009 else
2010 {
2011 if __stk_constexpr_cpp17 (TStrategy::NOSLEEP_YIELD_API)
2012 {
2013 // current task yielded and stays runnable: let the strategy pick the next task
2014 if ((task->m_state & KernelTask::STATE_YIELD_PENDING) != 0U)
2015 {
2017
2018 switch_context = UpdateFsmState(idle, active);
2019 }
2020 }
2021 }
2022 }
2023
2024 return switch_context;
2025 }
2026
2027 void OnTaskSwitch(Word caller_SP) override
2028 {
2029 // let the strategy handle the yield without putting the task to sleep (e.g. to hand
2030 // the CPU to same-priority peers only), not applicable in HRT mode where Yield()
2031 // means work completion
2032 if __stk_constexpr_cpp17 (TStrategy::NOSLEEP_YIELD_API && !IsHrtMode())
2033 {
2034 KernelTask *const task = FindTaskBySP(caller_SP);
2035 STK_ASSERT(task != nullptr);
2036
2037 if (task != nullptr)
2038 {
2039 bool handled = false;
2040 {
2042
2043 if (!task->IsSleeping())
2044 {
2045 if (m_strategy.OnTaskYield(task))
2046 {
2047 task->ScheduleYield();
2048 handled = true;
2049 }
2050 }
2051 }
2052
2053 if (handled)
2054 {
2055 task->BusyWaitWhileYielding(this);
2056 }
2057 else
2058 {
2059 OnTaskSleep(caller_SP, YIELD_TICKS);
2060 }
2061 }
2062 else
2063 {
2064 // defensive: do nothing on null task
2065 }
2066 }
2067 else
2068 {
2069 OnTaskSleep(caller_SP, YIELD_TICKS);
2070 }
2071 }
2072
2073 void OnTaskSleep(Word caller_SP, Timeout ticks) override
2074 {
2075 KernelTask *const task = FindTaskBySP(caller_SP);
2076 STK_ASSERT(task != nullptr);
2077
2078 // make change to HRT state and sleep time atomic
2079 {
2081
2083 {
2084 task->HrtOnWorkCompleted();
2085 }
2086
2087 if (ticks > 0)
2088 {
2089 task->ScheduleSleep(ticks);
2090
2091 // trace blocked on Sleep
2092 #if STK_SEGGER_SYSVIEW
2093 SEGGER_SYSVIEW_OnTaskStopReady(task->GetUserStackPtr()->tid, TRACE_EVENT_SLEEP);
2094 #endif
2095 }
2096 }
2097
2098 // note: we do not spin long here, kernel will switch this task out from scheduling on the next tick
2099 task->BusyWaitWhileSleeping(this);
2100 }
2101
2102 bool OnTaskSleepUntil(Word caller_SP, Ticks timestamp) override
2103 {
2104 KernelTask *const task = FindTaskBySP(caller_SP);
2105 STK_ASSERT(task != nullptr);
2106
2107 bool result = true;
2108
2109 // make change to HRT state and sleep time atomic
2110 {
2112
2113 // calculate signed delta (handles wrap-around correctly)
2114 const Ticks delta = timestamp - m_service.m_ticks;
2115
2116 if (delta > 0)
2117 {
2118 const Ticks infinite_ticks = WAIT_INFINITE;
2119 task->ScheduleSleep(static_cast<Timeout>(Min(delta, infinite_ticks)));
2120
2121 // trace blocked on on SleepUntil
2122 #if STK_SEGGER_SYSVIEW
2123 SEGGER_SYSVIEW_OnTaskStopReady(task->GetUserStackPtr()->tid, TRACE_EVENT_SLEEP);
2124 #endif
2125 }
2126 else
2127 {
2128 result = false; // deadline already hit or passed
2129 }
2130 }
2131
2132 // note: we do not spin long here, kernel will switch this task out from scheduling on the next tick
2133 if (result)
2134 {
2135 task->BusyWaitWhileSleeping(this);
2136 }
2137
2138 return result;
2139 }
2140
2142 {
2143 KernelTask *const task = FindTaskByUserTask(GetUserTaskFromTid(task_id));
2144 if (task != nullptr)
2145 {
2147
2148 if (task->IsSleeping())
2149 {
2150 task->Wake();
2151 }
2152 }
2153 }
2154
2155 void OnTaskExit(Stack *stack) override
2156 {
2158 {
2159 KernelTask *const task = FindTaskByStack(stack);
2160 STK_ASSERT(task != nullptr);
2161
2162 // task is being stopped and will terminate
2163 #if STK_SEGGER_SYSVIEW
2164 SEGGER_SYSVIEW_OnTaskStopExec();
2165 #endif
2166
2167 // notify kernel to execute removal
2168 {
2169 // make change to HRT/sleep/removal state atomic (same as OnTaskSleep/OnTaskSleepUntil)
2171
2172 task->ScheduleRemoval();
2173 }
2174 }
2175 else
2176 {
2177 // kernel operating mode must be KERNEL_DYNAMIC for tasks to be able to exit
2179 }
2180 }
2181
2182 EWaitResult OnTaskWait(Word caller_SP, ISyncObject *sync_obj, IMutex *mutex, Timeout timeout) override
2183 {
2185 {
2186 STK_ASSERT(timeout != 0); // API contract: caller must not be in ISR
2187 STK_ASSERT(sync_obj != nullptr); // API contract: ISyncObject instance must be provided
2188 STK_ASSERT(mutex != nullptr); // API contract: IMutex instance must be provided
2189 STK_ASSERT((sync_obj->GetHead() == nullptr) || (sync_obj->GetHead() == &m_sync_list[0]));
2190
2191 KernelTask *const task = FindTaskBySP(caller_SP);
2192 STK_ASSERT(task != nullptr);
2193
2194 // configure waiting
2195 task->m_wait_obj->SetupWait(sync_obj, timeout);
2196
2197 // register ISyncObject if not yet
2198 if (sync_obj->GetHead() == nullptr)
2199 {
2200 m_sync_list->LinkBack(sync_obj);
2201 }
2202
2203 // start sleeping infinitely, we rely on a Wake call via WaitObject
2204 task->ScheduleSleep(WAIT_INFINITE);
2205
2206 // trace blocked on sync object's Wait
2207 #if STK_SEGGER_SYSVIEW
2208 SEGGER_SYSVIEW_OnTaskStopReady(task->GetUserStackPtr()->tid, TRACE_EVENT_WAIT);
2209 #endif
2210
2211 // unlock mutex locked externally, so that we could wait in a busy-waiting loop
2212 mutex->Unlock();
2213
2214 // note: we do not spin long here, kernel will switch this task out from scheduling on the next tick
2215 task->BusyWaitWhileSleeping(this);
2216
2217 // extract & clear this wait's result BEFORE mutex->Lock(): a contended Lock() recurses
2218 // into OnTaskWait() and reuses this same per-task WaitObject, which would otherwise
2219 // silently overwrite/clear our still-unread result
2220 const EWaitResult result = task->m_wait_obj->GetWaitResult();
2221 task->m_wait_obj->ClearState();
2222
2223 // re-lock mutex when returning to the task's execution space
2224 mutex->Lock();
2225
2226 return result;
2227 }
2228 else
2229 {
2230 STK_ASSERT(false);
2231 return WAIT_RESULT_FAIL;
2232 }
2233 }
2234
2235 TId OnGetTid(Word caller_SP) override
2236 {
2237 KernelTask *const task = FindTaskBySP(caller_SP);
2238 STK_ASSERT(task != nullptr);
2239
2240 return task->GetTid();
2241 }
2242
2243 void OnSuspend(bool suspended) override
2244 {
2245 // toggle kernel state
2246 if (suspended)
2247 {
2248 if (m_kstate == KSTATE_RUNNING)
2249 {
2251 }
2252 }
2253 else
2254 {
2255 if (m_kstate == KSTATE_SUSPENDED)
2256 {
2258 }
2259 }
2260
2261 // force yield for a currently active task
2262 if (!m_task_now->IsSleeping())
2263 {
2264 m_task_now->ScheduleSleep(YIELD_TICKS);
2265 }
2266 }
2267
2268 void OnInheritWeight(TId tid, Weight weight)
2269 {
2270 STK_ASSERT(tid != TID_NONE);
2271 STK_ASSERT(TStrategy::WEIGHT_API && TStrategy::PRIORITY_INHERITANCE_API);
2272
2273 if (weight != NO_WEIGHT)
2274 {
2275 KernelTask *const task = FindTaskByUserTask(GetUserTaskFromTid(tid));
2276 STK_ASSERT(task != nullptr);
2277
2278 const Weight prev_weight = task->GetWeight();
2279
2280 if (prev_weight < weight)
2281 {
2282 task->SetCurrentWeight(weight);
2283 m_strategy.OnTaskWeightChange(task, prev_weight);
2284 }
2285 }
2286 }
2287
2289 {
2290 STK_ASSERT(tid != TID_NONE);
2291 STK_ASSERT(TStrategy::WEIGHT_API && TStrategy::PRIORITY_INHERITANCE_API);
2292 STK_ASSERT(weight != NO_WEIGHT);
2293
2294 KernelTask *const task = FindTaskByUserTask(GetUserTaskFromTid(tid));
2295 STK_ASSERT(task != nullptr);
2296
2297 const Weight prev_weight = task->GetWeight(); // effective weight (base or inherited)
2298 const Weight prev_base = task->GetBaseWeight();
2299
2300 task->SetBaseWeight(weight);
2301
2302 // an inherited weight that does not exceed the new base weight is no longer a boost
2303 if ((task->GetCurrentWeight() != NO_WEIGHT) && (task->GetCurrentWeight() <= weight))
2304 {
2305 task->SetCurrentWeight(NO_WEIGHT);
2306 }
2307
2308 // always notify: the strategy may depend on parts of the weight that do not change
2309 // the effective weight while a boost is active (e.g. preemption-threshold)
2310 m_strategy.OnTaskWeightChange(task, prev_weight);
2311
2312 return prev_base;
2313 }
2314
2316 {
2317 STK_ASSERT(tid != TID_NONE);
2318 STK_ASSERT(TStrategy::WEIGHT_API && TStrategy::PRIORITY_INHERITANCE_API);
2319
2320 KernelTask *const task = FindTaskByUserTask(GetUserTaskFromTid(tid));
2321 STK_ASSERT(task != nullptr);
2322
2323 const Weight prev_weight = task->GetWeight();
2324
2325 // restore to original or boost from wait objects
2326 task->SetCurrentWeight(sobj != nullptr ? sobj->FindWeightHigherThan(task->GetWeight()) : NO_WEIGHT);
2327
2328 m_strategy.OnTaskWeightChange(task, prev_weight);
2329 }
2330
2333 Timeout UpdateTasks(const Timeout elapsed_ticks)
2334 {
2335 // sync objects are updated before UpdateTaskRequest which may add a new object (newly added object must become 1 tick older)
2337 {
2338 UpdateSyncObjects(elapsed_ticks);
2339 }
2340
2341 if (m_request != REQ_NONE)
2342 {
2344 }
2345
2346 return UpdateTaskState(elapsed_ticks);
2347 }
2348
2349 void ProcessTaskPendingSleep(KernelTask *const task)
2350 {
2352
2353 // notify strategy that task is sleeping
2354 if __stk_constexpr_cpp17 (TStrategy::SLEEP_EVENT_API)
2355 {
2356 m_strategy.OnTaskSleep(task);
2357 }
2358 }
2359
2360 void ProcessTaskPendingYield(KernelTask *const task)
2361 {
2362 task->m_state &= ~KernelTask::STATE_YIELD_PENDING;
2363 }
2364
2365
2396 Timeout UpdateTaskState(const Timeout elapsed_ticks)
2397 {
2399
2400 for (size_t i = 0U; i < TASKS_MAX; ++i)
2401 {
2402 KernelTask *const task = &m_task_storage[i];
2403
2404 if __stk_constexpr_cpp17 (TStrategy::NOSLEEP_YIELD_API)
2405 {
2406 // a pending yield is consumed by this tick (the strategy already moved its cursor)
2407 if ((task->m_state & KernelTask::STATE_YIELD_PENDING) != 0U)
2408 {
2409 task->m_state &= ~KernelTask::STATE_YIELD_PENDING;
2410 }
2411 }
2412
2413 if (task->IsSleeping())
2414 {
2416 {
2417 // task is pending removal, wait until it is switched out
2418 if (task->IsPendingRemoval())
2419 {
2420 const size_t tasks_left = m_strategy.GetSize();
2421
2422 if ((task != m_task_now) ||
2423 ((tasks_left == 1U) && (m_fsm_state == FSM_STATE_SLEEPING)))
2424 {
2425 RemoveTask(task);
2426 continue;
2427 }
2428 }
2429 }
2430
2431 bool just_entered_sleep = false;
2432
2433 // note: only currently scheduled task can be pending to sleep
2434 if ((task->m_state & KernelTask::STATE_SLEEP_PENDING) != 0U)
2435 {
2436 STK_ASSERT(elapsed_ticks == 1); // entry tick must be a single, uncoalesced tick
2437
2438 // if Wake() raced in before this entry tick ran, STATE_WAKE_PENDING is set:
2439 // deliver OnTaskSleep() below as usual, then fall through to the advance block
2440 // in this same pass instead of waiting an extra tick, so the paired OnTaskWake()
2441 // still fires on schedule.
2442 const bool wake_pending = ((task->m_state & KernelTask::STATE_WAKE_PENDING) != 0U);
2443
2445
2446 just_entered_sleep = !wake_pending;
2447 }
2448
2449 if (!just_entered_sleep && !task->IsSleepInfinite())
2450 {
2451 // advance sleep time by number of elapsed ticks (always 1 if non-Tickless)
2452 task->m_time_sleep += elapsed_ticks;
2453
2454 // deliver sleep event to the strategy
2455 if __stk_constexpr_cpp17 (TStrategy::SLEEP_EVENT_API)
2456 {
2457 // notify strategy that the task woke up
2458 if (!task->IsSleeping())
2459 {
2460 m_strategy.OnTaskWake(task);
2461 }
2462 }
2463 }
2464 }
2465 else
2466 {
2468 {
2469 // in HRT mode we trace how long task spent in active state (doing some work)
2470 if (task->IsBusy())
2471 {
2472 task->m_hrt[0].duration += elapsed_ticks;
2473
2474 // check if deadline is missed (HRT failure)
2475 if (task->HrtIsDeadlineMissed(task->m_hrt[0].duration))
2476 {
2477 // report deadline overrun to the strategy which supports overrun recovery
2478 if __stk_constexpr_cpp17 (TStrategy::DEADLINE_MISSED_API)
2479 {
2480 if (!m_strategy.OnTaskDeadlineMissed(task))
2481 {
2482 // report failure if it could not be recovered by the scheduling strategy
2483 task->HrtHardFailDeadline(&m_platform);
2484 }
2485 }
2486 else
2487 {
2488 task->HrtHardFailDeadline(&m_platform);
2489 }
2490 }
2491 }
2492 }
2493 }
2494
2495 // get the number of ticks the driver has to keep CPU in Idle
2497 {
2498 if ((sleep_ticks > 1) && task->IsBusy())
2499 {
2500 sleep_ticks = task->GetSleepTicks(sleep_ticks);
2501 }
2502 }
2503 }
2504
2505 return sleep_ticks;
2506 }
2507
2510 void UpdateSyncObjects(const Timeout elapsed_ticks)
2511 {
2512 ISyncObject::ListEntryType *itr = m_sync_list->GetFirst();
2513
2514 while (itr != nullptr)
2515 {
2516 ISyncObject::ListEntryType *const next = itr->GetNext();
2517
2518 if (!util::DListCast::ListEntryToParent<ISyncObject>(itr)->Tick(elapsed_ticks))
2519 {
2520 m_sync_list->Unlink(itr);
2521 }
2522
2523 itr = next;
2524 }
2525 }
2526
2530 {
2531 // process AddTask requests coming from tasks (KERNEL_DYNAMIC mode only, KERNEL_HRT is
2532 // excluded as we assume that HRT tasks must be known to the kernel before a Start())
2534 {
2535 // process serialized AddTask request made from another active task, requesting process
2536 // is currently waiting due to SwitchToNext()
2537 if ((m_request & REQ_ADD_TASK) != 0U)
2538 {
2540
2541 for (size_t i = 0U; i < TASKS_MAX; ++i)
2542 {
2543 KernelTask *const task = &m_task_storage[i];
2544
2545 if (task->m_srt[0].add_task_req != nullptr)
2546 {
2547 AllocateAndAddNewTask(task->m_srt[0].add_task_req->user_task);
2548
2549 task->m_srt[0].add_task_req = nullptr;
2550 __stk_full_memfence();
2551 }
2552 }
2553 }
2554 }
2555 }
2556
2561 EFsmEvent FetchNextEvent(KernelTask *&next)
2562 {
2564
2565 // try getting next task for scheduling
2567
2568 // sleep-aware strategy returns nullptr if no active tasks available
2569 if (next != nullptr)
2570 {
2571 // strategy must provide active-only task
2572 STK_ASSERT(!next->IsSleeping());
2573
2574 // if was sleeping, process wake event first
2576 }
2577 // start sleeping
2578 else
2579 {
2581 {
2582 // if nullptr is returned then either strategy has all tasks sleeping or none left,
2583 // if KERNEL_DYNAMIC mode and no tasks left then exit from scheduling
2584 if (m_strategy.GetSize() == 0U)
2585 {
2586 next = nullptr;
2587 type = FSM_EVENT_EXIT;
2588 }
2589 }
2590 }
2591
2592 return type;
2593 }
2594
2599#ifdef _STK_UNDER_TEST
2600 virtual
2601#endif
2602 EFsmState GetNewFsmState(KernelTask *&next)
2603 {
2605 return m_fsm[m_fsm_state][FetchNextEvent(next)];
2606 }
2607
2613 bool UpdateFsmState(Stack *&idle, Stack *&active)
2614 {
2615 KernelTask *const now = m_task_now, *next = nullptr;
2616 bool switch_context = false;
2617
2618 const EFsmState new_state = GetNewFsmState(next);
2619
2620 switch (new_state)
2621 {
2623 switch_context = StateSwitch(now, next, idle, active);
2624 m_fsm_state = new_state;
2625 break;
2626 case FSM_STATE_SLEEPING:
2627 switch_context = StateSleep(now, next, idle, active);
2628 m_fsm_state = new_state;
2629 break;
2630 case FSM_STATE_WAKING:
2631 switch_context = StateWake(now, next, idle, active);
2632 m_fsm_state = new_state;
2633 break;
2634 case FSM_STATE_EXITING:
2635 switch_context = StateExit(now, next, idle, active);
2636 m_fsm_state = new_state;
2637 break;
2638 case FSM_STATE_NONE:
2639 break; // valid intermittent non-persisting state: no-transition
2640 case FSM_STATE_MAX:
2641 default: // invalid state value
2643 break;
2644 }
2645
2646 return switch_context;
2647 }
2648
2656 bool StateSwitch(KernelTask *now, KernelTask *next, Stack *&idle, Stack *&active)
2657 {
2658 STK_ASSERT(now != nullptr);
2659 STK_ASSERT(next != nullptr);
2660
2661 bool switch_context = false;
2662
2663 // if equal: do not switch context because task did not change
2664 if (next != now)
2665 {
2666 idle = now->GetUserStackPtr();
2667 active = next->GetUserStackPtr();
2668
2669 // if stack memory is exceeded these assertions will be hit
2670 #if STK_STACK_GUARD
2671 if (now->IsBusy())
2672 {
2673 // current task could exit, thus we check it with IsBusy to avoid referencing nullptr returned by GetUserTask()
2674 STK_ASSERT(now->GetUserTask()->GetStack()[0] == STK_STACK_MEMORY_FILLER);
2675 }
2676 STK_ASSERT(next->GetUserTask()->GetStack()[0] == STK_STACK_MEMORY_FILLER);
2677 #endif
2678
2679 m_task_now = next;
2680
2682 {
2683 if (now->m_hrt[0].done)
2684 {
2685 now->HrtOnSwitchedOut();
2686 next->HrtOnSwitchedIn();
2687 }
2688 }
2689
2690 switch_context = true;
2691 }
2692
2693 return switch_context;
2694 }
2695
2703 bool StateWake(KernelTask *now, KernelTask *next, Stack *&idle, Stack *&active)
2704 {
2705 STK_UNUSED(now);
2706
2707 STK_ASSERT(next != nullptr);
2708
2709 idle = &m_sleep_trap[0].stack;
2710 active = next->GetUserStackPtr();
2711
2712 // if stack memory is exceeded these assertions will be hit
2714 #if STK_STACK_GUARD
2715 STK_ASSERT(next->GetUserTask()->GetStack()[0] == STK_STACK_MEMORY_FILLER);
2716 #endif
2717
2718 m_task_now = next;
2719
2721 {
2722 next->HrtOnSwitchedIn();
2723 }
2724
2725 return true; // switch context
2726 }
2727
2735 bool StateSleep(KernelTask *now, KernelTask *next, Stack *&idle, Stack *&active)
2736 {
2737 STK_UNUSED(next);
2738
2739 STK_ASSERT(now != nullptr);
2740 STK_ASSERT(m_sleep_trap[0].stack.SP != 0);
2741
2742 idle = now->GetUserStackPtr();
2743 active = &m_sleep_trap[0].stack;
2744
2746
2748 {
2749 if (!now->IsPendingRemoval())
2750 {
2751 now->HrtOnSwitchedOut();
2752 }
2753 }
2754
2755 return true; // switch context
2756 }
2757
2766 bool StateExit(KernelTask *now, KernelTask *next, Stack *&idle, Stack *&active)
2767 {
2768 STK_UNUSED(now);
2769 STK_UNUSED(next);
2770
2772 {
2773 // dynamic tasks are not supported if main processes's stack memory is not provided in Start()
2774 STK_ASSERT(m_exit_trap[0].stack.SP != 0);
2775
2776 idle = nullptr;
2777 active = &m_exit_trap[0].stack;
2778
2779 m_task_now = nullptr;
2780
2781 m_platform.Stop();
2782 }
2783 else
2784 {
2785 STK_UNUSED(idle);
2786 STK_UNUSED(active);
2787 }
2788
2789 return false;
2790 }
2791
2795 bool IsInitialized() const { return (m_kstate != KSTATE_INACTIVE); }
2796
2802 {
2805 }
2806
2807#if STK_SEGGER_SYSVIEW
2812 void SendTaskTraceInfo(KernelTask *task)
2813 {
2814 STK_ASSERT(task->IsBusy());
2815
2816 const SEGGER_SYSVIEW_TASKINFO info =
2817 {
2818 .TaskID = task->GetUserStackPtr()->tid,
2819 .sName = task->GetUserTask()->GetTraceName(),
2820 .Prio = static_cast<U32>(task->GetWeight()),
2821 .StackBase = hw::PtrToWord(task->GetUserTask()->GetStack()),
2822 .StackSize = (task->GetUserTask()->GetStackSize() * sizeof(Word))
2823 };
2824 SEGGER_SYSVIEW_SendTaskInfo(&info);
2825 }
2826#endif
2827
2828 // Kernel modes:
2829 static constexpr bool IsStaticMode() { return ((TMode & KERNEL_STATIC) != 0U); }
2830 static constexpr bool IsDynamicMode() { return ((TMode & KERNEL_DYNAMIC) != 0U); }
2831 static constexpr bool IsHrtMode() { return ((TMode & KERNEL_HRT) != 0U); }
2832 static constexpr bool IsSyncMode() { return ((TMode & KERNEL_SYNC) != 0U); }
2833 static constexpr bool IsTicklessMode() { return ((TMode & KERNEL_TICKLESS) != 0U); }
2834
2835 // If hit here: Kernel<N> expects at least 1 task, e.g. N > 0
2837
2838 // If hit here: Kernel mode must be assigned.
2839 STK_STATIC_ASSERT_N(KERNEL_MODE_MUST_BE_SET, (TMode != 0U));
2840
2841 // If hit here: KERNEL_STATIC and KERNEL_DYNAMIC can not be mixed, either one of these is possible.
2842 STK_STATIC_ASSERT_N(KERNEL_MODE_MIX_NOT_ALLOWED,
2843 (((TMode & KERNEL_STATIC) & (TMode & KERNEL_DYNAMIC)) == 0U));
2844
2845 // If hit here: KERNEL_HRT must accompany KERNEL_STATIC or KERNEL_DYNAMIC.
2846 STK_STATIC_ASSERT_N(KERNEL_MODE_HRT_ALONE, (((TMode & KERNEL_HRT) == 0U) ||
2847 ((((TMode & KERNEL_HRT) != 0U)) && (((TMode & KERNEL_STATIC) != 0U) || ((TMode & KERNEL_DYNAMIC) != 0U)))));
2848
2849 // If hit here: KERNEL_TICKLESS is incompatible with KERNEL_HRT. Tickless suppresses the timer,
2850 // which destroys the precise periodicity HRT depends on.
2851 STK_STATIC_ASSERT_N(TICKLESS_HRT_CONFLICT,
2852 (((TMode & KERNEL_TICKLESS) == 0U) || ((TMode & KERNEL_HRT) == 0U)));
2853
2854 // If hit here: Strategy which supports Priority Inheritance API must also support Weight API.
2855 STK_STATIC_ASSERT_N(KERNEL_MODE_MUST_BE_SET, (TStrategy::PRIORITY_INHERITANCE_API && TStrategy::WEIGHT_API) ||
2856 !TStrategy::PRIORITY_INHERITANCE_API);
2857
2862
2877
2893
2900
2901 KernelService m_service;
2902 TPlatform m_platform;
2903 TStrategy m_strategy;
2904 KernelTask *m_task_now;
2906 SleepTrapStack m_sleep_trap[1];
2909 volatile uint8_t m_request;
2912
2914 // FSM_EVENT_SWITCH FSM_EVENT_SLEEP FSM_EVENT_WAKE FSM_EVENT_EXIT
2919 };
2920
2922};
2923
2924} // namespace stk
2925
2926#endif /* STK_H_ */
#define STK_UNUSED(X)
Explicitly marks a variable as unused to suppress compiler warnings.
Definition stk_defs.h:715
#define STK_STATIC_ASSERT_N(NAME, X)
Compile-time assertion with a user-defined name suffix.
Definition stk_defs.h:545
#define __stk_forceinline
Forces compiler to always inline the decorated function, regardless of optimisation level.
Definition stk_defs.h:277
#define STK_ASSERT(e)
Runtime assertion. Halts execution if the expression e evaluates to false.
Definition stk_defs.h:516
#define __stk_attr_noinline
Prevents compiler from inlining the decorated function (function prefix).
Definition stk_defs.h:357
#define __stk_constexpr_cpp17
constexpr definition for C++17 and above.
Definition stk_defs.h:489
#define STK_TICKLESS_TICKS_MAX
Maximum number of kernel ticks the hardware timer may be suppressed in one tickless idle interval whe...
Definition stk_defs.h:77
#define STK_STATIC_ASSERT_DESC(X, DESC)
Compile-time assertion with a custom error description. Produces a compilation error if X is false.
Definition stk_defs.h:536
#define STK_STACK_MEMORY_FILLER
Sentinel value written to the entire stack region at initialization (stack watermark pattern).
Definition stk_defs.h:563
#define STK_VIRT_DTOR
Makes destructors virtual and compliant to strict rules if STK_STRICT_COMPLIANCY=0.
Definition stk_defs.h:261
Contains helper implementations which simplify user-side code.
Earliest Deadline First (EDF) task-switching strategy (stk::SwitchStrategyEDF).
Fixed-priority preemptive task-switching strategy with round-robin within each priority level (stk::S...
Rate-Monotonic (RM) and Deadline-Monotonic (DM) task-switching strategies (stk::SwitchStrategyMonoton...
Fixed-priority preemptive strategy with ThreadX-style Preemption-Threshold (stk::SwitchStrategyPreemp...
Round-Robin task-switching strategy (stk::SwitchStrategyRoundRobin / stk::SwitchStrategyRR,...
Smooth Weighted Round-Robin task-switching strategy (stk::SwitchStrategySmoothWeightedRoundRobin / st...
Namespace of STK package.
uintptr_t Word
Native processor word type.
Definition stk_common.h:144
@ ACCESS_PRIVILEGED
Privileged access mode (access to hardware is fully unrestricted).
Definition stk_common.h:38
static constexpr ITask * GetUserTaskFromTid(TId task_id) noexcept
Get task instance from its identifier.
Definition stk_arch.h:725
static constexpr Timeout NO_WAIT
Timeout value: return immediately if the synchronization object is not yet signaled (non-blocking pol...
Definition stk_common.h:218
EWaitResult
Wait result (see IKernelService::Wait).
Definition stk_common.h:121
@ WAIT_RESULT_FAIL
IKernelService::Wait returned with error without waiting.
Definition stk_common.h:122
@ WAIT_RESULT_CANCELED
The wait was cancelled by another task via IKernel::CancelTaskWait(), independent of signal/timeout.
Definition stk_common.h:125
@ WAIT_RESULT_TIMEOUT
The wake was caused by a timeout expiry.
Definition stk_common.h:124
@ WAIT_RESULT_SIGNAL
The wake was caused by a signal.
Definition stk_common.h:123
@ PERIODICITY_DEFAULT
Default periodicity (microseconds), 1 millisecond.
Definition stk_common.h:91
@ PERIODICITY_MAX
Maximum periodicity (microseconds), 99 milliseconds (note: this value is the highest working on a rea...
Definition stk_common.h:90
int64_t Ticks
Ticks value.
Definition stk_common.h:159
@ KERNEL_PANIC_BAD_MODE
Kernel is in bad/unsupported mode for the current operation.
Definition stk_common.h:68
@ KERNEL_PANIC_BAD_STATE
Kernel entered unexpected (bad) state.
Definition stk_common.h:67
int32_t Timeout
Timeout time (ticks).
Definition stk_common.h:154
static void STK_KERNEL_PANIC(stk::EKernelPanicId id)
Called when the kernel detects an unrecoverable internal fault.
Definition stk_arch.h:183
@ SYS_TASK_ID_EXIT
Exit trap.
Definition stk_common.h:101
@ SYS_TASK_ID_SLEEP
Sleep trap.
Definition stk_common.h:100
static constexpr Weight DEFAULT_WEIGHT
Weight value: default weight of value (1) (see SwitchStrategySmoothWeightedRoundRobin).
Definition stk_common.h:228
static constexpr T Max(T a, T b) noexcept
Compile-time maximum of two values.
Definition stk_defs.h:759
static constexpr Weight NO_WEIGHT
Weight value: weight is not set.
Definition stk_common.h:223
Timeout GetInitialSleepTicks()
static constexpr Timeout WAIT_INFINITE
Timeout value: block indefinitely until the synchronization object is signaled.
Definition stk_common.h:212
static constexpr TId TID_NONE
Reserved task/thread id representing zero/none thread id.
Definition stk_common.h:206
static constexpr T Min(T a, T b) noexcept
Compile-time minimum of two values.
Definition stk_defs.h:753
@ TRACE_EVENT_SLEEP
Task entered a sleep state.
Definition stk_common.h:112
@ TRACE_EVENT_SUSPEND
Task suspended via Suspend.
Definition stk_common.h:114
@ TRACE_EVENT_WAIT
Task suspended on a sync object.
Definition stk_common.h:113
@ STACK_SLEEP_TRAP
Stack of the Sleep trap.
Definition stk_common.h:81
@ STACK_USER_TASK
Stack of the user task.
Definition stk_common.h:80
@ STACK_EXIT_TRAP
Stack of the Exit trap.
Definition stk_common.h:82
Timeout GetInitialSleepTicks< false >()
Definition stk.h:42
static constexpr TId GetTidFromUserTask(const ITask *task) noexcept
Get task identifier from ITask instance.
Definition stk_arch.h:716
Timeout GetInitialSleepTicks< true >()
Definition stk.h:41
uint64_t Cycles
Cycles value.
Definition stk_common.h:169
Word TId
Task (thread) id.
Definition stk_common.h:149
int32_t Weight
Weight value (aka priority).
Definition stk_common.h:174
@ KERNEL_TICKLESS
Tickless mode. To use this mode STK_TICKLESS_IDLE must be defined to 1 in stk_config....
Definition stk_common.h:51
@ KERNEL_SYNC
Synchronization support (see Event).
Definition stk_common.h:50
@ KERNEL_HRT
Hard Real-Time (HRT) behavior (tasks are scheduled periodically and have an execution deadline,...
Definition stk_common.h:49
@ KERNEL_STATIC
All tasks are static and can not exit.
Definition stk_common.h:47
@ KERNEL_DYNAMIC
Tasks can be added or removed and therefore exit when done.
Definition stk_common.h:48
static void WriteVolatile64(volatile T *addr, T value)
Atomically write a 64-bit volatile value.
Definition stk_arch.h:396
static constexpr Word PtrToWord(T *const ptr) noexcept
Cast a pointer to a CPU register-width integer.
Definition stk_arch.h:214
static T ReadVolatile64(volatile const T *addr)
Atomically read a 64-bit volatile value.
Definition stk_arch.h:331
bool IsInsideISR()
Check whether the CPU is currently executing inside a hardware interrupt service routine (ISR).
Memory-related primitives.
void OnStop() override
Called by the platform driver after a scheduler stop (all tasks have exited).
Definition stk.h:1934
bool UpdateFsmState(Stack *&idle, Stack *&active)
Update FSM state.
Definition stk.h:2613
KernelTask * AllocateNewTask(ITask *user_task)
Allocate new instance of KernelTask.
Definition stk.h:1643
void RequestAddTask(ITask *const user_task)
Request to add new task.
Definition stk.h:1729
void OnTaskExit(Stack *stack) override
Called from the Thread process when task finished (its Run function exited by return).
Definition stk.h:2155
void OnTaskSleepCancel(TId task_id)
Definition stk.h:2141
EFsmState
Finite-state machine (FSM) state. Encodes what the kernel is currently doing between two consecutive ...
Definition stk.h:1570
KernelTask * FindTaskByStack(const Stack *stack)
Find kernel task by the bound Stack instance.
Definition stk.h:1783
KernelTask TaskStorageType[TASKS_MAX]
KernelTask array type used as a storage for the KernelTask instances.
Definition stk.h:2861
~Kernel()=default
Destructor.
Weight OnSetWeight(TId tid, Weight weight)
Definition stk.h:2288
bool OnTick(Stack *&idle, Stack *&active, Timeout &ticks) override
Process one scheduler tick. Called from the platform timer/tick ISR.
Definition stk.h:1961
EWaitResult OnTaskWait(Word caller_SP, ISyncObject *sync_obj, IMutex *mutex, Timeout timeout) override
Called from the Thread process when task needs to wait.
Definition stk.h:2182
void OnSuspend(bool suspended) override
Called from the Thread process to suspend scheduling.
Definition stk.h:2243
void ScheduleTaskRemoval(ITask *user_task) override
Schedule task removal from scheduling (exit).
Definition stk.h:1320
EFsmState GetNewFsmState(KernelTask *&next)
Get new FSM state.
Definition stk.h:2602
void RemoveTask(ITask *user_task) override
Remove a previously added task from the kernel when it is not started.
Definition stk.h:1295
StackMemoryWrapper< STACK_SIZE_MIN > ExitTrapStackMemory
Stack memory wrapper type for the exit trap.
Definition stk.h:104
void OnRestoreWeight(TId tid, ISyncObject *sobj)
Definition stk.h:2315
void OnStart(Stack *&active) override
Called by platform driver immediately after a scheduler start (first tick).
Definition stk.h:1869
bool StateWake(KernelTask *now, KernelTask *next, Stack *&idle, Stack *&active)
Wakes up after sleeping.
Definition stk.h:2703
KernelTask * FindTaskBySP(Word SP)
Find kernel task for a Stack Pointer (SP).
Definition stk.h:1804
void InitTraps()
Initialize stack of the traps.
Definition stk.h:1609
static constexpr bool IsHrtMode()
Definition stk.h:2831
void AddTask(ITask *user_task) override
Register task for a soft real-time (SRT) scheduling.
Definition stk.h:1231
ISyncObject::ListHeadType SyncObjectList
Intrusive list of active ISyncObject instances registered with this kernel. Each sync object in this ...
Definition stk.h:2899
EFsmEvent
Finite-state machine (FSM) event. Computed by FetchNextEvent() each tick based on strategy output and...
Definition stk.h:1584
StackMemoryWrapper<((32U))> SleepTrapStackMemory
Stack memory wrapper type for the sleep trap.
Definition stk.h:98
size_t EnumerateKernelTasks(ArrayView< IKernelTask * > tasks) override
Enumerate kernel tasks.
Definition stk.h:1458
static constexpr bool IsSyncMode()
Definition stk.h:2832
KernelTask * FindTaskByUserTask(const ITask *user_task)
Find kernel task by the bound ITask instance.
Definition stk.h:1762
static bool IsValidFsmState(EFsmState state)
Check if FSM state is valid.
Definition stk.h:1602
void OnInheritWeight(TId tid, Weight weight)
Definition stk.h:2268
void ResumeTask(ITask *user_task) override
Resume task.
Definition stk.h:1401
void OnTaskSleep(Word caller_SP, Timeout ticks) override
Called by Thread process (via IKernelService::Sleep) for exclusion of the calling process from schedu...
Definition stk.h:2073
void Start() override
Start the scheduler. This call does not return until all tasks have exited (KERNEL_DYNAMIC mode) or i...
Definition stk.h:1510
static constexpr bool IsTicklessMode()
Definition stk.h:2833
void RemoveTask(KernelTask *task)
Remove kernel task.
Definition stk.h:1844
void CancelTaskWait(ITask *user_task) override
Cancel task's in-progress wait on a synchronization object.
Definition stk.h:1428
void AddKernelTask(KernelTask *task)
Add kernel task to the scheduling strategy.
Definition stk.h:1683
EKernelState GetState() const override
Get kernel state.
Definition stk.h:1554
ERequest
Bitmask flags for pending inter-task requests that must be processed by the kernel on the next tick (...
Definition stk.h:111
bool StateExit(KernelTask *now, KernelTask *next, Stack *&idle, Stack *&active)
Exits from scheduling.
Definition stk.h:2766
IPlatform * GetPlatform() override
Get platform driver instance owned by this kernel.
Definition stk.h:1545
void AllocateAndAddNewTask(ITask *user_task)
Allocate new instance of KernelTask and add it into the scheduling process.
Definition stk.h:1700
void OnTaskSwitch(Word caller_SP) override
Called by Thread process (via IKernelService::SwitchToNext) to switch to a next task.
Definition stk.h:2027
void Initialize(uint32_t resolution_us=PERIODICITY_DEFAULT) override
Initialize kernel.
Definition stk.h:1194
TPlatform m_platform
Platform driver (SysTick, PendSV, context switch implementation).
Definition stk.h:2902
void HrtAllocateAndAddNewTask(ITask *user_task, Timeout periodicity_tc, Timeout deadline_tc, Timeout start_delay_tc)
Allocate new instance of KernelTask and add it into the HRT scheduling process.
Definition stk.h:1715
TId OnGetTid(Word caller_SP) override
Called from the Thread process when for getting task/thread id of the process.
Definition stk.h:2235
size_t EnumerateTasks(ArrayView< ITask * > user_tasks) override
Enumerate user tasks.
Definition stk.h:1482
void ProcessTaskPendingSleep(KernelTask *const task)
Definition stk.h:2349
Timeout UpdateTasks(const Timeout elapsed_ticks)
Update tasks (sleep, requests).
Definition stk.h:2333
void ScheduleAddTask()
Signal the kernel to process a pending AddTask request on the next tick.
Definition stk.h:2801
bool IsInitialized() const
Check whether Initialize() has been called and completed successfully.
Definition stk.h:2795
static constexpr bool IsDynamicMode()
Definition stk.h:2830
bool OnForceContextSwitch(TId id, Stack *&idle, Stack *&active)
Called when task state change forces a context switch (Sleep, SleepUntil, Wait).
Definition stk.h:1988
void ProcessTaskPendingYield(KernelTask *const task)
Definition stk.h:2360
ITaskSwitchStrategy * GetSwitchStrategy() override
Get task-switching strategy instance owned by this kernel.
Definition stk.h:1550
bool StateSwitch(KernelTask *now, KernelTask *next, Stack *&idle, Stack *&active)
Switches contexts.
Definition stk.h:2656
void AddTask(ITask *user_task, Timeout periodicity_tc, Timeout deadline_tc, Timeout start_delay_tc) override
Register a task for hard real-time (HRT) scheduling.
Definition stk.h:1270
Kernel()
Construct the kernel with all storage zero-initialized, m_request cleared to REQ_NONE,...
Definition stk.h:1161
void UpdateTaskRequest()
Update pending task requests.
Definition stk.h:2529
bool StateSleep(KernelTask *now, KernelTask *next, Stack *&idle, Stack *&active)
Enters into a sleeping mode.
Definition stk.h:2735
Timeout UpdateTaskState(const Timeout elapsed_ticks)
Update task state: process removals, deliver sleep/wake notifications, advance sleep timers,...
Definition stk.h:2396
void UpdateSyncObjects(const Timeout elapsed_ticks)
Update synchronization objects.
Definition stk.h:2510
static constexpr bool IsStaticMode()
Definition stk.h:2829
bool IsStarted() const
Check whether scheduler is currently running.
Definition stk.h:1537
bool OnTaskSleepUntil(Word caller_SP, Ticks timestamp) override
Called by Thread process (via IKernelService::SleepUntil) for exclusion of the calling process from s...
Definition stk.h:2102
void SuspendTask(ITask *user_task, bool &suspended) override
Suspend task.
Definition stk.h:1348
EFsmEvent FetchNextEvent(KernelTask *&next)
Fetch next event for the FSM.
Definition stk.h:2561
Internal per-slot kernel descriptor that wraps a user ITask instance.
Definition stk.h:128
Weight GetBaseWeight() const override
Get base scheduling weight from the user task, ignoring priority inheritance.
Definition stk.h:260
Weight GetCurrentWeight() const override
Get current (run-time) scheduling weight.
Definition stk.h:297
KernelTask()
Construct a free (unbound) task slot. All fields set to zero/null.
Definition stk.h:162
void ScheduleSleep(Timeout ticks)
Put the task into a sleeping state for the specified number of ticks.
Definition stk.h:868
Timeout GetSleepTicks(Timeout sleep_ticks)
Definition stk.h:384
TId GetTid() const
Get task identifier.
Definition stk.h:200
void HrtOnSwitchedOut()
Called when task is switched out from the scheduling process.
Definition stk.h:811
EStateFlags
Bitmask of transient state flags. Set by the task or the kernel and consumed (cleared) during UpdateT...
Definition stk.h:136
@ STATE_REMOVE_PENDING
Task returned from its Run function; slot will be freed on the next tick (KERNEL_DYNAMIC only).
Definition stk.h:138
@ STATE_SUSPENDED
Task is suspended by SuspendTask.
Definition stk.h:141
@ STATE_YIELD_PENDING
Task called Yield() and the strategy handled it via OnTaskYield() (task stays runnable); cleared by t...
Definition stk.h:142
@ STATE_WAKE_PENDING
Task wake is pending (see Wake).
Definition stk.h:140
@ STATE_SLEEP_PENDING
Task called Sleep/SleepUntil/Yield; strategy's OnTaskSleep() will be invoked on the next tick (sleep-...
Definition stk.h:139
@ STATE_NONE
No pending state flags.
Definition stk.h:137
Timeout GetHrtPeriodicity() const override
Get HRT scheduling periodicity.
Definition stk.h:317
friend class Kernel
Definition stk.h:129
bool HrtIsDeadlineMissed(Timeout duration) const
Check if deadline missed.
Definition stk.h:854
SrtInfo m_srt[STK_ALLOCATE_COUNT< TMode, KERNEL_HRT, 0U, 1U >::Value]
SRT metadata. Zero-size (no memory) in KERNEL_HRT mode.
Definition stk.h:931
void ScheduleRemoval()
Schedule the removal of the task from the kernel on next tick.
Definition stk.h:718
Stack m_stack
Stack descriptor (SP register value + access mode + optional tid).
Definition stk.h:928
void SetBaseWeight(Weight weight)
Override the base weight at run-time (see IKernelService::SetWeight).
Definition stk.h:280
void Bind(TPlatform *platform, ITask *user_task)
Bind this slot to a user task: set access mode, task ID, and initialize the stack.
Definition stk.h:660
~KernelTask()=default
Destructor.
Weight m_rt_weight[STK_ALLOCATE_COUNT< TStrategy::WEIGHT_API, 1U, 1U, 0U >::Value]
Run-time weight for weighted-round-robin scheduling. Zero-size for unweighted strategies.
Definition stk.h:933
void HrtHardFailDeadline(IPlatform *platform)
Hard-fail HRT task when it missed its deadline.
Definition stk.h:831
Weight m_base_weight[STK_ALLOCATE_COUNT< TStrategy::WEIGHT_API, 1U, 1U, 0U >::Value]
Run-time override of ITask::GetWeight() (NO_WEIGHT = not overridden). Zero-size for unweighted strate...
Definition stk.h:934
void HrtInit(Timeout periodicity_tc, Timeout deadline_tc, Timeout start_delay_tc)
Initialize task with HRT info.
Definition stk.h:786
volatile uint32_t m_state
Bitmask of EStateFlags. Written by task thread, read/cleared by kernel tick.
Definition stk.h:929
bool IsSleepInfinite() const
Check whether this task is currently sleeping infinitely.
Definition stk.h:195
ITask * m_user
Bound user task, or NULL when slot is free.
Definition stk.h:927
Timeout GetHrtRelativeDeadline() const override
Get remaining HRT deadline (ticks left before the deadline expires).
Definition stk.h:365
void SetCurrentWeight(Weight weight) override
Update the run-time scheduling weight (weighted strategies only).
Definition stk.h:223
void BusyWaitWhileSleeping(Kernel *kernel) const
Block further execution of the task's context while in sleeping state.
Definition stk.h:912
Stack GetUserStack() const override
Get stack descriptor for this task slot.
Definition stk.h:180
bool IsBusy() const
Check whether this slot is bound to a user task.
Definition stk.h:185
bool IsSleeping() const override
Check whether this task is currently sleeping (waiting for a tick or a wake event).
Definition stk.h:190
Stack * GetUserStackPtr()
Get pointer to user Stack.
Definition stk.h:925
HrtInfo m_hrt[STK_ALLOCATE_COUNT< TMode, KERNEL_HRT, 1U, 0U >::Value]
HRT metadata. Zero-size (no memory) in non-HRT mode.
Definition stk.h:932
void HrtOnWorkCompleted()
Called when task process called IKernelService::SwitchToNext to inform Kernel that work is completed.
Definition stk.h:845
void Wake() override
Wake this task on the next scheduling tick.
Definition stk.h:206
Weight GetWeight() const override
Get static scheduling weight from the user task.
Definition stk.h:234
volatile Timeout m_time_sleep
Sleep countdown: negative while sleeping (absolute value = ticks remaining), zero when awake.
Definition stk.h:930
bool IsPendingRemoval() const
Check if task is pending removal.
Definition stk.h:743
Timeout GetHrtDeadline() const override
Get absolute HRT deadline (ticks elapsed since task was activated).
Definition stk.h:340
void Unbind()
Reset this slot to the free (unbound) state, clearing all scheduling metadata.
Definition stk.h:693
void HrtOnSwitchedIn()
Called when task is switched into the scheduling process.
Definition stk.h:806
bool IsMemoryOfSP(Word SP) const
Check if Stack Pointer (SP) belongs to this task.
Definition stk.h:748
void BusyWaitWhileYielding(Kernel *kernel) const
Request a context switch and wait until the kernel consumes the pending yield.
Definition stk.h:897
ITask * GetUserTask() override
Get bound user task.
Definition stk.h:175
WaitObject m_wait_obj[STK_ALLOCATE_COUNT< TMode, KERNEL_SYNC, 1U, 0U >::Value]
Embedded wait object for synchronization. Zero-size (no memory) if KERNEL_SYNC is not set.
Definition stk.h:935
void ScheduleYield()
Mark the task as yielding (strategy handled Yield() without sleeping the task).
Definition stk.h:885
Payload for an in-flight AddTask() request issued by a running task.
Definition stk.h:154
ITask * user_task
User task to add. Must remain valid for the lifetime of its kernel slot.
Definition stk.h:155
Per-task soft real-time (SRT) metadata.
Definition stk.h:429
void Clear()
Clear all fields, ready for slot re-use.
Definition stk.h:435
AddTaskRequest * add_task_req
Definition stk.h:445
Per-task Hard Real-Time (HRT) scheduling metadata.
Definition stk.h:453
void Clear()
Clear all fields, ready for slot re-use or re-activation.
Definition stk.h:459
volatile bool done
Set to true when the task signals work completion (via Yield() or on exit). Triggers HrtOnSwitchedOut...
Definition stk.h:470
Timeout deadline
Maximum allowed active duration in ticks (relative to switch-in). Exceeding this triggers OnDeadlineM...
Definition stk.h:468
Timeout periodicity
Activation period in ticks: the task is re-activated every this many ticks.
Definition stk.h:467
Timeout duration
Ticks spent in the active (non-sleeping) state in the current period. Incremented by UpdateTaskState(...
Definition stk.h:469
Concrete implementation of IWaitObject, embedded in each KernelTask slot.
Definition stk.h:480
bool IsWaiting() const
Check if busy with waiting.
Definition stk.h:514
EWaitResult GetWaitResult() const
Get wait result.
Definition stk.h:520
EState GetState() const override
Get current state.
Definition stk.h:509
Timeout m_time_wait
Ticks remaining until timeout. Decremented each tick; WAIT_INFINITE means no timeout.
Definition stk.h:652
void Wake(bool timeout) override
Wake the waiting task (called by ISyncObject when it signals).
Definition stk.h:553
~WaitObject()=default
Destructor.
void Cancel()
Cancel an in-progress wait and wake the owning task early.
Definition stk.h:637
bool Tick(Timeout elapsed_ticks) override
Advance the timeout countdown by one tick.
Definition stk.h:572
void CancelNoWake()
Cancel an in-progress wait without waking the owning task.
Definition stk.h:615
void SetupWait(ISyncObject *sync_obj, Timeout timeout)
Configure and arm this wait object for a new wait operation.
Definition stk.h:597
TId GetTid() const override
Get the TId of the task that owns this wait object.
Definition stk.h:503
void ClearState()
Clear state.
Definition stk.h:541
volatile EState m_state
State of the object, see WaitObject::EState.
Definition stk.h:653
ISyncObject * m_sync_obj
Sync object this wait is registered with, or NULL when not waiting.
Definition stk.h:651
KernelTask * m_task
Back-pointer to the owning KernelTask. Set once at construction; never changes.
Definition stk.h:650
Payload stored in the sync object's kernel-side list entry while a task is waiting.
Definition stk.h:496
ISyncObject * sync_obj
Sync object whose Tick() will be called each kernel tick.
Definition stk.h:497
KernelService()
Construct an uninitialized service instance (m_platform = null, m_ticks = 0).
Definition stk.h:1117
Timeout Suspend() override
Suspend scheduling.
Definition stk.h:1054
void SwitchToNext() override
Notify scheduler to switch to the next task (yield).
Definition stk.h:1015
volatile Ticks m_ticks
Global tick counter. Written via hw::WriteVolatile64() by IncrementTick() (ISR context); read via hw:...
Definition stk.h:1145
void Wake(ISyncObject *sobj, bool all) override
Wake one or all tasks currently waiting on a synchronization object.
Definition stk.h:1035
uint32_t GetSysTimerFrequency() const override
Get system timer frequency.
Definition stk.h:958
friend class Kernel
Definition stk.h:947
void Sleep(Timeout ticks) override
Put calling process into a sleep state.
Definition stk.h:975
Kernel * m_kernel
Pointer to the Kernel.
Definition stk.h:1144
Ticks GetTicks() const override
Get number of ticks elapsed since kernel start.
Definition stk.h:952
void SleepCancel(TId task_id) override
Cancel sleep of the task.
Definition stk.h:1007
void Resume(Timeout elapsed_ticks) override
Resume scheduling after a prior Suspend() call.
Definition stk.h:1067
bool SleepUntil(Ticks timestamp) override
Put calling process into a sleep state until the specified timestamp.
Definition stk.h:991
void RestoreWeight(TId tid, ISyncObject *sobj) override
Restore weight of the task to the original value.
Definition stk.h:1087
~KernelService()=default
Destructor.
void InheritWeight(TId tid, Weight weight) override
Inherit weight for the task.
Definition stk.h:1079
Cycles GetSysTimerCount() const override
Get system timer count value.
Definition stk.h:956
uint32_t GetTickResolution() const override
Get number of microseconds in one tick.
Definition stk.h:954
void Delay(Timeout ticks) override
Delay calling process.
Definition stk.h:960
Weight SetWeight(TId tid, Weight weight) override
Change the base weight of the task at run-time (replaces ITask::GetWeight() value).
Definition stk.h:1095
TId GetTid() const override
Get thread Id of the currently running task.
Definition stk.h:950
EWaitResult Wait(ISyncObject *sobj, IMutex *mutex, Timeout ticks) override
Put calling process into a waiting state until synchronization object is signaled or timeout occurs.
Definition stk.h:1022
void IncrementTicks(Ticks advance)
Increment counter by value.
Definition stk.h:1138
void Initialize(Kernel *kernel)
Initialize instance.
Definition stk.h:1130
Storage bundle for the sleep trap: a Stack descriptor paired with its backing memory.
Definition stk.h:2871
SleepTrapStackMemory::MemoryType Memory
Definition stk.h:2872
Memory memory
Backing stack memory array. Size: STK_SLEEP_TRAP_STACK_SIZE elements of Word.
Definition stk.h:2875
Stack stack
Stack descriptor (SP register value + access mode). Initialized by InitTraps() on every Start().
Definition stk.h:2874
Storage bundle for the exit trap: a Stack descriptor paired with its backing memory.
Definition stk.h:2887
Memory memory
Backing stack memory array. Size: STACK_SIZE_MIN elements of Word.
Definition stk.h:2891
ExitTrapStackMemory::MemoryType Memory
Definition stk.h:2888
Stack stack
Stack descriptor (SP register value + access mode). Initialized by InitTraps() on every Start().
Definition stk.h:2890
RAII instance that enters the critical section on construction and exits it on destruction.
Definition stk_arch.h:494
Lightweight, non-owning view over a contiguous sequence of elements.
Definition stk_common.h:255
size_t GetSize() const
Get number of elements in the view.
Definition stk_common.h:295
Stack descriptor.
Definition stk_common.h:393
uint32_t access_mode
Bitfield with hardware access mode of the task (see EAccessMode).
Definition stk_common.h:395
TId tid
Task id (see STK_SEGGER_SYSVIEW, STK_MPU_STACK_GUARD).
Definition stk_common.h:406
Interface for a stack memory region.
Definition stk_common.h:414
virtual size_t GetStackSize() const =0
Get number of elements of the stack memory array.
virtual const Word * GetStack() const =0
Get pointer to the stack memory.
Wait object.
Definition stk_common.h:463
EState
State of the WaitObject.
Definition stk_common.h:479
@ STATE_CANCELLED
Wait was cancelled via Cancel() rather than Wake()/timeout.
Definition stk_common.h:484
@ STATE_TIMEOUT
Wait expired due to timeout rather than a Wake() signal.
Definition stk_common.h:483
@ STATE_SIGNALED
Wake signaled.
Definition stk_common.h:482
@ STATE_WAIT
Wait is active.
Definition stk_common.h:481
Synchronization object interface.
Definition stk_common.h:578
virtual void WakeAll()=0
Wake all tasks currently in the wait list.
DLEntryType ListEntryType
List entry type of ISyncObject elements.
Definition stk_common.h:591
virtual void WakeOne()=0
Wake the first task in the wait list (FIFO order).
DLHeadType ListHeadType
List head type for ISyncObject elements.
Definition stk_common.h:586
static void AddWaitObject(IWaitObject::ListHeadType &wlist, IWaitObject *wobj)
Called by kernel when a new task starts waiting on this event.
Definition stk_common.h:597
Weight FindWeightHigherThan(Weight comp) const
Find higher weight within linked wait objects.
Definition stk_helper.h:333
Interface for mutex synchronization primitive.
Definition stk_common.h:714
virtual void Unlock()=0
Unlock the mutex.
virtual void Lock()=0
Lock the mutex.
Interface for a user task.
Definition stk_common.h:772
virtual EAccessMode GetAccessMode() const =0
Get pointer to the stack memory.
virtual void OnExit()
Called by the kernel before removal from the scheduling (see stk::KERNEL_DYNAMIC).
Definition stk_common.h:869
Scheduling-strategy-facing interface for a kernel task slot.
Definition stk_common.h:902
Interface for a platform driver.
virtual void ProcessHardFault()=0
Cause a hard fault of the system.
Interface for a back-end event handler.
Interface for a task switching strategy implementation.
Interface for the implementation of the kernel of the scheduler. It supports Soft and Hard Real-Time ...
Interface for the kernel services exposed to the user processes during run-time when Kernel started s...
static IWaitObject::ListHeadType & GetWaitList(ISyncObject *sobj)
IWaitObject::GetWaitList() access helper.
static constexpr size_t Value
Definition stk_defs.h:675
Adapts an externally-owned stack memory array to the IStackMemory interface.
Definition stk_helper.h:189
StackMemoryDef< _StackSize >::Type MemoryType
Definition stk_helper.h:194
DLEntryType * GetNext()
Get the next entry in the list.
DLHeadType * GetHead()
Get the list head this entry currently belongs to.
static __stk_forceinline TTargetType * ListEntryToParent(TSourceType *const lentry)
Safely casts an intrusive list entry to its concrete parent container object type.