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
cmsis_os2_stk.cpp
Go to the documentation of this file.
1/*
2 * SuperTinyKernel(TM) RTOS: Lightweight High-Performance Deterministic C++ RTOS for Embedded Systems.
3 *
4 * Source: https://github.com/SuperTinyKernel-RTOS
5 *
6 * Copyright (c) 2022-2026 Neutron Code Limited <stk@neutroncode.com>. All Rights Reserved.
7 * License: MIT License, see LICENSE for a full text.
8 */
9
10#include "stk.h"
11#include "sync/stk_sync.h"
12#include "time/stk_time.h"
13#include "memory/stk_memory.h"
14
15// See design notes, API coverage and other details in cmsis_os2.h.
16
17#include "cmsis_os2.h"
18
19// -----------------------------------------------------------------------------
20// Kernel version / identification
21// -----------------------------------------------------------------------------
22
23#define STK_WRAPPER_API_VERSION 20030000UL // 2.3.0
24#define STK_WRAPPER_KERNEL_VERSION 20030000UL
25#define STK_WRAPPER_KERNEL_ID "STK RTOS2 Wrapper v1.1"
26
27// -----------------------------------------------------------------------------
28// Kernel configuration
29// -----------------------------------------------------------------------------
30
31// Number of task slots in the global kernel instance.
32// Increase if more concurrent threads are needed.
33#ifndef CMSIS_STK_MAX_THREADS
34#define CMSIS_STK_MAX_THREADS (16U)
35#endif
36
37// Default stack size (in Words) when the caller passes stack_size == 0.
38#ifndef CMSIS_STK_DEFAULT_STACK_WORDS
39#define CMSIS_STK_DEFAULT_STACK_WORDS (256U)
40#endif
41
42// Minimum stack size (in Words) – mirrors STK's own STACK_SIZE_MIN.
43#define CMSIS_STK_MIN_STACK_WORDS (STK_STACK_SIZE_MIN)
44
45// Returns a size of memory in stk::Word elements required for object allocation.
46template <typename T> static constexpr size_t StkGetWordCountForType()
47{
48 return ((sizeof(T) + sizeof(stk::Word) - 1U) / sizeof(stk::Word));
49}
50
51// Custom strlen replacement.
52static size_t CmsisStrlen(const char str[]) // MISRA: declared as array, not pointer to allow indexed access
53{
54 size_t total_len = 0U;
55
56 while (str[total_len] != '\0')
57 {
58 total_len++;
59 }
60
61 return total_len;
62}
63
64// Private memory allocators (we define malloc, free here to overcome absence of declaration in
65// case of -ffreestanding compiler flag).
66extern "C" void *malloc(size_t size);
67extern "C" void free(void *ptr);
68void *stk::memory::MemoryAllocator::Allocate(size_t size) { return malloc(size); }
69void stk::memory::MemoryAllocator::Free(void *ptr) { free(ptr); }
70
71// -----------------------------------------------------------------------------
72// Priority mapping:
73// CMSIS range: osPriorityIdle(1) .. osPriorityISR(56) -> 57 levels
74// STK FP32 range: 0 .. 31 -> 32 levels
75// Linear map: stk_prio = (cmsis_prio * 31) / 56
76// -----------------------------------------------------------------------------
78{
79 int32_t result;
80
81 if (p <= osPriorityIdle)
82 {
83 result = 0;
84 }
85 else if (p >= osPriorityISR)
86 {
87 result = 31;
88 }
89 else
90 {
91 result = ((static_cast<int32_t>(p) * 31) / 56);
92 }
93
94 return result;
95}
97{
98 // Inverse: cmsis_prio = (stk_prio * 56) / 31
99 int32_t r = (p * 56) / 31;
100 if (r < static_cast<int32_t>(osPriorityIdle))
101 {
102 r = static_cast<int32_t>(osPriorityIdle);
103 }
104
105 if (r > static_cast<int32_t>(osPriorityISR))
106 {
107 r = static_cast<int32_t>(osPriorityISR);
108 }
109
110 return static_cast<osPriority_t>(r);
111}
112
113// -----------------------------------------------------------------------------
114// Convert CMSIS timeout (ticks) -> STK timeout (ticks / milliseconds).
115//
116// CMSIS ticks == STK ticks when tick resolution is 1 ms (PERIODICITY_DEFAULT).
117// For other resolutions the conversion uses the kernel's tick resolution.
118// osWaitForever -> WAIT_INFINITE.
119// -----------------------------------------------------------------------------
121{
122 stk::Timeout result;
123
124 if (ticks > static_cast<uint32_t>(stk::WAIT_INFINITE))
125 {
126 result = stk::WAIT_INFINITE;
127 }
128 else if (ticks == 0U)
129 {
130 result = stk::NO_WAIT;
131 }
132 else
133 {
134 // CMSIS ticks are kernel ticks – pass through directly as STK ticks
135 result = static_cast<stk::Timeout>(ticks);
136 }
137
138 return result;
139}
140
141// -----------------------------------------------------------------------------
142// Map a failed timed wait to a CMSIS status: NO_WAIT -> osErrorResource (resource
143// not available), any other timeout -> osErrorTimeout.
144// -----------------------------------------------------------------------------
149
150// -----------------------------------------------------------------------------
151// Map the full outcome of an STK wait (the *Ex() APIs) to a CMSIS status.
152//
153// CMSIS has no "canceled" status, so a wait cancelled via IKernel::CancelTaskWait(),
154// or IKernelService::SleepCancel() and a wait rejected by the kernel (WAIT_RESULT_FAIL)
155// are both reported as the generic osError instead of being disguised as a timeout.
156// A timeout keeps the usual mapping: NO_WAIT -> osErrorResource, otherwise -> osErrorTimeout.
157// -----------------------------------------------------------------------------
159{
160 osStatus_t result;
161
162 switch (wr)
163 {
165 result = osOK;
166 break;
167
169 result = StkWaitFailToCmsis(timeout);
170 break;
171
174 default:
175 result = osError;
176 break;
177 }
178
179 return result;
180}
181
182// -----------------------------------------------------------------------------
183// ISR context check
184// -----------------------------------------------------------------------------
186{
187 return stk::hw::IsInsideISR();
188}
189
190// -----------------------------------------------------------------------------
191// Global kernel type alias
192// -----------------------------------------------------------------------------
194#if STK_TICKLESS_IDLE
196#endif
198
200static uint32_t g_StkKernelLocked = 0U;
201
202// -----------------------------------------------------------------------------
203// Thread control block
204//
205// StkThread wraps a single CMSIS thread.
206//
207// Stack memory is provided either by the caller (static allocation) or
208// allocated dynamically from operator new[] (heap allocation).
209//
210// The CMSIS function pointer + argument are stored here; the STK task's
211// Run() method simply calls func(argument).
212//
213// Priority is stored as STK weight (0..31) and returned via GetWeight().
214// -----------------------------------------------------------------------------
215
216// Detached threads which the kernel has already removed (OnExit() fired) and which are waiting to be freed
217// from task context by StkReapZombieThreads(). Chained via StkThread::m_argument. Pushed only from the
218// kernel tick (OnExit()), detached only inside a critical section, which masks the tick.
219//
220// Limitation: for a detached thread created with osThreadAttr_t::cb_mem, that memory is NOT safe to reuse until
221// the zombie has been reaped, because the list link (m_argument) lives inside the control block. osThreadNew()
222// reaps before it placement-news, so reusing the same cb_mem for another thread is fine; reusing it for anything
223// else before the reap corrupts the list. Reaping is also done in osThreadTerminate(), osThreadGetCount() and
224// osThreadEnumerate(), which narrows the window but does not close it.
225class StkThread;
226static StkThread *g_ZombieThreads = nullptr;
227
228class StkThread final : public stk::ITask
229{
231
232public:
233 // --- Join support ---
234 enum class JoinState : uint8_t
235 {
236 Detached, // osThreadDetach() was called, or created with osThreadDetached
237 Joinable, // created with osThreadJoinable, not yet joined or exited
238 Exited, // Run() has returned; OnExit() has fired; not yet joined
239 Joined, // a joiner has already collected the result; further joins are errors
240 };
241
242 explicit StkThread()
243 : m_func(nullptr), m_argument(nullptr), m_name(nullptr),
245 m_stack(nullptr), m_stack_size(0U), m_join_state(JoinState::Detached),
246 m_stack_owned(false), m_suspended(false), m_cb_owned(true)
247 {}
248
249 virtual ~StkThread()
250 {
251 if (m_stack_owned && (m_stack != nullptr))
252 {
253 delete[] m_stack;
254 m_stack = nullptr;
255 }
256 }
257
258 void Run() override
259 {
261
262 // KERNEL_DYNAMIC: returning from Run() removes the task automatically.
263 }
264
265 void OnExit() override
266 {
267 // Called by the kernel from the tick ISR when the task is removed.
269 {
270 // Nobody will join: do not free here (heap is not ISR-safe), queue for StkReapZombieThreads().
271 // The thread will never run again (its function returned, or it was terminated/exited via
272 // osThreadTerminate()/osThreadExit()), so m_argument is safe to reuse as the list link.
274 g_ZombieThreads = this;
275 }
276 else
277 {
279
280 // wake any osThreadJoin() caller
281 m_join_cv.NotifyAll();
282 }
283 }
284
285 // ---- IStackMemory ----
286 const stk::Word *GetStack() const override { return m_stack; }
287 size_t GetStackSize() const override { return m_stack_size; }
288
289 // ---- ITask ----
291 void OnDeadlineMissed(uint32_t) override {}
292 int32_t GetWeight() const override { return m_stk_priority; }
293 const char *GetTraceName() const override { return m_name; }
294
296 {
297 // tid is hw::PtrToWord(this) where 'this' is the StkThread* - cast it back.
298 return reinterpret_cast<osThreadId_t>(static_cast<void *>(
299 reinterpret_cast<StkThread *>(static_cast<uintptr_t>(tid))));
300 }
301
302 // ---- Members ----
305 const char *m_name;
306 volatile int32_t m_stk_priority; // STK priority level 0..31
307 stk::Word *m_stack; // pointer to stack memory (may be owned)
308 size_t m_stack_size; // stack size in Words
309 volatile JoinState m_join_state; // guarded by m_join_mutex between other tasks
311 stk::sync::EventFlags m_thread_flags; // Per-thread event flags - backed by STK's native 32-bit EventFlags primitive.
312 bool m_stack_owned; // true if we allocated the stack ourselves
313 bool m_suspended; // true if suspended
314 bool m_cb_owned; // true -> heap-allocated control block, delete on Terminate(), false -> caller-supplied cb_mem, call destructor explicitly
315};
316
317// -----------------------------------------------------------------------------
318// Mutex control block
319// -----------------------------------------------------------------------------
320
321// Select the STK primitive behind osMutexNew():
322// 1 -> mutexes created without osMutexRecursive use sync::FastMutex (non-recursive, smaller and faster, as
323// required by the CMSIS-RTOS2 specification: re-acquiring by the owner returns osErrorResource),
324// mutexes created with osMutexRecursive use sync::Mutex.
325// 0 -> all mutexes use sync::Mutex and are always recursive (osMutexRecursive is ignored).
326#ifndef STK_CMSIS_USE_FAST_MUTEX
327#define STK_CMSIS_USE_FAST_MUTEX 1
328#endif
329
330// Common part of both mutex control blocks: osMutexId_t always points to it, virtual calls dispatch to the derived type.
332{
333 explicit StkMutexBase() : m_cb_owned(true)
334 {}
335
337 {}
338
339 virtual const char *GetTraceName() const = 0;
340 virtual stk::TId GetOwner() const = 0;
341 virtual osStatus_t Acquire(stk::Timeout timeout) = 0;
342 virtual void Unlock() = 0;
343
344 bool m_cb_owned; // true -> heap-allocated, false -> placement-new in caller memory
345};
346
347// Recursive mutex (osMutexRecursive)
349{
350 explicit StkMutex(const char *n = nullptr) : StkMutexBase(), m_mutex()
351 {
352 m_mutex.SetTraceName(n);
353 }
354
355 const char *GetTraceName() const override { return m_mutex.GetTraceName(); }
356 stk::TId GetOwner() const override { return m_mutex.GetOwner(); }
357
359 {
360 return StkWaitResultToCmsis(m_mutex.TimedLockEx(timeout), timeout);
361 }
362
363 void Unlock() override { return m_mutex.Unlock(); }
364
365 // ---- Members ----
367};
368
369// Non-recursive mutex
371{
372 explicit StkFastMutex(const char *n = nullptr) : StkMutexBase(), m_mutex()
373 {
374 m_mutex.SetTraceName(n);
375 }
376
377 const char *GetTraceName() const override { return m_mutex.GetTraceName(); }
378 stk::TId GetOwner() const override { return m_mutex.GetOwner(); }
379
381 {
382 osStatus_t result;
383
384 // CMSIS-RTOS2: acquiring a non-recursive mutex already owned by the caller fails with osErrorResource
385 // (sync::FastMutex would panic with KERNEL_PANIC_SYNC_DEADLOCK). Only the owner itself can observe
386 // itself as the owner, so the check does not need a critical section.
387 if (m_mutex.GetOwner() == stk::GetTid())
388 {
389 result = osErrorResource;
390 }
391 else
392 {
393 result = StkWaitResultToCmsis(m_mutex.TimedLockEx(timeout), timeout);
394 }
395
396 return result;
397 }
398
399 void Unlock() override { return m_mutex.Unlock(); }
400
401 // ---- Members ----
403};
404
405// -----------------------------------------------------------------------------
406// Semaphore control block
407// -----------------------------------------------------------------------------
408
410{
411 explicit StkSemaphore(uint16_t initial, uint16_t max_count, const char *n = nullptr)
412 : m_semaphore(initial, max_count), m_cb_owned(true)
413 {
414 m_semaphore.SetTraceName(n);
415 }
416
417 // ---- Members ----
419 bool m_cb_owned; // true -> heap-allocated, false -> placement-new in caller memory
420};
421
422// -----------------------------------------------------------------------------
423// Event flags control block
424//
425// Backed directly by stk::sync::EventFlags - STK's native 32-bit multi-flag
426// synchronization primitive (Set/Clear/Get/Wait with ANY/ALL/NO_CLEAR options,
427// ISR-safe Set/Clear, absolute-deadline wait loop).
428// -----------------------------------------------------------------------------
429
431{
432 explicit StkEventFlags(const char *n = nullptr) : m_ef(0U), m_cb_owned(true)
433 {
434 m_ef.SetTraceName(n);
435 }
436
437 // ---- Members ----
439 bool m_cb_owned; // true -> heap-allocated, false -> placement-new in caller memory
440};
441
442// -----------------------------------------------------------------------------
443// Timer control block
444//
445// CMSIS timers are backed by stk::time::TimerHost.
446// A single global TimerHost is created on first osTimerNew().
447// -----------------------------------------------------------------------------
448
451
453{
455
456public:
457 explicit StkTimer(osTimerFunc_t const func, osTimerType_t tt, void *arg, const char *name)
458 : m_func(func), m_argument(arg), m_type(tt), m_name(name), m_period_ticks(0U), m_cb_owned(true)
459 {}
460 virtual ~StkTimer() = default;
461
462 void OnExpired(stk::time::TimerHost * /*host*/) override
463 {
465 }
466
468 {
469 if (g_TimerHost == nullptr)
470 {
473 }
474 }
475
476 // ---- Members ----
480 const char *m_name;
481 uint32_t m_period_ticks; // stored period for restart
482 bool m_cb_owned; // true -> heap-allocated, false -> placement-new in caller memory
483};
484
485// -----------------------------------------------------------------------------
486// Memory pool control block
487//
488// Backed directly by stk::memory::BlockMemoryPool.
489// -----------------------------------------------------------------------------
490
492{
494
495public:
496 // Construct with caller-supplied pool storage (storage_owned = false).
497 explicit StkMemPool(uint32_t cap, uint32_t raw_block_size,
498 const char *name, uint8_t *ext_storage)
499 : m_mpool(static_cast<size_t>(cap),
500 static_cast<size_t>(raw_block_size),
501 ext_storage,
502 cap * stk::memory::BlockMemoryPool::AlignBlockSize(raw_block_size),
503 name),
504 m_cb_owned(true)
505 {}
506
507 // Construct with heap-allocated pool storage (storage_owned = true).
508 explicit StkMemPool(uint32_t cap, uint32_t raw_block_size, const char *name)
509 : m_mpool(static_cast<size_t>(cap),
510 static_cast<size_t>(raw_block_size),
511 name),
512 m_cb_owned(true)
513 {}
514
515 // ---- Members ----
517 bool m_cb_owned; // true -> heap-allocated; false -> placement-new in caller memory
518};
519
520// -----------------------------------------------------------------------------
521// Message queue control block
522//
523// Backed by stk::sync::MessageQueue - STK's native fixed-capacity, fixed-
524// message-size FIFO ring-buffer with integrated blocking semantics (Put/Get
525// with WAIT_INFINITE / NO_WAIT / timed variants, ISR-safe TryPut/TryGet).
526//
527// Two independent memory regions are managed:
528//
529// Control block (cb_mem / cb_size in osMessageQueueAttr_t):
530// Policy identical to all other object types - PlacementNewOrHeap selects
531// placement-new into caller memory when cb_mem != nullptr and cb_size is
532// sufficient, otherwise heap. m_cb_owned tracks whether to free on Delete().
533//
534// Data buffer (mq_mem / mq_size in osMessageQueueAttr_t):
535// 1. Caller-supplied: attr->mq_mem / attr->mq_size are used as-is when the
536// region is large enough to hold (msg_count * msg_size) bytes.
537// 2. Dynamic fallback: heap buffer of (msg_count * msg_size) bytes.
538// Allocated buffer is freed in the destructor.
539// -----------------------------------------------------------------------------
540
542{
544
545public:
546 // Construct with a caller-supplied data buffer.
547 explicit StkMessageQueue(uint32_t cap, uint32_t msz, const char *name,uint8_t *ext_buf)
548 : m_mq(ext_buf, static_cast<size_t>(cap), static_cast<size_t>(msz)),
549 m_bf_owned(false), m_cb_owned(true)
550 {
551 m_mq.SetTraceName(name);
552 }
553
554 // Construct with a heap-allocated data buffer.
555 explicit StkMessageQueue(uint32_t cap, uint32_t msz, const char *name)
556 : m_mq(AllocBuffer(cap, msz), static_cast<size_t>(cap), static_cast<size_t>(msz)),
557 m_bf_owned(m_mq.IsStorageValid()), m_cb_owned(true)
558 {
559 m_mq.SetTraceName(name);
560 }
561
563 {
564 if (m_bf_owned)
565 {
566 delete[] m_mq.GetBuffer();
567 }
568 }
569
570 static uint8_t *AllocBuffer(uint32_t cap, uint32_t msz)
571 {
572 uint8_t *const ret = new (std::nothrow) uint8_t[static_cast<size_t>(cap) * msz];
573 STK_ASSERT(ret != nullptr); // fail in Debug: increase Heap size in linker settings
574 return ret;
575 }
576
577 // ---- Members ----
578 stk::sync::MessageQueue m_mq; // STK native message queue (owns blocking semantics)
579 bool m_bf_owned; // true -> when we heap-allocated the data buffer, false otherwise
580 bool m_cb_owned; // true -> heap-allocated control block (delete on Delete())
581 // false -> placement-new in caller memory (call dtor only)
582};
583
584// -----------------------------------------------------------------------------
585// Internal helpers
586// -----------------------------------------------------------------------------
587
588// Placement-new helper: construct T in caller-supplied memory when the block
589// is large enough; otherwise fall back to heap (operator new).
590// Sets obj->m_cb_owned = false for caller-supplied, true for heap.
591// Returns nullptr if heap fallback is needed but allocation fails.
592template <typename T, typename... Args>
593static T *PlacementNewOrHeap(void *mem, size_t size, const Args &... args)
594{
595 T *obj;
596
597 if ((mem != nullptr) && (size >= sizeof(T)))
598 {
599 obj = new (mem) T(args...);
600 obj->m_cb_owned = false;
601 }
602 else
603 {
604 obj = new (std::nothrow) T(args...);
605 // m_cb_owned is already true from the constructor default
606 STK_ASSERT(obj != nullptr);
607 }
608
609 return obj;
610}
611
612// Destroy an object created by PlacementNewOrHeap:
613// - m_cb_owned == false -> call destructor only (memory is caller's)
614// - m_cb_owned == true -> delete (destructor + free)
615template <typename T>
616static void ObjDestroy(T *obj)
617{
618 if (obj->m_cb_owned)
619 {
620 delete obj;
621 }
622 else
623 {
624 obj->~T();
625 }
626}
627
628// Free detached threads that the kernel has finished removing (see StkThread::OnExit()). Task context only.
630{
631 StkThread *list;
632
633 {
635
636 list = g_ZombieThreads;
637 g_ZombieThreads = nullptr;
638 }
639
640 while (list != nullptr)
641 {
642 StkThread *const next = static_cast<StkThread *>(list->m_argument);
643 ObjDestroy(list);
644 list = next;
645 }
646}
647
648// Helper: map CMSIS flags options -> STK EventFlags options bitmask.
649static __stk_forceinline uint32_t CmsisFlagsOptionsToStk(uint32_t options)
650{
651 uint32_t stk_opts = stk::sync::EventFlags::OPT_WAIT_ANY; // default
652
653 if ((options & osFlagsWaitAll) != 0U)
654 {
656 }
657
658 if ((options & osFlagsNoClear) != 0U)
659 {
661 }
662
663 return stk_opts;
664}
665
666// Helper: map STK EventFlags error sentinel -> CMSIS flags error code.
667static __stk_forceinline uint32_t StkFlagsResultToCmsis(uint32_t result)
668{
669 uint32_t cmsis_result;
670
672 {
673 cmsis_result = result;
674 }
675 else if (result == stk::sync::EventFlags::ERROR_TIMEOUT)
676 {
677 cmsis_result = osFlagsErrorTimeout;
678 }
680 {
681 cmsis_result = osFlagsErrorParameter;
682 }
683 else if (result == stk::sync::EventFlags::ERROR_ISR)
684 {
685 cmsis_result = osFlagsErrorISR;
686 }
687 else if ((result == stk::sync::EventFlags::ERROR_CANCELED) ||
689 {
690 // CMSIS has no "canceled" flags status: report cancellation and a kernel-rejected wait explicitly
691 cmsis_result = osFlagsErrorUnknown;
692 }
693 else
694 {
695 cmsis_result = osFlagsErrorUnknown;
696 }
697
698 return cmsis_result;
699}
700
701
702// ===========================================================================
703// ==== Kernel Management Functions ====
704// ===========================================================================
705
707{
708 osStatus_t result;
709
710 if (IsIrqContext())
711 {
712 result = osErrorISR;
713 }
715 {
716 result = osError;
717 }
718 else
719 {
720 g_StkKernel.Initialize(); // default 1 ms tick resolution
721 result = osOK;
722 }
723
724 return result;
725}
726
727osStatus_t osKernelGetInfo(osVersion_t *version, char *id_buf, uint32_t id_size)
728{
729 if (version != nullptr)
730 {
731 version->api = STK_WRAPPER_API_VERSION;
733 }
734
735 if ((id_buf != nullptr) && (id_size > 0U))
736 {
737 const char *const id = STK_WRAPPER_KERNEL_ID;
738 size_t copy_len = id_size - 1U;
739 const size_t id_len = CmsisStrlen(id);
740 if (copy_len > id_len)
741 {
742 copy_len = id_len;
743 }
744
745 STK_MEMCPY(id_buf, id, copy_len);
746 id_buf[copy_len] = '\0';
747 }
748
749 return osOK;
750}
751
753{
754 osKernelState_t state;
755
756 if (g_StkKernelLocked != 0U)
757 {
758 state = osKernelLocked;
759 }
760 else
761 {
762 switch (g_StkKernel.GetState())
763 {
765 state = osKernelInactive;
766 break;
768 state = osKernelReady;
769 break;
771 state = osKernelRunning;
772 break;
774 state = osKernelSuspended;
775 break;
776 default:
777 state = osKernelError;
778 break;
779 }
780 }
781
782 return state;
783}
784
786{
787 osStatus_t result;
788
789 if (IsIrqContext())
790 {
791 result = osErrorISR;
792 }
793 else if (osKernelGetState() != osKernelReady)
794 {
795 result = osError;
796 }
797 else
798 {
799 // Start() does not return for KERNEL_STATIC;
800 // for KERNEL_DYNAMIC it returns when all tasks exit.
801 g_StkKernel.Start();
802 result = osOK;
803 }
804
805 return result;
806}
807
808int32_t osKernelLock(void)
809{
810 int32_t result;
811
812 if (IsIrqContext())
813 {
814 result = static_cast<int32_t>(osErrorISR);
815 }
816 else
817 {
820 result = 0;
821 }
822
823 return result;
824}
825
826int32_t osKernelUnlock(void)
827{
828 int32_t result;
829
830 if (IsIrqContext())
831 {
832 result = static_cast<int32_t>(osErrorISR);
833 }
834 else if (g_StkKernelLocked == 0U)
835 {
836 result = static_cast<int32_t>(osErrorResource);
837 }
838 else
839 {
842 result = 0;
843 }
844
845 return result;
846}
847
848int32_t osKernelRestoreLock(int32_t lock)
849{
850 int32_t result;
851
852 if (IsIrqContext())
853 {
854 result = static_cast<int32_t>(osErrorISR);
855 }
856 else if (lock == 1)
857 {
860 result = lock;
861 }
862 else if (g_StkKernelLocked == 0U)
863 {
864 result = static_cast<int32_t>(osErrorResource);
865 }
866 else
867 {
870 result = lock;
871 }
872
873 return result;
874}
875
876uint32_t osKernelSuspend(void)
877{
878 uint32_t result;
879
880#if STK_TICKLESS_IDLE
882 {
883 result = 0U;
884 }
885 else
886 {
887 result = static_cast<uint32_t>(stk::IKernelService::GetInstance()->Suspend());
888 }
889#else
890 // Not supported in non-tickless kernel.
891 result = 0U;
892#endif
893
894 return result;
895}
896
897void osKernelResume(uint32_t sleep_ticks)
898{
899#if STK_TICKLESS_IDLE
901 {
903 }
904#else
905 // Not supported in non-tickless kernel.
906 STK_UNUSED(sleep_ticks);
907#endif
908}
909
911{
912 uint32_t result;
913
915 {
916 result = 0U;
917 }
918 else
919 {
920 result = static_cast<uint32_t>(stk::GetTicks());
921 }
922
923 return result;
924}
925
927{
928 uint32_t result;
929
931 {
932 result = 1000U; // default 1 kHz
933 }
934 else
935 {
936 const int32_t res_us = stk::GetTickResolution(); // us per tick
937 if (res_us <= 0)
938 {
939 result = 1000U;
940 }
941 else
942 {
943 result = (1000000U / static_cast<uint32_t>(res_us));
944 }
945 }
946
947 return result;
948}
949
951{
952 return static_cast<uint32_t>(stk::GetSysTimerCount());
953}
954
956{
957 return stk::GetSysTimerCount();
958}
959
961{
963}
964
965
966// ===========================================================================
967// ==== Thread Management Functions ====
968// ===========================================================================
969
970osThreadId_t osThreadNew(osThreadFunc_t func, void *argument, const osThreadAttr_t *attr)
971{
972 osThreadId_t result = nullptr;
973
974 const bool is_irq = IsIrqContext();
975 const osKernelState_t kernel_state = osKernelGetState();
976
977 // validate environment and arguments
978 bool is_valid = (!is_irq && (func != nullptr) && (kernel_state != osKernelInactive));
979
980 // validate priority value
981 osPriority_t cmsis_prio = osPriorityNormal;
982 if (is_valid && (attr != nullptr))
983 {
984 if (attr->priority != osPriorityNone)
985 {
986 cmsis_prio = attr->priority;
987 }
988
989 if ((cmsis_prio < osPriorityIdle) || (cmsis_prio > osPriorityISR))
990 {
991 is_valid = false; // invalidate object creation
992 }
993 }
994
995 // validate stack configuration:
996 // - caller-supplied stack (stack_mem != nullptr): used as-is, never silently enlarged beyond the buffer
997 // the caller gave us, so a zero/too small size or a misaligned address fails the call;
998 // - otherwise heap-allocated: stack_size is honored (never below the minimum), 0 -> default size.
999 stk::Word *stack_mem = nullptr;
1000 size_t stack_words = CMSIS_STK_DEFAULT_STACK_WORDS;
1001 if (is_valid && (attr != nullptr))
1002 {
1003 const size_t req_words = (attr->stack_size / sizeof(stk::Word)); // rounds down: never exceeds the buffer
1004
1005 if (attr->stack_mem != nullptr)
1006 {
1007 if ((req_words < static_cast<size_t>(CMSIS_STK_MIN_STACK_WORDS)) ||
1008 ((reinterpret_cast<uintptr_t>(attr->stack_mem) % alignof(stk::Word)) != 0U))
1009 {
1010 is_valid = false; // invalidate object creation
1011 }
1012 else
1013 {
1014 stack_mem = static_cast<stk::Word *>(attr->stack_mem);
1015 stack_words = req_words;
1016 }
1017 }
1018 else if (attr->stack_size != 0U)
1019 {
1020 stack_words = stk::Max<size_t>(req_words, CMSIS_STK_MIN_STACK_WORDS);
1021 }
1022 else
1023 {
1024 // noop: default heap-allocated stack size
1025 }
1026 }
1027
1028 if (is_valid)
1029 {
1030 // release resources of detached threads that have exited since the last call
1032
1033 const bool is_joinable = (attr != nullptr) && ((attr->attr_bits & osThreadJoinable) != 0U);
1034 void *const cb_mem = ((attr != nullptr) ? attr->cb_mem : nullptr);
1035 const uint32_t cb_size = ((attr != nullptr) ? attr->cb_size : 0U);
1036
1037 StkThread *th = PlacementNewOrHeap<StkThread>(cb_mem, cb_size);
1038 if (th != nullptr)
1039 {
1040 th->m_func = func;
1041 th->m_argument = argument;
1042 th->m_name = (attr != nullptr) ? attr->name : nullptr;
1044
1045 // stack configuration (validated above)
1046 if (stack_mem != nullptr)
1047 {
1048 th->m_stack = stack_mem;
1049 th->m_stack_size = stack_words;
1050 th->m_stack_owned = false;
1051 }
1052 // allocate stack memory if not caller-provided
1053 else
1054 {
1055 th->m_stack = new (std::nothrow) stk::Word[stack_words];
1056 STK_ASSERT(th->m_stack != nullptr);
1057
1058 if (th->m_stack == nullptr)
1059 {
1060 ObjDestroy(th);
1061 th = nullptr;
1062 }
1063 else
1064 {
1065 th->m_stack_size = stack_words;
1066 th->m_stack_owned = true;
1067 }
1068 }
1069
1070 // finalize and register the thread
1071 if (th != nullptr)
1072 {
1073 th->m_stk_priority = CmsisPrioToStk(cmsis_prio);
1074 g_StkKernel.AddTask(th);
1075 result = static_cast<osThreadId_t>(th);
1076 }
1077 }
1078 }
1079
1080 return result; // The solitary exit point
1081}
1082
1083const char *osThreadGetName(osThreadId_t thread_id)
1084{
1085 const char *result;
1086
1087 if (thread_id == nullptr)
1088 {
1089 result = nullptr;
1090 }
1091 else
1092 {
1093 result = static_cast<StkThread *>(thread_id)->m_name;
1094 }
1095
1096 return result;
1097}
1098
1100{
1101 osThreadId_t threadId = nullptr;
1102
1103 const osKernelState_t kernel_state = osKernelGetState();
1104 const bool is_irq = IsIrqContext();
1105
1106 if ((kernel_state != osKernelInactive) && !is_irq)
1107 {
1108 // STK's GetTid() returns the ITask pointer cast to Word.
1110 }
1111
1112 return threadId;
1113}
1114
1116{
1117 osThreadState_t threadState = osThreadError;
1118
1119 if (!IsIrqContext() && (thread_id != nullptr))
1120 {
1121 const StkThread *const th = static_cast<StkThread *>(thread_id);
1122
1123 if (th->m_suspended)
1124 {
1125 threadState = osThreadBlocked;
1126 }
1127 else if (thread_id == osThreadGetId())
1128 {
1129 threadState = osThreadRunning;
1130 }
1131 else
1132 {
1133 threadState = osThreadReady;
1134 }
1135 }
1136
1137 return threadState;
1138}
1139
1141{
1142 uint32_t result;
1143
1144 if (thread_id == nullptr)
1145 {
1146 result = 0U;
1147 }
1148 else
1149 {
1150 StkThread *const th = static_cast<StkThread *>(thread_id);
1151 result = static_cast<uint32_t>(th->GetStackSize()) * sizeof(stk::Word);
1152 }
1153
1154 return result;
1155}
1156
1158{
1159 uint32_t result;
1160
1161 if (thread_id == nullptr)
1162 {
1163 result = 0U;
1164 }
1165 else
1166 {
1167 StkThread *const th = static_cast<StkThread *>(thread_id);
1168 result = static_cast<uint32_t>(th->GetStackSpace() * sizeof(stk::Word));
1169 }
1170
1171 return result;
1172}
1173
1175{
1176 osStatus_t result;
1177
1178 if (IsIrqContext())
1179 {
1180 result = osErrorISR;
1181 }
1182 else if (thread_id == nullptr)
1183 {
1184 result = osErrorParameter;
1185 }
1186 else if ((priority < osPriorityIdle) || (priority > osPriorityISR))
1187 {
1188 result = osErrorParameter;
1189 }
1190 else
1191 {
1192 StkThread *const th = static_cast<StkThread *>(thread_id);
1193 th->m_stk_priority = CmsisPrioToStk(priority);
1194 result = osOK;
1195 }
1196
1197 return result;
1198}
1199
1201{
1202 osPriority_t result;
1203
1204 if (IsIrqContext() || (thread_id == nullptr))
1205 {
1206 result = osPriorityError;
1207 }
1208 else
1209 {
1210 StkThread *const th = static_cast<StkThread *>(thread_id);
1211 result = StkPrioToCmsis(th->m_stk_priority);
1212 }
1213
1214 return result;
1215}
1216
1218{
1219 osStatus_t result;
1220
1221 if (IsIrqContext())
1222 {
1223 result = osErrorISR;
1224 }
1225 else if (osKernelGetState() == osKernelInactive)
1226 {
1227 result = osError;
1228 }
1229 else
1230 {
1231 stk::Yield();
1232 result = osOK;
1233 }
1234
1235 return result;
1236}
1237
1239{
1240 osStatus_t result;
1241
1242 if (IsIrqContext())
1243 {
1244 result = osErrorISR;
1245 }
1246 else if (thread_id == nullptr)
1247 {
1248 result = osErrorParameter;
1249 }
1250 else if (osKernelGetState() == osKernelInactive)
1251 {
1252 result = osErrorParameter;
1253 }
1254 else
1255 {
1256 StkThread *const th = static_cast<StkThread *>(thread_id);
1257 g_StkKernel.SuspendTask(th, th->m_suspended);
1258 result = osOK;
1259 }
1260
1261 return result;
1262}
1263
1265{
1266 osStatus_t result;
1267
1268 if (IsIrqContext())
1269 {
1270 result = osErrorISR;
1271 }
1272 else if (thread_id == nullptr)
1273 {
1274 result = osErrorParameter;
1275 }
1276 else if (osKernelGetState() == osKernelInactive)
1277 {
1278 result = osErrorParameter;
1279 }
1280 else
1281 {
1282 StkThread *const th = static_cast<StkThread *>(thread_id);
1283
1285
1286 if (!th->m_suspended)
1287 {
1288 result = osOK; // not suspended, nothing to do
1289 }
1290 else
1291 {
1292 g_StkKernel.ResumeTask(th);
1293 th->m_suspended = false;
1294 result = osOK;
1295 }
1296 }
1297
1298 return result;
1299}
1300
1302{
1303 osStatus_t result;
1304
1305 if (IsIrqContext())
1306 {
1307 result = osErrorISR;
1308 }
1309 else if (thread_id == nullptr)
1310 {
1311 result = osErrorParameter;
1312 }
1313 else
1314 {
1315 StkThread *const th = static_cast<StkThread *>(thread_id);
1316
1318
1319 switch (th->m_join_state)
1320 {
1322 // already detached - CMSIS spec says this is an error
1323 result = osError;
1324 break;
1325
1327 // already joined - cannot detach
1328 result = osError;
1329 break;
1330
1332 // thread finished but nobody joined yet, transition to Detached
1333 // and free the control block now, since no joiner will ever do it
1335 ObjDestroy(th); // safe: task slot already freed by the kernel
1336 result = osOK;
1337 break;
1338
1340 // normal case: thread is still running or just hasn't been joined
1342 result = osOK;
1343 break;
1344
1345 default:
1346 result = osError;
1347 break;
1348 }
1349 }
1350
1351 return result;
1352}
1353
1355{
1356 osStatus_t result;
1357
1358 if (IsIrqContext())
1359 {
1360 result = osErrorISR;
1361 }
1362 else if (thread_id == nullptr)
1363 {
1364 result = osErrorParameter;
1365 }
1366 // Self-join is undefined behavior per POSIX / CMSIS spec.
1367 else if (thread_id == osThreadGetId())
1368 {
1369 result = osErrorParameter;
1370 }
1371 else
1372 {
1373 StkThread *th = static_cast<StkThread *>(thread_id);
1374 bool destroy = false;
1375
1376 {
1378
1379 // Only joinable threads can be joined.
1381 {
1382 result = osError;
1383 }
1384 // Double-join: second caller always gets an error.
1386 {
1387 result = osError;
1388 }
1389 else
1390 {
1391 // Only wait if the thread has not exited yet: if OnExit() already set Exited, its notification
1392 // has already happened and waiting for it would block forever.
1394 {
1396
1397 // Block until OnExit() fires (transitions state to Exited).
1398 // m_join_cv.Wait() atomically releases the critical section and suspends.
1400 {
1401 // WAIT_INFINITE - CMSIS osThreadJoin has no timeout parameter.
1402 // A cancelled wait is harmless (the predicate is re-checked), but a rejected one
1403 // (WAIT_RESULT_FAIL) returns at once without releasing cs_: bail out instead of
1404 // spinning with interrupts disabled. The state check below turns it into osError.
1406 {
1407 break;
1408 }
1409 }
1410 }
1411 // else Exited: OnExit() already fired, nothing to wait for
1412
1413 // At this point m_join_state == Exited (or Detached if someone
1414 // raced osThreadDetach - treat that as an error).
1416 {
1417 result = osError;
1418 }
1419 else
1420 {
1421 // Claim the control block: any further join/detach on it now fails with an error
1422 // instead of racing with the destruction below.
1424 destroy = true;
1425 result = osOK;
1426 }
1427 }
1428 }
1429
1430 if (destroy)
1431 {
1432 // OnExit() notifies m_join_cv from the kernel tick, which empties its wait list, but the kernel unlinks
1433 // the (now idle) condition variable from its active sync list only on the NEXT tick. If the joiner is
1434 // woken within the same tick it must not destroy the control block yet, otherwise that sync list would
1435 // reference freed memory. Sleeping one tick guarantees the kernel has processed it. Done outside of the
1436 // critical section, as sleeping inside of it would block the tick.
1437 stk::Sleep(1);
1438
1439 // Free the control block - the kernel has already freed the slot.
1440 ObjDestroy(th);
1441 }
1442 }
1443
1444 return result;
1445}
1446
1448{
1449 StkThread *const th = static_cast<StkThread *>(osThreadGetId());
1450
1451 g_StkKernel.ScheduleTaskRemoval(th);
1452
1453 // Wait for removal.
1454 for (;;)
1455 {
1456 stk::Yield();
1457 }
1458}
1459
1461{
1463 const bool is_active = (osKernelGetState() != osKernelInactive);
1464
1465 if ((thread_id != nullptr) && is_active)
1466 {
1467 // Terminating the calling thread: ScheduleTaskRemoval() only marks it, and the thread would keep
1468 // running until the next tick, so route it through the exit path instead.
1469 if (thread_id == osThreadGetId())
1470 {
1471 osThreadExit(); // does not return
1472 }
1473
1474 // release resources of detached threads that have already been removed by the kernel
1475 if (!IsIrqContext())
1476 {
1478 }
1479
1480 StkThread *const th = static_cast<StkThread *>(thread_id);
1481
1482 // avoid race conditions during termination
1484
1485 // RemoveTask triggers the STATE_REMOVE_PENDING path in the kernel,
1486 // which will call OnExit() before freeing the slot.
1487 g_StkKernel.ScheduleTaskRemoval(th);
1488
1489 // Do NOT free here: the kernel still references the control block (and the stack, which may be
1490 // in use until the task is switched out) and calls OnExit() on it from the tick.
1491 // Detached threads are queued by OnExit() and freed by StkReapZombieThreads(),
1492 // for joinable threads OnExit() wakes the joiner, which calls ObjDestroy().
1493 status = osOK;
1494 }
1495
1496 return status;
1497}
1498
1499uint32_t osThreadGetCount(void)
1500{
1501 uint32_t count = 0U;
1502
1504 {
1505 // release resources of detached threads that have already been removed by the kernel
1506 // (task context only: osThreadGetCount() may not be called from ISR per CMSIS, and the reap is skipped there)
1507 if (!IsIrqContext())
1508 {
1510 }
1511
1512 // avoid race with OnTick
1514
1515 count = static_cast<uint32_t>(g_StkKernel.GetSwitchStrategy()->GetSize());
1516 }
1517
1518 return count;
1519}
1520
1521uint32_t osThreadEnumerate(osThreadId_t *thread_array, uint32_t array_items)
1522{
1523 uint32_t result_count = 0U;
1524 const osKernelState_t kstate = osKernelGetState();
1525
1526 // kernel must be active and buffer must be valid
1527 if ((kstate != osKernelInactive) && (thread_array != nullptr) && (array_items != 0U))
1528 {
1529 // release resources of detached threads that have already been removed by the kernel
1530 if (!IsIrqContext())
1531 {
1533 }
1534
1535 // cast the raw pointer array to the expected ITask* destination type
1536 stk::ITask **const tasks_destination = reinterpret_cast<stk::ITask **>(
1537 reinterpret_cast<void *>(thread_array));
1538
1539 // bind raw destination buffer into a temporary ArrayView object
1540 const size_t count = g_StkKernel.EnumerateTasks(
1541 stk::ArrayView<stk::ITask *>(tasks_destination, static_cast<size_t>(array_items)));
1542
1543 result_count = static_cast<uint32_t>(count);
1544 }
1545
1546 return result_count;
1547}
1548
1549// ===========================================================================
1550// ==== Thread Flags Functions ====
1551// ===========================================================================
1552
1553uint32_t osThreadFlagsSet(osThreadId_t thread_id, uint32_t flags)
1554{
1555 uint32_t result;
1556
1557 if ((thread_id == nullptr) || ((flags & osFlagsError) != 0U))
1558 {
1559 result = osFlagsErrorParameter;
1560 }
1561 else
1562 {
1563 StkThread *th = static_cast<StkThread *>(thread_id);
1564 result = StkFlagsResultToCmsis(th->m_thread_flags.Set(flags));
1565 }
1566
1567 return result;
1568}
1569
1570uint32_t osThreadFlagsClear(uint32_t flags)
1571{
1572 uint32_t result;
1573
1574 osThreadId_t const self = osThreadGetId();
1575 if (self == nullptr)
1576 {
1577 result = osFlagsErrorUnknown;
1578 }
1579 else
1580 {
1581 StkThread *th = static_cast<StkThread *>(self);
1582 result = StkFlagsResultToCmsis(th->m_thread_flags.Clear(flags));
1583 }
1584
1585 return result;
1586}
1587
1588uint32_t osThreadFlagsGet(void)
1589{
1590 uint32_t result;
1591
1592 osThreadId_t const self = osThreadGetId();
1593 if (self == nullptr)
1594 {
1595 result = 0U;
1596 }
1597 else
1598 {
1599 result = static_cast<StkThread *>(self)->m_thread_flags.Get();
1600 }
1601
1602 return result;
1603}
1604
1605uint32_t osThreadFlagsWait(uint32_t flags, uint32_t options, uint32_t timeout)
1606{
1607 uint32_t result;
1608
1609 if (IsIrqContext())
1610 {
1611 result = osFlagsErrorISR;
1612 }
1613 else
1614 {
1615 osThreadId_t const self = osThreadGetId();
1616 if (self == nullptr)
1617 {
1618 result = osFlagsErrorUnknown;
1619 }
1620 else
1621 {
1622 StkThread *th = static_cast<StkThread *>(self);
1623 result = StkFlagsResultToCmsis(th->m_thread_flags.Wait(flags,
1624 CmsisFlagsOptionsToStk(options), CmsisTimeoutToStk(timeout)));
1625 }
1626 }
1627
1628 return result;
1629}
1630
1631
1632// ===========================================================================
1633// ==== Generic Wait Functions ====
1634// ===========================================================================
1635
1636osStatus_t osDelay(uint32_t ticks)
1637{
1638 osStatus_t result;
1639
1640 if (IsIrqContext())
1641 {
1642 result = osErrorISR;
1643 }
1644 else if (osKernelGetState() == osKernelInactive)
1645 {
1646 result = osError;
1647 }
1648 else
1649 {
1651 result = osOK;
1652 }
1653
1654 return result;
1655}
1656
1658{
1659 osStatus_t result;
1660
1661 if (IsIrqContext())
1662 {
1663 result = osErrorISR;
1664 }
1665 else if (osKernelGetState() == osKernelInactive)
1666 {
1667 result = osError;
1668 }
1669 else
1670 {
1671 result = (stk::SleepUntil(static_cast<stk::Ticks>(ticks)) ? osOK : osError);
1672 }
1673
1674 return result;
1675}
1676
1677
1678// ===========================================================================
1679// ==== Timer Management Functions ====
1680// ===========================================================================
1681
1683 const osTimerAttr_t *attr)
1684{
1685 osTimerId_t result;
1686
1687 if (IsIrqContext() || (func == nullptr))
1688 {
1689 result = nullptr;
1690 }
1691 else if (osKernelGetState() == osKernelInactive)
1692 {
1693 result = nullptr;
1694 }
1695 else
1696 {
1697 const char *const name = ((attr != nullptr) ? attr->name : nullptr);
1698 void *const cb_mem = ((attr != nullptr) ? attr->cb_mem : nullptr);
1699 const uint32_t cb_sz = ((attr != nullptr) ? attr->cb_size : 0U);
1700
1702
1703 StkTimer *const tmr = PlacementNewOrHeap<StkTimer>(cb_mem, cb_sz, func, type, argument, name);
1704 result = static_cast<osTimerId_t>(tmr);
1705 }
1706
1707 return result;
1708}
1709
1710const char *osTimerGetName(osTimerId_t timer_id)
1711{
1712 const char *result;
1713
1714 if (timer_id == nullptr)
1715 {
1716 result = nullptr;
1717 }
1718 else
1719 {
1720 result = static_cast<StkTimer *>(timer_id)->m_name;
1721 }
1722
1723 return result;
1724}
1725
1726osStatus_t osTimerStart(osTimerId_t timer_id, uint32_t ticks)
1727{
1728 osStatus_t result;
1729
1730 if (IsIrqContext())
1731 {
1732 result = osErrorISR;
1733 }
1734 else if ((timer_id == nullptr) || (g_TimerHost == nullptr))
1735 {
1736 result = osErrorParameter;
1737 }
1738 else
1739 {
1740 StkTimer *const tmr = static_cast<StkTimer *>(timer_id);
1741
1742 const uint32_t period_ticks = ((tmr->m_type == osTimerPeriodic) ? ticks : 0U);
1743 tmr->m_period_ticks = period_ticks;
1744
1745 const bool ok = g_TimerHost->Restart(*tmr, ticks, period_ticks);
1746 result = (ok ? osOK : osError);
1747 }
1748
1749 return result;
1750}
1751
1753{
1754 osStatus_t result;
1755
1756 if (IsIrqContext())
1757 {
1758 result = osErrorISR;
1759 }
1760 else if ((timer_id == nullptr) || (g_TimerHost == nullptr))
1761 {
1762 result = osErrorParameter;
1763 }
1764 else
1765 {
1766 StkTimer *const tmr = static_cast<StkTimer *>(timer_id);
1767
1768 if (!tmr->IsActive())
1769 {
1770 result = osErrorResource;
1771 }
1772 else
1773 {
1774 result = (g_TimerHost->Stop(*tmr) ? osOK : osError);
1775 }
1776 }
1777
1778 return result;
1779}
1780
1782{
1783 uint32_t result;
1784
1785 if (timer_id == nullptr)
1786 {
1787 result = 0U;
1788 }
1789 else
1790 {
1791 result = (static_cast<StkTimer *>(timer_id)->IsActive() ? 1U : 0U);
1792 }
1793
1794 return result;
1795}
1796
1798{
1799 osStatus_t result;
1800
1801 if (IsIrqContext())
1802 {
1803 result = osErrorISR;
1804 }
1805 else if ((timer_id == nullptr) || (g_TimerHost == nullptr))
1806 {
1807 result = osErrorParameter;
1808 }
1809 else
1810 {
1811 result = osOK;
1812
1813 StkTimer *const tmr = static_cast<StkTimer *>(timer_id);
1814
1815 if (tmr->IsActive())
1816 {
1817 if (!g_TimerHost->Stop(*tmr))
1818 {
1819 result = osError;
1820 }
1821 }
1822
1823 if (result == osOK)
1824 {
1825 ObjDestroy(tmr);
1826 }
1827 }
1828
1829 return result;
1830}
1831
1832
1833// ===========================================================================
1834// ==== Event Flags Management Functions ====
1835// ===========================================================================
1836
1838{
1839 osEventFlagsId_t result;
1840
1841 if (IsIrqContext())
1842 {
1843 result = nullptr;
1844 }
1845 else
1846 {
1847 const char *const name = ((attr != nullptr) ? attr->name : nullptr);
1848 void *const cb_mem = ((attr != nullptr) ? attr->cb_mem : nullptr);
1849 const uint32_t cb_sz = ((attr != nullptr) ? attr->cb_size : 0U);
1850
1851 StkEventFlags *const ef = PlacementNewOrHeap<StkEventFlags>(cb_mem, cb_sz, name);
1852 result = static_cast<osEventFlagsId_t>(ef);
1853 }
1854
1855 return result;
1856}
1857
1859{
1860 const char *result;
1861
1862 if (ef_id == nullptr)
1863 {
1864 result = nullptr;
1865 }
1866 else
1867 {
1868 result = static_cast<StkEventFlags *>(ef_id)->m_ef.GetTraceName();
1869 }
1870
1871 return result;
1872}
1873
1874uint32_t osEventFlagsSet(osEventFlagsId_t ef_id, uint32_t flags)
1875{
1876 uint32_t result;
1877
1878 if ((ef_id == nullptr) || ((flags & osFlagsError) != 0U))
1879 {
1880 result = osFlagsErrorParameter;
1881 }
1882 else
1883 {
1884 result = StkFlagsResultToCmsis(static_cast<StkEventFlags *>(ef_id)->m_ef.Set(flags));
1885 }
1886
1887 return result;
1888}
1889
1890uint32_t osEventFlagsClear(osEventFlagsId_t ef_id, uint32_t flags)
1891{
1892 uint32_t result;
1893
1894 if ((ef_id == nullptr) || ((flags & osFlagsError) != 0U))
1895 {
1896 result = osFlagsErrorParameter;
1897 }
1898 else
1899 {
1900 result = StkFlagsResultToCmsis(static_cast<StkEventFlags *>(ef_id)->m_ef.Clear(flags));
1901 }
1902
1903 return result;
1904}
1905
1907{
1908 uint32_t result;
1909
1910 if (ef_id == nullptr)
1911 {
1912 result = 0U;
1913 }
1914 else
1915 {
1916 result = static_cast<StkEventFlags *>(ef_id)->m_ef.Get();
1917 }
1918
1919 return result;
1920}
1921
1922uint32_t osEventFlagsWait(osEventFlagsId_t ef_id, uint32_t flags, uint32_t options,
1923 uint32_t timeout)
1924{
1925 uint32_t result;
1926
1927 if ((ef_id == nullptr) || ((flags & osFlagsError) != 0U))
1928 {
1929 result = osFlagsErrorParameter;
1930 }
1931 else
1932 {
1933 result = StkFlagsResultToCmsis(static_cast<StkEventFlags *>(ef_id)->m_ef.Wait(flags,
1934 CmsisFlagsOptionsToStk(options), CmsisTimeoutToStk(timeout)));
1935 }
1936
1937 return result;
1938}
1939
1941{
1942 osStatus_t result;
1943
1944 if (IsIrqContext())
1945 {
1946 result = osErrorISR;
1947 }
1948 else if (ef_id == nullptr)
1949 {
1950 result = osErrorParameter;
1951 }
1952 else
1953 {
1954 ObjDestroy(static_cast<StkEventFlags *>(ef_id));
1955 result = osOK;
1956 }
1957
1958 return result;
1959}
1960
1961
1962// ===========================================================================
1963// ==== Mutex Management Functions ====
1964// ===========================================================================
1965
1966
1968{
1969 osMutexId_t result;
1970
1971 if (IsIrqContext())
1972 {
1973 result = nullptr;
1974 }
1975 else
1976 {
1977 // osMutexPrioInherit: ignored, supported by default.
1978 // osMutexRecursive: sync::Mutex if set, otherwise sync::FastMutex (non-recursive) unless
1979 // STK_CMSIS_USE_FAST_MUTEX is 0, in which case sync::Mutex is always used.
1980 // osMutexRobust: will assert as unsafe code.
1981 const char *const name = ((attr != nullptr) ? attr->name : nullptr);
1982 void *const cb_mem = ((attr != nullptr) ? attr->cb_mem : nullptr);
1983 const uint32_t cb_sz = ((attr != nullptr) ? attr->cb_size : 0U);
1984 const bool robust = ((attr != nullptr) && ((attr->attr_bits & osMutexRobust) != 0U));
1985 const bool recursive = ((STK_CMSIS_USE_FAST_MUTEX == 0) ||
1986 ((attr != nullptr) && ((attr->attr_bits & osMutexRecursive) != 0U)));
1987
1988 // disallow osMutexRobust
1989 STK_ASSERT(!robust);
1990 if (robust)
1991 {
1992 result = nullptr;
1993 }
1994 else
1995 {
1996 // convert to the common base pointer first: osMutexId_t always holds a StkMutexBase pointer
1997 StkMutexBase *mb;
1998
1999 if (recursive)
2000 mb = PlacementNewOrHeap<StkMutex>(cb_mem, cb_sz, name);
2001 else
2002 mb = PlacementNewOrHeap<StkFastMutex>(cb_mem, cb_sz, name);
2003
2004 result = static_cast<osMutexId_t>(mb);
2005 }
2006 }
2007
2008 return result;
2009}
2010
2011const char *osMutexGetName(osMutexId_t mutex_id)
2012{
2013 const char *result;
2014
2015 if (mutex_id == nullptr)
2016 {
2017 result = nullptr;
2018 }
2019 else
2020 {
2021 result = static_cast<const StkMutexBase *>(mutex_id)->GetTraceName();
2022 }
2023
2024 return result;
2025}
2026
2027osStatus_t osMutexAcquire(osMutexId_t mutex_id, uint32_t timeout)
2028{
2029 osStatus_t result;
2030
2031 if (IsIrqContext())
2032 {
2033 result = osErrorISR;
2034 }
2035 else if (mutex_id == nullptr)
2036 {
2037 result = osErrorParameter;
2038 }
2039 else
2040 {
2041 result = static_cast<StkMutexBase *>(mutex_id)->Acquire(CmsisTimeoutToStk(timeout));
2042 }
2043
2044 return result;
2045}
2046
2048{
2049 osStatus_t result;
2050
2051 if (IsIrqContext())
2052 {
2053 result = osErrorISR;
2054 }
2055 else if (mutex_id == nullptr)
2056 {
2057 result = osErrorParameter;
2058 }
2059 else
2060 {
2061 StkMutexBase *const mb = static_cast<StkMutexBase *>(mutex_id);
2062
2063 // CMSIS-RTOS2: releasing a mutex which is not owned by the caller fails with osErrorResource
2064 if (mb->GetOwner() != stk::GetTid())
2065 {
2066 result = osErrorResource;
2067 }
2068 else
2069 {
2070 mb->Unlock();
2071 result = osOK;
2072 }
2073 }
2074
2075 return result;
2076}
2077
2079{
2080 osThreadId_t result;
2081
2082 if (mutex_id == nullptr)
2083 {
2084 result = nullptr;
2085 }
2086 else
2087 {
2088 result = StkThread::ConvertTIdToThreadId(static_cast<const StkMutexBase *>(mutex_id)->GetOwner());
2089 }
2090
2091 return result;
2092}
2093
2095{
2096 osStatus_t result;
2097
2098 if (IsIrqContext())
2099 {
2100 result = osErrorISR;
2101 }
2102 else if (mutex_id == nullptr)
2103 {
2104 result = osErrorParameter;
2105 }
2106 else
2107 {
2108 ObjDestroy(static_cast<StkMutexBase *>(mutex_id));
2109 result = osOK;
2110 }
2111
2112 return result;
2113}
2114
2115// ===========================================================================
2116// ==== Semaphore Management Functions ====
2117// ===========================================================================
2118
2119osSemaphoreId_t osSemaphoreNew(uint32_t max_count, uint32_t initial_count,
2120 const osSemaphoreAttr_t *attr)
2121{
2122 osSemaphoreId_t result;
2123
2124 if (IsIrqContext() || (max_count == 0U) || (initial_count > max_count))
2125 {
2126 result = nullptr;
2127 }
2128 else
2129 {
2130 // STK Semaphore uses uint16_t counters, clamp to stk::sync::Semaphore::COUNT_MAX.
2131 const uint16_t mc = static_cast<uint16_t>(stk::Min(max_count, static_cast<uint32_t>(stk::sync::Semaphore::COUNT_MAX)));
2132 const uint16_t ic = static_cast<uint16_t>(stk::Min(initial_count, static_cast<uint32_t>(stk::sync::Semaphore::COUNT_MAX)));
2133
2134 const char *const name = ((attr != nullptr) ? attr->name : nullptr);
2135 void *const cb_mem = ((attr != nullptr) ? attr->cb_mem : nullptr);
2136 const uint32_t cb_sz = ((attr != nullptr) ? attr->cb_size : 0U);
2137
2138 StkSemaphore *const sem = PlacementNewOrHeap<StkSemaphore>(cb_mem, cb_sz, ic, mc, name);
2139 result = static_cast<osSemaphoreId_t>(sem);
2140 }
2141
2142 return result;
2143}
2144
2145const char *osSemaphoreGetName(osSemaphoreId_t semaphore_id)
2146{
2147 const char *result;
2148
2149 if (semaphore_id == nullptr)
2150 {
2151 result = nullptr;
2152 }
2153 else
2154 {
2155 result = static_cast<StkSemaphore *>(semaphore_id)->m_semaphore.GetTraceName();
2156 }
2157
2158 return result;
2159}
2160
2161osStatus_t osSemaphoreAcquire(osSemaphoreId_t semaphore_id, uint32_t timeout)
2162{
2163 osStatus_t result;
2164
2165 if (semaphore_id == nullptr)
2166 {
2167 result = osErrorParameter;
2168 }
2169 else if (IsIrqContext() && (timeout != 0U))
2170 {
2171 result = osErrorISR;
2172 }
2173 else
2174 {
2175 StkSemaphore *sem = static_cast<StkSemaphore *>(semaphore_id);
2176 const stk::Timeout stk_timeout = CmsisTimeoutToStk(timeout);
2177
2178 result = StkWaitResultToCmsis(sem->m_semaphore.WaitEx(stk_timeout), stk_timeout);
2179 }
2180
2181 return result;
2182}
2183
2185{
2186 osStatus_t result;
2187
2188 if (semaphore_id == nullptr)
2189 {
2190 result = osErrorParameter;
2191 }
2192 else
2193 {
2194 static_cast<StkSemaphore *>(semaphore_id)->m_semaphore.Signal();
2195 result = osOK;
2196 }
2197
2198 return result;
2199}
2200
2202{
2203 uint32_t result;
2204
2205 if (semaphore_id == nullptr)
2206 {
2207 result = 0U;
2208 }
2209 else
2210 {
2211 result = static_cast<uint32_t>(
2212 static_cast<StkSemaphore *>(semaphore_id)->m_semaphore.GetCount());
2213 }
2214
2215 return result;
2216}
2217
2219{
2220 osStatus_t result;
2221
2222 if (IsIrqContext())
2223 {
2224 result = osErrorISR;
2225 }
2226 else if (semaphore_id == nullptr)
2227 {
2228 result = osErrorParameter;
2229 }
2230 else
2231 {
2232 ObjDestroy(static_cast<StkSemaphore *>(semaphore_id));
2233 result = osOK;
2234 }
2235
2236 return result;
2237}
2238
2239
2240// ===========================================================================
2241// ==== Memory Pool Management Functions ====
2242// ===========================================================================
2243
2244osMemoryPoolId_t osMemoryPoolNew(uint32_t block_count, uint32_t block_size,
2245 const osMemoryPoolAttr_t *attr)
2246{
2247 osMemoryPoolId_t result;
2248
2249 // ISR context: forbidden per CMSIS spec.
2250 // Zero capacity or zero block size are meaningless.
2251 if (IsIrqContext() || (block_count == 0U) || (block_size == 0U))
2252 {
2253 result = nullptr;
2254 }
2255 else
2256 {
2257 const char *const name = ((attr != nullptr) ? attr->name : nullptr);
2258 void *const cb_mem = ((attr != nullptr) ? attr->cb_mem : nullptr);
2259 const uint32_t cb_sz = ((attr != nullptr) ? attr->cb_size : 0U);
2260 void *const mp_mem = ((attr != nullptr) ? attr->mp_mem : nullptr);
2261 const uint32_t mp_sz = ((attr != nullptr) ? attr->mp_size : 0U);
2262
2263 // Compute the aligned block size and required storage byte count.
2264 const uint32_t aligned_blk = stk::memory::BlockMemoryPool::AlignBlockSize(block_size);
2265 const uint32_t storage_required = (block_count * aligned_blk);
2266
2267 StkMemPool *pool;
2268
2269 if ((mp_mem != nullptr) && (mp_sz >= storage_required))
2270 {
2271 // Caller-supplied pool storage - BlockMemoryPool external-storage ctor.
2272 pool = PlacementNewOrHeap<StkMemPool>(cb_mem, cb_sz,
2273 block_count, block_size, name, static_cast<uint8_t *>(mp_mem));
2274 }
2275 else
2276 {
2277 // Heap-allocated pool storage - BlockMemoryPool heap ctor.
2278 pool = PlacementNewOrHeap<StkMemPool>(cb_mem, cb_sz,
2279 block_count, block_size, name);
2280
2281 // If the heap ctor failed to allocate storage, clean up and bail.
2282 if ((pool != nullptr) && !pool->m_mpool.IsStorageValid())
2283 {
2284 ObjDestroy(pool);
2285 pool = nullptr;
2286 }
2287 }
2288
2289 result = static_cast<osMemoryPoolId_t>(pool);
2290 }
2291
2292 return result;
2293}
2294
2296{
2297 const char *result;
2298
2299 if (mp_id == nullptr)
2300 {
2301 result = nullptr;
2302 }
2303 else
2304 {
2305 result = static_cast<StkMemPool *>(mp_id)->m_mpool.GetTraceName();
2306 }
2307
2308 return result;
2309}
2310
2311void *osMemoryPoolAlloc(osMemoryPoolId_t mp_id, uint32_t timeout)
2312{
2313 void *result;
2314
2315 if (mp_id == nullptr)
2316 {
2317 result = nullptr;
2318 }
2319 // ISR context is only valid with timeout == 0 (NO_WAIT / TryAlloc).
2320 else if (IsIrqContext() && (timeout != 0U))
2321 {
2322 result = nullptr;
2323 }
2324 else
2325 {
2326 result = static_cast<StkMemPool *>(mp_id)->m_mpool.TimedAlloc(CmsisTimeoutToStk(timeout));
2327 }
2328
2329 return result;
2330}
2331
2333{
2334 osStatus_t result;
2335
2336 if ((mp_id == nullptr) || (block == nullptr))
2337 {
2338 result = osErrorParameter;
2339 }
2340 else if (!static_cast<StkMemPool *>(mp_id)->m_mpool.Free(block))
2341 {
2342 result = osErrorParameter; // ptr not from this pool
2343 }
2344 else
2345 {
2346 result = osOK;
2347 }
2348
2349 return result;
2350}
2351
2353{
2354 uint32_t result;
2355
2356 if (mp_id == nullptr)
2357 {
2358 result = 0U;
2359 }
2360 else
2361 {
2362 result = static_cast<StkMemPool *>(mp_id)->m_mpool.GetCapacity();
2363 }
2364
2365 return result;
2366}
2367
2369{
2370 uint32_t result;
2371
2372 if (mp_id == nullptr)
2373 {
2374 result = 0U;
2375 }
2376 else
2377 {
2378 result = static_cast<uint32_t>(static_cast<StkMemPool *>(mp_id)->m_mpool.GetBlockSize());
2379 }
2380
2381 return result;
2382}
2383
2385{
2386 uint32_t result;
2387
2388 if (mp_id == nullptr)
2389 {
2390 result = 0U;
2391 }
2392 else
2393 {
2394 result = static_cast<StkMemPool *>(mp_id)->m_mpool.GetUsedCount();
2395 }
2396
2397 return result;
2398}
2399
2401{
2402 uint32_t result;
2403
2404 if (mp_id == nullptr)
2405 {
2406 result = 0U;
2407 }
2408 else
2409 {
2410 result = static_cast<StkMemPool *>(mp_id)->m_mpool.GetFreeCount();
2411 }
2412
2413 return result;
2414}
2415
2417{
2418 osStatus_t result;
2419
2420 if (IsIrqContext())
2421 {
2422 result = osErrorISR;
2423 }
2424 else if (mp_id == nullptr)
2425 {
2426 result = osErrorParameter;
2427 }
2428 else
2429 {
2430 ObjDestroy(static_cast<StkMemPool *>(mp_id));
2431 result = osOK;
2432 }
2433
2434 return result;
2435}
2436
2437
2438// ===========================================================================
2439// ==== Message Queue Management Functions ====
2440// ===========================================================================
2441
2442osMessageQueueId_t osMessageQueueNew(uint32_t msg_count, uint32_t msg_size,
2443 const osMessageQueueAttr_t *attr)
2444{
2445 osMessageQueueId_t result;
2446
2447 if (IsIrqContext() || (msg_count == 0U) || (msg_size == 0U) ||
2449 {
2450 result = nullptr;
2451 }
2452 else
2453 {
2454 const char *const name = ((attr != nullptr) ? attr->name : nullptr);
2455 void *const cb_mem = ((attr != nullptr) ? attr->cb_mem : nullptr);
2456 const uint32_t cb_sz = ((attr != nullptr) ? attr->cb_size : 0U);
2457 void *const ext_buf = ((attr != nullptr) ? attr->mq_mem : nullptr);
2458 const uint32_t ext_buf_size = ((attr != nullptr) ? attr->mq_size : 0U);
2459
2460 const uint32_t buf_required = (msg_count * msg_size);
2461
2462 StkMessageQueue *mq;
2463
2464 if ((ext_buf != nullptr) && (ext_buf_size >= buf_required))
2465 {
2466 // Data buffer: use caller-supplied memory.
2467 mq = PlacementNewOrHeap<StkMessageQueue>(cb_mem, cb_sz,
2468 msg_count, msg_size, name, static_cast<uint8_t *>(ext_buf));
2469 }
2470 else
2471 {
2472 // Data buffer: heap-allocated inside StkMessageQueue constructor.
2473 mq = PlacementNewOrHeap<StkMessageQueue>(cb_mem, cb_sz,
2474 msg_count, msg_size, name);
2475
2476 // Validate
2477 if (mq != nullptr)
2478 {
2479 if (!mq->m_mq.IsStorageValid())
2480 {
2481 ObjDestroy(mq);
2482 mq = nullptr;
2483 }
2484 }
2485 }
2486
2487 result = static_cast<osMessageQueueId_t>(mq);
2488 }
2489
2490 return result;
2491}
2492
2494{
2495 const char *result;
2496
2497 if (mq_id == nullptr)
2498 {
2499 result = nullptr;
2500 }
2501 else
2502 {
2503 result = static_cast<StkMessageQueue *>(mq_id)->m_mq.GetTraceName();
2504 }
2505
2506 return result;
2507}
2508
2510 uint8_t /*msg_prio*/, uint32_t timeout)
2511{
2512 osStatus_t result;
2513
2514 if ((mq_id == nullptr) || (msg_ptr == nullptr))
2515 {
2516 result = osErrorParameter;
2517 }
2518 else if (IsIrqContext() && (timeout != 0U))
2519 {
2520 result = osErrorISR;
2521 }
2522 else
2523 {
2524 const stk::Timeout stk_timeout = CmsisTimeoutToStk(timeout);
2525
2526 result = StkWaitResultToCmsis(
2527 static_cast<StkMessageQueue *>(mq_id)->m_mq.PutEx(msg_ptr, stk_timeout), stk_timeout);
2528 }
2529
2530 return result;
2531}
2532
2534 uint8_t *msg_prio, uint32_t timeout)
2535{
2536 osStatus_t result;
2537
2538 if ((mq_id == nullptr) || (msg_ptr == nullptr))
2539 {
2540 result = osErrorParameter;
2541 }
2542 else if (IsIrqContext() && (timeout != 0U))
2543 {
2544 result = osErrorISR;
2545 }
2546 else
2547 {
2548 const stk::Timeout stk_timeout = CmsisTimeoutToStk(timeout);
2549
2550 result = StkWaitResultToCmsis(
2551 static_cast<StkMessageQueue *>(mq_id)->m_mq.GetEx(msg_ptr, stk_timeout), stk_timeout);
2552
2553 if ((result == osOK) && (msg_prio != nullptr))
2554 {
2555 *msg_prio = 0U; // STK queues have no priority lanes.
2556 }
2557 }
2558
2559 return result;
2560}
2561
2563{
2564 uint32_t result;
2565
2566 if (mq_id == nullptr)
2567 {
2568 result = 0U;
2569 }
2570 else
2571 {
2572 result = static_cast<uint32_t>(static_cast<StkMessageQueue *>(mq_id)->m_mq.GetCapacity());
2573 }
2574
2575 return result;
2576}
2577
2579{
2580 uint32_t result;
2581
2582 if (mq_id == nullptr)
2583 {
2584 result = 0U;
2585 }
2586 else
2587 {
2588 result = static_cast<uint32_t>(static_cast<StkMessageQueue *>(mq_id)->m_mq.GetMsgSize());
2589 }
2590
2591 return result;
2592}
2593
2595{
2596 uint32_t result;
2597
2598 if (mq_id == nullptr)
2599 {
2600 result = 0U;
2601 }
2602 else
2603 {
2604 result = static_cast<uint32_t>(static_cast<StkMessageQueue *>(mq_id)->m_mq.GetCount());
2605 }
2606
2607 return result;
2608}
2609
2611{
2612 uint32_t result;
2613
2614 if (mq_id == nullptr)
2615 {
2616 result = 0U;
2617 }
2618 else
2619 {
2620 result = static_cast<uint32_t>(static_cast<StkMessageQueue *>(mq_id)->m_mq.GetSpace());
2621 }
2622
2623 return result;
2624}
2625
2627{
2628 osStatus_t result;
2629
2630 if (IsIrqContext())
2631 {
2632 result = osErrorISR;
2633 }
2634 else if (mq_id == nullptr)
2635 {
2636 result = osErrorParameter;
2637 }
2638 else
2639 {
2640 static_cast<StkMessageQueue *>(mq_id)->m_mq.Reset();
2641 result = osOK;
2642 }
2643
2644 return result;
2645}
2646
2648{
2649 osStatus_t result;
2650
2651 if (IsIrqContext())
2652 {
2653 result = osErrorISR;
2654 }
2655 else if (mq_id == nullptr)
2656 {
2657 result = osErrorParameter;
2658 }
2659 else
2660 {
2661 ObjDestroy(static_cast<StkMessageQueue *>(mq_id));
2662 result = osOK;
2663 }
2664
2665 return result;
2666}
Collection of memory-related primitives (stk::memory namespace).
Top-level STK include. Provides the Kernel class template and all built-in task-switching strategies.
static void STK_MEMCPY(void *const dest, const void *const src, const size_t size)
Implementation of STK_MEMCPY.
Definition stk_arch.h:731
#define STK_UNUSED(X)
Explicitly marks a variable as unused to suppress compiler warnings.
Definition stk_defs.h:715
#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
Collection of synchronization primitives (stk::sync namespace).
Collection of time-related primitives (stk::time namespace).
static constexpr size_t StkGetWordCountForType()
void * malloc(size_t size)
void free(void *ptr)
CMSIS RTOS2 interface for SuperTinyKernel RTOS.
#define __NO_RETURN
Definition cmsis_os2.h:80
static size_t CmsisStrlen(const char str[])
stk::Kernel< stk::KERNEL_DYNAMIC|stk::KERNEL_SYNC|stk::KERNEL_TICKLESS,(16U), stk::SwitchStrategyFP32, stk::PlatformDefault > StkKernel
static uint32_t g_StkKernelLocked
static StkKernel g_StkKernel
static StkThread * g_ZombieThreads
static __stk_forceinline bool IsIrqContext()
static __stk_forceinline uint32_t StkFlagsResultToCmsis(uint32_t result)
static stk::time::TimerHost * g_TimerHost
static constexpr size_t StkGetWordCountForType()
#define CMSIS_STK_MIN_STACK_WORDS
#define CMSIS_STK_MAX_THREADS
static stk::Word g_TimerHostBuf[StkGetWordCountForType< stk::time::TimerHost >()]
static __stk_forceinline stk::Timeout CmsisTimeoutToStk(uint32_t ticks)
static void StkReapZombieThreads()
static __stk_forceinline osStatus_t StkWaitResultToCmsis(stk::EWaitResult wr, stk::Timeout timeout)
void * malloc(size_t size)
static void ObjDestroy(T *obj)
#define CMSIS_STK_DEFAULT_STACK_WORDS
#define STK_WRAPPER_KERNEL_VERSION
#define STK_WRAPPER_KERNEL_ID
#define STK_CMSIS_USE_FAST_MUTEX
static __stk_forceinline osPriority_t StkPrioToCmsis(int32_t p)
static __stk_forceinline osStatus_t StkWaitFailToCmsis(stk::Timeout timeout)
static __stk_forceinline uint32_t CmsisFlagsOptionsToStk(uint32_t options)
static __stk_forceinline int32_t CmsisPrioToStk(osPriority_t p)
static T * PlacementNewOrHeap(void *mem, size_t size, const Args &... args)
#define STK_WRAPPER_API_VERSION
void free(void *ptr)
#define osFlagsError
Error indicator.
Definition cmsis_os2.h:254
uint32_t osMemoryPoolGetSpace(osMemoryPoolId_t mp_id)
osKernelState_t
Kernel state.
Definition cmsis_os2.h:155
osStatus_t osSemaphoreRelease(osSemaphoreId_t semaphore_id)
osPriority_t osThreadGetPriority(osThreadId_t thread_id)
const char * osMutexGetName(osMutexId_t mutex_id)
void * osMessageQueueId_t
Definition cmsis_os2.h:329
#define osThreadJoinable
Thread created in joinable mode.
Definition cmsis_os2.h:264
osMessageQueueId_t osMessageQueueNew(uint32_t msg_count, uint32_t msg_size, const osMessageQueueAttr_t *attr)
void * osMutexId_t
Definition cmsis_os2.h:320
osSemaphoreId_t osSemaphoreNew(uint32_t max_count, uint32_t initial_count, const osSemaphoreAttr_t *attr)
#define osFlagsErrorTimeout
osErrorTimeout (-2).
Definition cmsis_os2.h:256
osStatus_t osThreadTerminate(osThreadId_t thread_id)
uint32_t osEventFlagsSet(osEventFlagsId_t ef_id, uint32_t flags)
#define osFlagsErrorUnknown
osError (-1).
Definition cmsis_os2.h:255
osStatus_t osDelayUntil(uint32_t ticks)
osStatus_t osThreadResume(osThreadId_t thread_id)
osStatus_t osThreadJoin(osThreadId_t thread_id)
osKernelState_t osKernelGetState(void)
osThreadId_t osThreadNew(osThreadFunc_t func, void *argument, const osThreadAttr_t *attr)
uint32_t osThreadGetCount(void)
osMemoryPoolId_t osMemoryPoolNew(uint32_t block_count, uint32_t block_size, const osMemoryPoolAttr_t *attr)
uint64_t osKernelGetSysTimerCount64(void)
uint32_t osKernelGetSysTimerFreq(void)
uint32_t osEventFlagsWait(osEventFlagsId_t ef_id, uint32_t flags, uint32_t options, uint32_t timeout)
uint32_t osThreadEnumerate(osThreadId_t *thread_array, uint32_t array_items)
const char * osEventFlagsGetName(osEventFlagsId_t ef_id)
void * osSemaphoreId_t
Definition cmsis_os2.h:323
void * osThreadId_t
Definition cmsis_os2.h:311
uint32_t osThreadFlagsClear(uint32_t flags)
#define osMutexRecursive
Recursive mutex.
Definition cmsis_os2.h:278
uint32_t osTimerIsRunning(osTimerId_t timer_id)
uint32_t osMessageQueueGetCount(osMessageQueueId_t mq_id)
osStatus_t
Status code values returned by CMSIS-RTOS functions.
Definition cmsis_os2.h:297
osStatus_t osKernelGetInfo(osVersion_t *version, char *id_buf, uint32_t id_size)
uint32_t osThreadFlagsSet(osThreadId_t thread_id, uint32_t flags)
uint32_t osSemaphoreGetCount(osSemaphoreId_t semaphore_id)
void osThreadExit(void)
Terminate execution of current running thread.
void(* osTimerFunc_t)(void *argument)
Timer callback function.
Definition cmsis_os2.h:237
const char * osTimerGetName(osTimerId_t timer_id)
uint32_t osKernelGetTickFreq(void)
osStatus_t osEventFlagsDelete(osEventFlagsId_t ef_id)
osTimerType_t
Timer type.
Definition cmsis_os2.h:240
osStatus_t osSemaphoreAcquire(osSemaphoreId_t semaphore_id, uint32_t timeout)
osThreadId_t osMutexGetOwner(osMutexId_t mutex_id)
osStatus_t osSemaphoreDelete(osSemaphoreId_t semaphore_id)
uint32_t osKernelGetTickCount(void)
uint32_t osThreadFlagsGet(void)
osStatus_t osThreadSetPriority(osThreadId_t thread_id, osPriority_t priority)
#define osFlagsWaitAll
Wait for all flags.
Definition cmsis_os2.h:250
uint32_t osEventFlagsGet(osEventFlagsId_t ef_id)
osStatus_t osMemoryPoolDelete(osMemoryPoolId_t mp_id)
void osKernelResume(uint32_t sleep_ticks)
osThreadId_t osThreadGetId(void)
void * osMemoryPoolAlloc(osMemoryPoolId_t mp_id, uint32_t timeout)
void(* osThreadFunc_t)(void *argument)
Entry point of a thread.
Definition cmsis_os2.h:234
uint32_t osEventFlagsClear(osEventFlagsId_t ef_id, uint32_t flags)
int32_t osKernelLock(void)
const char * osSemaphoreGetName(osSemaphoreId_t semaphore_id)
uint32_t osMemoryPoolGetCount(osMemoryPoolId_t mp_id)
uint32_t osMessageQueueGetMsgSize(osMessageQueueId_t mq_id)
osStatus_t osKernelStart(void)
uint32_t osThreadGetStackSpace(osThreadId_t thread_id)
osStatus_t osMessageQueuePut(osMessageQueueId_t mq_id, const void *msg_ptr, uint8_t msg_prio, uint32_t timeout)
#define osFlagsErrorParameter
osErrorParameter (-4).
Definition cmsis_os2.h:258
osStatus_t osThreadSuspend(osThreadId_t thread_id)
osStatus_t osThreadDetach(osThreadId_t thread_id)
#define osFlagsNoClear
Do not clear flags which have been specified to wait for.
Definition cmsis_os2.h:251
osEventFlagsId_t osEventFlagsNew(const osEventFlagsAttr_t *attr)
uint32_t osMemoryPoolGetBlockSize(osMemoryPoolId_t mp_id)
const char * osMemoryPoolGetName(osMemoryPoolId_t mp_id)
osStatus_t osTimerStart(osTimerId_t timer_id, uint32_t ticks)
osMutexId_t osMutexNew(const osMutexAttr_t *attr)
uint32_t osThreadGetStackSize(osThreadId_t thread_id)
osStatus_t osMessageQueueDelete(osMessageQueueId_t mq_id)
osStatus_t osMemoryPoolFree(osMemoryPoolId_t mp_id, void *block)
osStatus_t osMutexAcquire(osMutexId_t mutex_id, uint32_t timeout)
osStatus_t osTimerStop(osTimerId_t timer_id)
void * osMemoryPoolId_t
Definition cmsis_os2.h:326
osStatus_t osMutexDelete(osMutexId_t mutex_id)
uint32_t osThreadFlagsWait(uint32_t flags, uint32_t options, uint32_t timeout)
void * osTimerId_t
Definition cmsis_os2.h:314
uint32_t osMessageQueueGetCapacity(osMessageQueueId_t mq_id)
const char * osThreadGetName(osThreadId_t thread_id)
osStatus_t osMessageQueueReset(osMessageQueueId_t mq_id)
osThreadState_t osThreadGetState(osThreadId_t thread_id)
osStatus_t osTimerDelete(osTimerId_t timer_id)
osStatus_t osThreadYield(void)
osThreadState_t
Thread state.
Definition cmsis_os2.h:166
osPriority_t
Priority values.
Definition cmsis_os2.h:177
osTimerId_t osTimerNew(osTimerFunc_t func, osTimerType_t type, void *argument, const osTimerAttr_t *attr)
uint32_t osMemoryPoolGetCapacity(osMemoryPoolId_t mp_id)
osStatus_t osMessageQueueGet(osMessageQueueId_t mq_id, void *msg_ptr, uint8_t *msg_prio, uint32_t timeout)
uint32_t osMessageQueueGetSpace(osMessageQueueId_t mq_id)
uint32_t osKernelGetSysTimerCount(void)
uint32_t osKernelSuspend(void)
const char * osMessageQueueGetName(osMessageQueueId_t mq_id)
int32_t osKernelRestoreLock(int32_t lock)
osStatus_t osKernelInitialize(void)
#define osFlagsErrorISR
osErrorISR (-6).
Definition cmsis_os2.h:259
osStatus_t osMutexRelease(osMutexId_t mutex_id)
void * osEventFlagsId_t
Definition cmsis_os2.h:317
#define osMutexRobust
Robust mutex.
Definition cmsis_os2.h:280
int32_t osKernelUnlock(void)
osStatus_t osDelay(uint32_t ticks)
@ osKernelLocked
Locked.
Definition cmsis_os2.h:159
@ osKernelError
Error.
Definition cmsis_os2.h:161
@ osKernelSuspended
Suspended.
Definition cmsis_os2.h:160
@ osKernelReady
Ready.
Definition cmsis_os2.h:157
@ osKernelRunning
Running.
Definition cmsis_os2.h:158
@ osKernelInactive
Inactive.
Definition cmsis_os2.h:156
@ osErrorISR
Not allowed in ISR context: the function cannot be called from interrupt service routines.
Definition cmsis_os2.h:304
@ osErrorResource
Resource not available.
Definition cmsis_os2.h:301
@ osErrorTimeout
Operation not completed within the timeout period.
Definition cmsis_os2.h:300
@ osOK
Operation completed successfully.
Definition cmsis_os2.h:298
@ osError
Unspecified RTOS error: run-time error but no other error message fits.
Definition cmsis_os2.h:299
@ osErrorParameter
Parameter error.
Definition cmsis_os2.h:302
@ osTimerPeriodic
Repeating timer.
Definition cmsis_os2.h:242
@ osThreadBlocked
Blocked.
Definition cmsis_os2.h:170
@ osThreadRunning
Running.
Definition cmsis_os2.h:169
@ osThreadError
Error.
Definition cmsis_os2.h:172
@ osThreadReady
Ready.
Definition cmsis_os2.h:168
@ osPriorityISR
Reserved for ISR deferred thread.
Definition cmsis_os2.h:228
@ osPriorityNormal
Priority: normal.
Definition cmsis_os2.h:196
@ osPriorityIdle
Reserved for Idle thread.
Definition cmsis_os2.h:179
@ osPriorityNone
No priority (not initialized).
Definition cmsis_os2.h:178
@ osPriorityError
System cannot determine priority or illegal priority.
Definition cmsis_os2.h:229
Namespace of STK package.
uintptr_t Word
Native processor word type.
Definition stk_common.h:146
static void Yield()
Notify scheduler to switch to the next runnable task.
Definition stk_helper.h:546
SwitchStrategyFixedPriority< 32 > SwitchStrategyFP32
Shorthand alias for SwitchStrategyFixedPriority<32>: 32 priority levels (0..31), using a single 32-bi...
static void Sleep(Timeout tick_count)
Put calling process into a sleep state.
Definition stk_helper.h:483
EAccessMode
Hardware access modes by the task.
Definition stk_common.h:36
@ ACCESS_PRIVILEGED
Privileged access mode (access to hardware is fully unrestricted).
Definition stk_common.h:38
static constexpr Timeout NO_WAIT
Timeout value: return immediately if the synchronization object is not yet signaled (non-blocking pol...
Definition stk_common.h:220
EWaitResult
Wait result (see IKernelService::Wait).
Definition stk_common.h:123
@ WAIT_RESULT_FAIL
IKernelService::Wait returned with error without waiting.
Definition stk_common.h:124
@ WAIT_RESULT_CANCELED
The wait was cancelled by another task via IKernel::CancelTaskWait(), independent of signal/timeout.
Definition stk_common.h:127
@ WAIT_RESULT_TIMEOUT
The wake was caused by a timeout expiry.
Definition stk_common.h:126
@ WAIT_RESULT_SIGNAL
The wake was caused by a signal.
Definition stk_common.h:125
int64_t Ticks
Ticks value.
Definition stk_common.h:161
int32_t Timeout
Timeout time (ticks).
Definition stk_common.h:156
static bool SleepUntil(Ticks timestamp)
Put calling process into a sleep state until the specified timestamp.
Definition stk_helper.h:508
static uint32_t GetTickResolution()
Get number of microseconds in one tick.
Definition stk_helper.h:372
static uint32_t GetSysTimerFrequency()
Get system timer frequency.
Definition stk_helper.h:472
static constexpr T Max(T a, T b) noexcept
Compile-time maximum of two values.
Definition stk_defs.h:759
static Ticks GetTicks()
Get number of ticks elapsed since kernel start.
Definition stk_helper.h:438
PlatformArmCortexM PlatformDefault
Default platform implementation.
static constexpr Timeout WAIT_INFINITE
Timeout value: block indefinitely until the synchronization object is signaled.
Definition stk_common.h:214
static TId GetTid()
Get task/thread Id of the calling task.
Definition stk_helper.h:362
static constexpr T Min(T a, T b) noexcept
Compile-time minimum of two values.
Definition stk_defs.h:753
static Cycles GetSysTimerCount()
Get system timer count value.
Definition stk_helper.h:463
Word TId
Task (thread) id.
Definition stk_common.h:151
@ 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_DYNAMIC
Tasks can be added or removed and therefore exit when done.
Definition stk_common.h:48
bool IsInsideISR()
Check whether the CPU is currently executing inside a hardware interrupt service routine (ISR).
static void Free(void *ptr) __stk_weak
Free the memory chunk.
static void * Allocate(size_t size) __stk_weak
Allocate the memory chunk.
Fixed-size block allocator with O(1) alloc/free and proper task-blocking semantics.
bool IsStorageValid() const
Verify that the backing storage is valid and the pool is ready for use.
static constexpr size_t AlignBlockSize(size_t raw_size)
Round a raw block size up to the nearest multiple of BLOCK_ALIGN.
size_t GetBlockSize() const
Get the aligned block size used internally by the allocator.
Concrete implementation of IKernel.
Definition stk.h:97
static Session Enter(const Session ses=DEFAULT_SESSION)
Enter a critical section.
static void Exit(const Session ses=DEFAULT_SESSION)
Exit a critical section.
Lightweight, non-owning view over a contiguous sequence of elements.
Definition stk_common.h:263
virtual size_t GetStackSpace() const
Get available stack space.
Definition stk_common.h:439
Interface for a user task.
Definition stk_common.h:780
@ KSTATE_RUNNING
Initialized and running, IKernel::Start() was called successfully.
@ KSTATE_SUSPENDED
Scheduling is suspended with IKernelService::Suspend().
@ KSTATE_INACTIVE
Not ready, IKernel::Initialize() must be called.
@ KSTATE_READY
Ready to start, IKernel::Start() must be called.
static IKernelService * GetInstance()
Get CPU-local instance of the kernel service.
virtual void Resume(Timeout elapsed_ticks)=0
Resume scheduling after a prior Suspend() call.
virtual Timeout Suspend()=0
Suspend scheduling.
RAII-style low-level synchronization primitive for atomic code execution. Used as building brick for ...
Definition stk_sync_cs.h:54
Condition Variable primitive for signaling between tasks based on specific predicates.
Definition stk_sync_cv.h:68
EWaitResult WaitEx(IMutex &mutex, Timeout timeout_ticks=WAIT_INFINITE)
Wait for a signal, preserving the full wait outcome.
32-bit event flags group for multi-flag synchronization between tasks.
uint32_t Wait(uint32_t flags, uint32_t options=OPT_WAIT_ANY, Timeout timeout_ticks=WAIT_INFINITE)
Wait for one or more flags to be set.
static const uint32_t ERROR_CANCELED
Return sentinel: the wait was cancelled via IKernel::CancelTaskWait() before the flag condition was m...
static const uint32_t ERROR_ISR
Return sentinel: called from an ISR with a blocking timeout.
static const uint32_t ERROR_FAIL
Return sentinel: the kernel rejected the wait (IKernelService::Wait() returned WAIT_RESULT_FAIL,...
static const uint32_t ERROR_TIMEOUT
Return sentinel: wait timed out before the flags condition was met.
static bool IsError(uint32_t result)
Checks if a return value from Set(), Clear(), or Wait() is an error.
static const uint32_t OPT_NO_CLEAR
Do not clear matched flags after a successful wait.
static const uint32_t ERROR_PARAMETER
Return sentinel: invalid flags argument (bit 31 set).
static const uint32_t OPT_WAIT_ALL
Wait for ALL of the specified flags to be set simultaneously (AND semantics).
uint32_t Clear(uint32_t flags)
Clear one or more flags.
static const uint32_t OPT_WAIT_ANY
Wait for ANY of the specified flags to be set (OR semantics, default).
uint32_t Set(uint32_t flags)
Set one or more flags.
Non-recursive (binary) mutex primitive with ownership tracking.
Fixed-capacity, fixed-message-size FIFO queue for inter-task communication.
size_t GetCapacity() const
Get the maximum number of messages the queue can hold.
static const size_t CAPACITY_MAX
Max capacity supported (number of messages).
size_t GetMsgSize() const
Get the size of each message in bytes.
size_t GetSpace() const
Get the number of free slots currently available.
size_t GetCount() const
Get the current number of messages in the queue.
bool IsStorageValid() const
Verify that the backing storage is valid and the pool is ready for use.
Recursive mutex primitive that allows the same thread to acquire the lock multiple times.
Counting semaphore primitive for resource management and signaling.
uint16_t GetCount() const
Get current counter value.
static const uint16_t COUNT_MAX
Max count value supported.
EWaitResult WaitEx(Timeout timeout_ticks=WAIT_INFINITE)
Wait for a signal (decrement counter), preserving the full wait outcome.
Software timer multiplexer that manages multiple Timer instances on top of a small fixed set of kerne...
Abstract base class for a timer managed by TimerHost.
bool IsActive() const
Check whether the timer is currently active.
Version information.
Definition cmsis_os2.h:149
uint32_t api
API version (major.minor.rev: mmnnnrrrr dec).
Definition cmsis_os2.h:150
uint32_t kernel
Kernel version (major.minor.rev: mmnnnrrrr dec).
Definition cmsis_os2.h:151
Attributes structure for thread.
Definition cmsis_os2.h:340
void * cb_mem
memory for control block
Definition cmsis_os2.h:343
uint32_t attr_bits
attribute bits
Definition cmsis_os2.h:342
uint32_t cb_size
size of provided memory for control block
Definition cmsis_os2.h:344
void * stack_mem
memory for stack
Definition cmsis_os2.h:345
osPriority_t priority
initial thread priority (default: osPriorityNormal)
Definition cmsis_os2.h:347
uint32_t stack_size
size of stack
Definition cmsis_os2.h:346
const char * name
name of the thread
Definition cmsis_os2.h:341
Attributes structure for timer.
Definition cmsis_os2.h:353
void * cb_mem
memory for control block
Definition cmsis_os2.h:356
const char * name
name of the timer
Definition cmsis_os2.h:354
uint32_t cb_size
size of provided memory for control block
Definition cmsis_os2.h:357
Attributes structure for event flags.
Definition cmsis_os2.h:361
uint32_t cb_size
size of provided memory for control block
Definition cmsis_os2.h:365
const char * name
name of the event flags
Definition cmsis_os2.h:362
void * cb_mem
memory for control block
Definition cmsis_os2.h:364
Attributes structure for mutex.
Definition cmsis_os2.h:369
uint32_t cb_size
size of provided memory for control block
Definition cmsis_os2.h:373
uint32_t attr_bits
attribute bits
Definition cmsis_os2.h:371
const char * name
name of the mutex
Definition cmsis_os2.h:370
void * cb_mem
memory for control block
Definition cmsis_os2.h:372
Attributes structure for semaphore.
Definition cmsis_os2.h:377
const char * name
name of the semaphore
Definition cmsis_os2.h:378
uint32_t cb_size
size of provided memory for control block
Definition cmsis_os2.h:381
void * cb_mem
memory for control block
Definition cmsis_os2.h:380
Attributes structure for memory pool.
Definition cmsis_os2.h:385
uint32_t mp_size
size of provided memory for data storage
Definition cmsis_os2.h:391
const char * name
name of the memory pool
Definition cmsis_os2.h:386
void * cb_mem
memory for control block
Definition cmsis_os2.h:388
uint32_t cb_size
size of provided memory for control block
Definition cmsis_os2.h:389
void * mp_mem
memory for data storage
Definition cmsis_os2.h:390
Attributes structure for message queue.
Definition cmsis_os2.h:395
const char * name
name of the message queue
Definition cmsis_os2.h:396
void * cb_mem
memory for control block
Definition cmsis_os2.h:398
uint32_t mq_size
size of provided memory for data storage
Definition cmsis_os2.h:401
uint32_t cb_size
size of provided memory for control block
Definition cmsis_os2.h:399
void * mq_mem
memory for data storage
Definition cmsis_os2.h:400
const stk::Word * GetStack() const override
Get pointer to the stack memory.
int32_t GetWeight() const override
Get static base weight of the task.
void OnExit() override
Called by the kernel before removal from the scheduling (see stk::KERNEL_DYNAMIC).
stk::Word * m_stack
size_t m_stack_size
STK_NONCOPYABLE_CLASS(StkThread)
const char * GetTraceName() const override
Get task trace name set by application.
volatile JoinState m_join_state
static osThreadId_t ConvertTIdToThreadId(const stk::TId tid)
size_t GetStackSize() const override
Get number of elements of the stack memory array.
stk::sync::EventFlags m_thread_flags
void Run() override
Entry point of the user task.
stk::sync::ConditionVariable m_join_cv
volatile int32_t m_stk_priority
osThreadFunc_t m_func
const char * m_name
stk::EAccessMode GetAccessMode() const override
Get pointer to the stack memory.
virtual ~StkThread()
void * m_argument
void OnDeadlineMissed(uint32_t) override
Called by the scheduler if deadline of the task is missed when Kernel is operating in Hard Real-Time ...
virtual stk::TId GetOwner() const =0
virtual osStatus_t Acquire(stk::Timeout timeout)=0
virtual const char * GetTraceName() const =0
virtual ~StkMutexBase()
virtual void Unlock()=0
StkMutex(const char *n=nullptr)
stk::TId GetOwner() const override
osStatus_t Acquire(stk::Timeout timeout) override
void Unlock() override
const char * GetTraceName() const override
stk::sync::Mutex m_mutex
osStatus_t Acquire(stk::Timeout timeout) override
const char * GetTraceName() const override
stk::TId GetOwner() const override
stk::sync::FastMutex m_mutex
StkFastMutex(const char *n=nullptr)
void Unlock() override
stk::sync::Semaphore m_semaphore
StkSemaphore(uint16_t initial, uint16_t max_count, const char *n=nullptr)
stk::sync::EventFlags m_ef
StkEventFlags(const char *n=nullptr)
STK_NONCOPYABLE_CLASS(StkTimer)
static void EnsureTimerHostCreated()
void OnExpired(stk::time::TimerHost *) override
Callback invoked by the handler task when this timer expires.
virtual ~StkTimer()=default
StkTimer(osTimerFunc_t const func, osTimerType_t tt, void *arg, const char *name)
osTimerType_t m_type
void * m_argument
const char * m_name
osTimerFunc_t m_func
uint32_t m_period_ticks
StkMemPool(uint32_t cap, uint32_t raw_block_size, const char *name)
STK_NONCOPYABLE_CLASS(StkMemPool)
stk::memory::BlockMemoryPool m_mpool
StkMemPool(uint32_t cap, uint32_t raw_block_size, const char *name, uint8_t *ext_storage)
StkMessageQueue(uint32_t cap, uint32_t msz, const char *name, uint8_t *ext_buf)
StkMessageQueue(uint32_t cap, uint32_t msz, const char *name)
static uint8_t * AllocBuffer(uint32_t cap, uint32_t msz)
STK_NONCOPYABLE_CLASS(StkMessageQueue)
stk::sync::MessageQueue m_mq