Skip to content

Commit db8f5d3

Browse files
committed
Refactor implementation to comply with MISRA rules.
Rename IKernel::EState to IKernel::EKernelState and its members from STATE_ to KSTATE to resolve ambiguity with task state. Make EnumerateTasks, EnumerateKernelTasks safe by using ArrayView wrapper for arrays.
1 parent 389ceb2 commit db8f5d3

43 files changed

Lines changed: 2546 additions & 1605 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,24 @@
2121

2222
STK combines the control and transparency of bare-metal development with the structure and maintainability of modern, type-safe C++.
2323

24-
### For Embedded Developers
25-
26-
- **Deterministic execution** — no dynamic memory allocation (`malloc/free`) and no heap fragmentation. Memory usage is fully predictable at compile time.
27-
- **Lightweight C++ architecture** — clean object-oriented design without STL dependencies, exceptions, RTTI, or heavy runtime abstractions.
28-
- **Native C interoperability** — fully featured C API available for pure C projects and mixed C/C++ codebases.
29-
- **Transparent implementation** — minimal wrapper macros and readable scheduler internals simplify debugging and tracing.
30-
- **Portable design** — minimal BSP surface and straightforward architecture porting.
31-
32-
### For Technical Leads and Product Teams
33-
34-
- **Reduced hardware requirements** — the compact kernel footprint can enable the use of lower-RAM or lower-cost MCU variants.
35-
- **Higher CPU availability for applications** — benchmarks show up to **~12% more application CPU time** compared to FreeRTOS under comparable workloads (see [Benchmark](#benchmark)).
36-
- **Lower power potential** — reduced scheduling overhead can help meet timing requirements at lower MCU clock frequencies.
37-
- **Simplified migration** — compatibility layers for FreeRTOS and CMSIS-RTOS2 allow existing projects to migrate with minimal application changes.
38-
- **Predictable system behavior** — static allocation and deterministic scheduling simplify validation, debugging, and long-term maintenance.
39-
40-
> STK does not attempt to abstract or manage MCU peripherals. Its purpose is to provide a fast, predictable, and memory-efficient scheduling core for embedded applications.
24+
You get:
25+
26+
- **C++ native RTOS** — Built so the C++ compiler can efficiently optimize your STK-based application for maximum speed and ultra-low overhead.
27+
- **Safe code from day one** — Thoughtful OOP design enforces strict encapsulation and type safety to deliver secure, robust, and high-performance firmware.
28+
- **Full-featured RTOS** — A comprehensive suite of thread synchronization, memory, and time management primitives. You only need to bring your own HAL.
29+
- **Safety-critical ready** — Built for strict compliance with MISRA standards. Looking for a safe C++ RTOS for your certified device? Explore our [Services](#services).
30+
- **Deterministic execution** — Zero dynamic memory allocation (`malloc`/`free`) and zero heap fragmentation. Memory usage is fully predictable at compile time.
31+
- **Clean C++ design** — No STL dependencies, exceptions, RTTI, or heavy runtime abstractions. Readable internals simplify debugging and tracing.
32+
- **Verbose-free code** — A clean C++ API makes your implementation highly concise, making it significantly easier to maintain, refactor, and debug than standard C-only APIs.
33+
- **Portable design** — Minimal BSP (Board Support Package) footprint with complete independence from specific board and MCU peripherals.
34+
- **Reduced hardware requirements** — Compact kernel footprint that allows you to deploy on lower-RAM, lower-cost MCU variants.
35+
- **Higher CPU availability** — More time for your application logic. Benchmarks show up to **~12% more application CPU time** compared to FreeRTOS under comparable workloads (see [Benchmark](#benchmark)).
36+
- **Lower power consumption** — Features ultra-low power, tickless scheduling paired with reduced overhead, enabling the use of lower-frequency MCUs to save battery of your portable design.
37+
- **Native C support** — Includes a fully featured C API wrapper, allowing you to seamlessly use STK in pure C projects.
38+
- **Simplified migration** — Drop-in compatibility layers for FreeRTOS and CMSIS-RTOS2 to help you migrate legacy codebases with minimal application changes.
39+
- **B2B professional support** — Engineered for seamless integration into commercial projects. Explore our [Services](#services) for enterprise-grade support and custom engineering.
40+
41+
> STK does not attempt to abstract or manage MCU peripherals, similarly to FreeRTOS or CMSIS-RTOS2.
4142
4243
STK is an open-source project developed at https://github.com/SuperTinyKernel-RTOS.
4344

@@ -326,6 +327,13 @@ A complete ultra-low power demo targeting the [STM32F407G-DISC1](https://www.st.
326327

327328
---
328329

330+
## Language
331+
332+
* Minimal: C++11
333+
* Recommended: C++17 and higher
334+
335+
---
336+
329337
## Dedicated C interface
330338

331339
For seamless integration with C projects, STK provides a dedicated, fully-featured C API. See [interop/c](https://github.com/SuperTinyKernel-RTOS/stk/tree/main/interop/c) for the full reference and examples.
@@ -963,7 +971,7 @@ You may freely use it in projects of any type:
963971

964972
---
965973

966-
## 🔒 Professional Services & Commercial Licensing
974+
## Services
967975

968976
While **SuperTinyKernel™ RTOS** is provided under the permissive MIT license, we offer dedicated professional services for organizations integrating STK into production-grade, mission-critical, or regulated environments.
969977

build/benchmark/eclipse/perf-stm32f407g-disc1-stk/src/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ extern "C" void SysTick_Handler()
3232
if (g_Enable)
3333
++g_Ticks;
3434

35-
if (g_Kernel.GetState() == stk::IKernel::STATE_RUNNING)
35+
if (g_Kernel.GetState() == stk::IKernel::KSTATE_RUNNING)
3636
g_Kernel.GetPlatform()->ProcessTick();
3737
}
3838

build/example/driver/led.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,18 @@ struct Led
3939
static void Init(Id led, bool init_state);
4040
static inline void InitAll(bool init_state)
4141
{
42-
for (uint8_t led = 0; led < LED_MAX; ++led)
42+
for (uint8_t led = 0U; led < LED_MAX; ++led)
43+
{
4344
Led::Init(static_cast<LedId>(led), init_state);
45+
}
4446
}
4547
static void Set(Id led, bool state);
4648
static void SwitchOnExclusive(Id led)
4749
{
48-
for (uint8_t i = 0; i < LED_MAX; ++i)
50+
for (uint8_t i = 0U; i < LED_MAX; ++i)
51+
{
4952
Led::Set(static_cast<LedId>(i), (i == led));
53+
}
5054
}
5155
};
5256

interop/c/include/stk_c.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -321,18 +321,18 @@ void stk_kernel_start(stk_kernel_t *k);
321321
\note It is a direct match for IKernel::EState enum.
322322
\see stk_kernel_get_state()
323323
*/
324-
typedef enum _EKernelState {
324+
typedef enum stk_kernel_state_t {
325325
STK_KERNEL_STATE_INACTIVE = 0, //!< not ready, stk_kernel_init() must be called
326326
STK_KERNEL_STATE_READY = 1, //!< ready to start, stk_kernel_start() must be called
327327
STK_KERNEL_STATE_RUNNING = 2, //!< initialized and running, stk_kernel_start() was called successfully
328328
STK_KERNEL_STATE_SUSPENDED = 3 //!< scheduling suspended via stk_kernel_service_suspend() (tickless idle)
329-
} EKernelState;
329+
} stk_kernel_state_t;
330330

331331
/*! \brief Get state of the scheduler.
332332
\param[in] k: Kernel handle.
333-
\return State value, see \a EKernelState.
333+
\return State value, see \a stk_kernel_state_t.
334334
*/
335-
EKernelState stk_kernel_get_state(const stk_kernel_t *k);
335+
stk_kernel_state_t stk_kernel_get_state(const stk_kernel_t *k);
336336

337337
/*! \brief Test whether currently configured task set is schedulable.
338338
\param[in] k: Kernel handle.
@@ -567,7 +567,7 @@ stk_tick_t stk_ticks(void);
567567
/*! \brief Returns how many microseconds correspond to one kernel tick.
568568
\return Tick resolution in microseconds.
569569
*/
570-
int32_t stk_tick_resolution(void);
570+
uint32_t stk_tick_resolution(void);
571571

572572
/*! \brief Get ticks from milliseconds using current kernel tick resolution.
573573
\param[in] msec: Milliseconds to convert.
@@ -763,12 +763,12 @@ void stk_tls_set(void *ptr);
763763
/*! \brief Typed helper for getting TLS value.
764764
\note Expands to ((type *)stk_tls_get())
765765
*/
766-
#define STK_TLS_GET(type) ((type *)stk_tls_get())
766+
#define STK_TLS_GET_T(type) ((type *)stk_tls_get())
767767

768768
/*! \brief Typed helper for setting TLS value.
769769
\note Expands to stk_tls_set((void *)(ptr))
770770
*/
771-
#define STK_TLS_SET(ptr) stk_tls_set((void *)(ptr))
771+
#define STK_TLS_SET_T(ptr) stk_tls_set((void *)(ptr))
772772

773773
// =============================================================================
774774
// Synchronization Primitives

interop/c/include/stk_c_memory.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ extern "C" {
7171
\note Increase if your application needs more simultaneous pools.
7272
*/
7373
#ifndef STK_C_BLOCKPOOL_MAX
74-
#define STK_C_BLOCKPOOL_MAX 8
74+
#define STK_C_BLOCKPOOL_MAX 8U
7575
#endif
7676

7777
// =============================================================================

interop/c/src/stk_c.cpp

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -139,17 +139,25 @@ class EventOverrider final : public IPlatform::IEventOverrider
139139
// IPlatform::IEventOverrider
140140
bool OnSleep(Timeout sleep_ticks) override
141141
{
142+
bool is_handled = false;
143+
142144
if ((m_cb != nullptr) && (m_cb->on_sleep != nullptr))
143-
return m_cb->on_sleep(static_cast<stk_timeout_t>(sleep_ticks), m_cb->user_data);
145+
{
146+
is_handled = m_cb->on_sleep(static_cast<stk_timeout_t>(sleep_ticks), m_cb->user_data);
147+
}
144148

145-
return false;
149+
return is_handled;
146150
}
147151
bool OnHardFault() override
148152
{
153+
bool is_handled = false;
154+
149155
if ((m_cb != nullptr) && (m_cb->on_hard_fault != nullptr))
150-
return m_cb->on_hard_fault(m_cb->user_data);
156+
{
157+
is_handled = m_cb->on_hard_fault(m_cb->user_data);
158+
}
151159

152-
return false;
160+
return is_handled;
153161
}
154162

155163
private:
@@ -320,7 +328,6 @@ void stk_kernel_destroy(stk_kernel_t *k)
320328
// GetPlatform() paths, and we must not leave a dangling overrider pointer
321329
// registered at that point.
322330
UnregisterKernel(reinterpret_cast<IKernel *>(k));
323-
reinterpret_cast<IKernel *>(k)->~IKernel();
324331
}
325332

326333
// -----------------------------------------------------------------------------
@@ -340,11 +347,11 @@ void stk_kernel_start(stk_kernel_t *k)
340347
reinterpret_cast<IKernel *>(k)->Start();
341348
}
342349

343-
EKernelState stk_kernel_get_state(const stk_kernel_t *k)
350+
stk_kernel_state_t stk_kernel_get_state(const stk_kernel_t *k)
344351
{
345352
STK_ASSERT(k != nullptr);
346353

347-
return static_cast<EKernelState>(reinterpret_cast<const stk::IKernel *>(k)->GetState());
354+
return static_cast<stk_kernel_state_t>(reinterpret_cast<const stk::IKernel *>(k)->GetState());
348355
}
349356

350357
bool stk_kernel_is_schedulable(const stk_kernel_t *k)
@@ -375,8 +382,8 @@ bool stk_kernel_is_started(const stk_kernel_t *k)
375382
{
376383
STK_ASSERT(k != nullptr);
377384

378-
const stk::IKernel::EState st = reinterpret_cast<const stk::IKernel *>(k)->GetState();
379-
return ((st == stk::IKernel::STATE_RUNNING) || (st == stk::IKernel::STATE_SUSPENDED));
385+
const stk::IKernel::EKernelState st = reinterpret_cast<const stk::IKernel *>(k)->GetState();
386+
return ((st == stk::IKernel::KSTATE_RUNNING) || (st == stk::IKernel::KSTATE_SUSPENDED));
380387
}
381388

382389
void stk_kernel_schedule_task_removal(stk_kernel_t *k, stk_task_t *task)
@@ -409,16 +416,23 @@ size_t stk_kernel_enumerate_tasks(stk_kernel_t *k, stk_task_t **tasks, size_t ma
409416
STK_ASSERT(k != nullptr);
410417
STK_ASSERT(tasks != nullptr);
411418

412-
// Collect ITask* pointers from the kernel, then map each back to stk_task_t*.
413-
// TaskWrapper is the first member of stk_task_t, so ITask* == stk_task_t*.
414419
stk::ITask *itasks[STK_C_TASKS_MAX] = {};
415-
size_t n = reinterpret_cast<IKernel *>(k)->EnumerateTasks(
416-
itasks, (max_count < STK_C_TASKS_MAX ? max_count : STK_C_TASKS_MAX));
420+
421+
// Determine the safe upper bound for the temporary buffer
422+
const size_t requested_size = (max_count < static_cast<size_t>(STK_C_TASKS_MAX)) ? max_count :
423+
static_cast<size_t>(STK_C_TASKS_MAX);
424+
425+
// Pass via ArrayView temporary object
426+
const size_t ret_count = reinterpret_cast<IKernel *>(k)->EnumerateTasks(
427+
ArrayView<stk::ITask*>(itasks, requested_size));
417428

418-
for (size_t i = 0; i < n; ++i)
419-
tasks[i] = reinterpret_cast<stk_task_t *>(itasks[i]);
429+
ArrayView<stk_task_t *> output_view(tasks, max_count);
430+
for (size_t i = 0U; i < ret_count; ++i)
431+
{
432+
output_view[i] = reinterpret_cast<stk_task_t *>(itasks[i]);
433+
}
420434

421-
return n;
435+
return ret_count;
422436
}
423437

424438
stk_timeout_t stk_kernel_suspend(stk_kernel_t *k)
@@ -549,7 +563,7 @@ void stk_task_destroy(stk_task_t *task)
549563
// -----------------------------------------------------------------------------
550564
stk_tid_t stk_tid(void) { return stk::GetTid(); }
551565
stk_tick_t stk_ticks(void) { return stk::GetTicks(); }
552-
int32_t stk_tick_resolution(void) { return stk::GetTickResolution(); }
566+
uint32_t stk_tick_resolution(void) { return stk::GetTickResolution(); }
553567
stk_time_t stk_time_now_ms(void) { return stk::GetTimeNowMs(); }
554568
stk_tick_t stk_ticks_from_ms(stk_time_t msec) { return stk_ticks_from_ms_r(msec, stk::GetTickResolution()); }
555569
stk_cycle_t stk_sys_timer_count(void) { return stk::GetSysTimerCount(); }

interop/c/src/stk_c_memory.cpp

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ static_assert(
3434
// Returns a size of memory in stk::Word elements required for object allocation.
3535
template <typename T> static constexpr size_t StkGetWordCountForType()
3636
{
37-
return ((sizeof(T) + sizeof(stk::Word) - 1) / sizeof(stk::Word));
37+
return ((sizeof(T) + sizeof(stk::Word) - 1U) / sizeof(stk::Word));
3838
}
3939

4040
// Private memory allocators (we define malloc, free here to overcome absence of declaration in
@@ -92,25 +92,30 @@ s_BlockPools[STK_C_BLOCKPOOL_MAX];
9292
// Find the slot that owns 'pool'; returns nullptr if not found.
9393
static BlockPoolSlot *FindSlot(const stk_blockpool_t *pool)
9494
{
95-
for (uint32_t i = 0; i < STK_C_BLOCKPOOL_MAX; ++i)
95+
for (size_t i = 0U; i < STK_C_BLOCKPOOL_MAX; ++i)
9696
{
97-
if (s_BlockPools[i].busy && (s_BlockPools[i].pool() == pool))
98-
return &s_BlockPools[i];
97+
if (s_BlockPools[i].busy)
98+
{
99+
if (s_BlockPools[i].pool() == pool)
100+
return &s_BlockPools[i];
101+
}
99102
}
103+
100104
return nullptr;
101105
}
102106

103107
// Acquire a free slot; returns nullptr when the pool is exhausted.
104108
static BlockPoolSlot *AcquireSlot()
105109
{
106-
for (uint32_t i = 0; i < STK_C_BLOCKPOOL_MAX; ++i)
110+
for (size_t i = 0U; i < STK_C_BLOCKPOOL_MAX; ++i)
107111
{
108112
if (!s_BlockPools[i].busy)
109113
{
110114
s_BlockPools[i].busy = true;
111115
return &s_BlockPools[i];
112116
}
113117
}
118+
114119
return nullptr;
115120
}
116121

@@ -128,7 +133,7 @@ stk_blockpool_t *stk_blockpool_create(size_t capacity, size_t raw_block_size, co
128133
STK_ASSERT(capacity > 0U);
129134
STK_ASSERT(raw_block_size > 0U);
130135

131-
sync::ScopedCriticalSection __cs;
136+
const sync::ScopedCriticalSection cs_;
132137

133138
BlockPoolSlot *slot = AcquireSlot();
134139
if (slot == nullptr)
@@ -152,7 +157,7 @@ stk_blockpool_t *stk_blockpool_create_static(size_t capacity,
152157
STK_ASSERT(storage != nullptr);
153158
STK_ASSERT(storage_size >= (capacity * BlockMemoryPool::AlignBlockSize(raw_block_size)));
154159

155-
sync::ScopedCriticalSection __cs;
160+
sync::ScopedCriticalSection cs_;
156161

157162
BlockPoolSlot *slot = AcquireSlot();
158163
if (slot == nullptr)
@@ -170,7 +175,7 @@ void stk_blockpool_destroy(stk_blockpool_t *pool)
170175
{
171176
STK_ASSERT(pool != nullptr);
172177

173-
sync::ScopedCriticalSection __cs;
178+
sync::ScopedCriticalSection cs_;
174179

175180
BlockPoolSlot *slot = FindSlot(pool);
176181

interop/cmsis/rtos2/src/cmsis_os2_stk.cpp

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ struct StkThread final : public stk::ITask
179179
}
180180

181181
// ---- IStackMemory ----
182-
stk::Word *GetStack() const override { return m_stack; }
182+
const stk::Word *GetStack() const override { return m_stack; }
183183
size_t GetStackSize() const override { return m_stack_size; }
184184
size_t GetStackSizeBytes() const override { return m_stack_size * sizeof(stk::Word); }
185185

@@ -510,11 +510,11 @@ osKernelState_t osKernelGetState(void)
510510

511511
switch (g_StkKernel.GetState())
512512
{
513-
case stk::IKernel::STATE_INACTIVE: return osKernelInactive;
514-
case stk::IKernel::STATE_READY: return osKernelReady;
515-
case stk::IKernel::STATE_RUNNING: return osKernelRunning;
516-
case stk::IKernel::STATE_SUSPENDED: return osKernelSuspended;
517-
default: return osKernelError;
513+
case stk::IKernel::KSTATE_INACTIVE: return osKernelInactive;
514+
case stk::IKernel::KSTATE_READY: return osKernelReady;
515+
case stk::IKernel::KSTATE_RUNNING: return osKernelRunning;
516+
case stk::IKernel::KSTATE_SUSPENDED: return osKernelSuspended;
517+
default: return osKernelError;
518518
}
519519
}
520520

@@ -959,13 +959,24 @@ uint32_t osThreadGetCount(void)
959959

960960
uint32_t osThreadEnumerate(osThreadId_t *thread_array, uint32_t array_items)
961961
{
962-
if (osKernelGetState() == osKernelInactive)
963-
return 0U;
962+
uint32_t result_count = 0U;
963+
const osKernelState_t kstate = osKernelGetState();
964964

965-
// osThreadId_t maps directly to stk::ITask (see StkThread)
966-
return g_StkKernel.EnumerateTasks(reinterpret_cast<stk::ITask **>(thread_array), array_items);
967-
}
965+
// kernel must be active and buffer must be valid
966+
if ((kstate != osKernelInactive) && (thread_array != nullptr) && (array_items != 0U))
967+
{
968+
// cast the raw pointer array to the expected ITask* destination type
969+
stk::ITask **tasks_destination = reinterpret_cast<stk::ITask **>(thread_array);
968970

971+
// bind raw destination buffer into a temporary ArrayView object
972+
const size_t count = g_StkKernel.EnumerateTasks(
973+
stk::ArrayView<stk::ITask *>(tasks_destination, static_cast<size_t>(array_items)));
974+
975+
result_count = static_cast<uint32_t>(count);
976+
}
977+
978+
return result_count;
979+
}
969980

970981
// ===========================================================================
971982
// ==== Thread Flags Functions ====

0 commit comments

Comments
 (0)