SuperTinyKernel™ RTOS 1.05.3
Lightweight, high-performance, deterministic, bare-metal C++ RTOS for resource-constrained embedded systems. MIT Open Source License.
Loading...
Searching...
No Matches
test_blockpool.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_config.h>
11#include <stk.h>
13#include <assert.h>
14#include <string.h>
15
16#include "stktest_context.h"
17
18using namespace stk;
19using namespace stk::test;
20
22
23#define _STK_POOL_TEST_TASKS_MAX 5
24#define _STK_POOL_TEST_TIMEOUT 1000
25#define _STK_POOL_TEST_SHORT_SLEEP 10
26#define _STK_POOL_TEST_LONG_SLEEP 100
27#define _STK_POOL_BLOCK_SIZE 32U
28#define _STK_POOL_CAPACITY 8U
29#ifdef __ARM_ARCH_6M__
30#define _STK_POOL_STACK_SIZE 128 // ARM Cortex-M0
31#define STK_TASK
32#else
33#define _STK_POOL_STACK_SIZE 256
34#define STK_TASK static
35#endif
36
37namespace stk {
38namespace test {
39
43namespace blockpool {
44
45// Test results storage
46static volatile int32_t g_TestResult = 0;
47static volatile int32_t g_InstancesDone = 0;
48static volatile int32_t g_SharedCounter = 0;
49
50// Kernel
53
54// Shared pool + external-storage buffer used by most tests
55alignas(sizeof(void *)) static uint8_t
56 g_PoolStorage[_STK_POOL_CAPACITY * stk::memory::BlockMemoryPool::AlignBlockSize(_STK_POOL_BLOCK_SIZE)];
57
59
60// ---------------------------------------------------------------------------
61// Test 1 – TryAlloc / Free basic cycle (single task)
62// ---------------------------------------------------------------------------
63
68template <EAccessMode _AccessMode>
69class TryAllocFreeTask : public Task<_STK_POOL_STACK_SIZE, _AccessMode>
70{
71 uint8_t m_task_id;
72
73public:
74 TryAllocFreeTask(uint8_t task_id, int32_t) : m_task_id(task_id)
75 {}
76
77private:
78 void Run() override
79 {
80 if (m_task_id == 0)
81 {
82 bool ok = true;
83
84 // Pool should start fully empty (no outstanding allocations)
85 ok &= g_Pool->IsEmpty();
86 ok &= (g_Pool->GetFreeCount() == _STK_POOL_CAPACITY);
87 ok &= (g_Pool->GetUsedCount() == 0U);
88
89 // Allocate one block non-blocking
90 void *blk = g_Pool->TryAlloc();
91 ok &= (blk != nullptr);
92 ok &= (g_Pool->GetUsedCount() == 1U);
93 ok &= (g_Pool->GetFreeCount() == (_STK_POOL_CAPACITY - 1U));
94
95 // Write to the block to verify it is usable memory
96 memset(blk, 0xAB, _STK_POOL_BLOCK_SIZE);
97
98 // Return the block
99 ok &= g_Pool->Free(blk);
100 ok &= g_Pool->IsEmpty();
101 ok &= (g_Pool->GetUsedCount() == 0U);
102
103 // TryAlloc on empty-after-full-drain should still succeed
104 void *blk2 = g_Pool->TryAlloc();
105 ok &= (blk2 != nullptr);
106 g_Pool->Free(blk2);
107
108 printf("TryAllocFree: %s\n", ok ? "PASS" : "FAIL");
109 if (ok)
110 g_TestResult = 1;
111 }
112
114 }
115};
116
117// ---------------------------------------------------------------------------
118// Test 2 – Exhaust pool with TryAlloc then verify IsFull / free all
119// ---------------------------------------------------------------------------
120
125template <EAccessMode _AccessMode>
126class ExhaustPoolTask : public Task<_STK_POOL_STACK_SIZE, _AccessMode>
127{
128 uint8_t m_task_id;
129
130public:
131 ExhaustPoolTask(uint8_t task_id, int32_t) : m_task_id(task_id)
132 {}
133
134private:
135 void Run() override
136 {
137 if (m_task_id == 0)
138 {
139 bool ok = true;
140 void *blocks[_STK_POOL_CAPACITY] = {};
141
142 // Drain every block
143 for (size_t i = 0; i < _STK_POOL_CAPACITY; ++i)
144 {
145 blocks[i] = g_Pool->TryAlloc();
146 ok &= (blocks[i] != nullptr);
147 }
148
149 ok &= g_Pool->IsFull();
150 ok &= (g_Pool->GetFreeCount() == 0U);
151 ok &= (g_Pool->GetUsedCount() == _STK_POOL_CAPACITY);
152
153 // One more TryAlloc must fail without blocking
154 void *extra = g_Pool->TryAlloc();
155 ok &= (extra == nullptr);
156
157 // Return all blocks
158 for (size_t i = 0; i < _STK_POOL_CAPACITY; ++i)
159 ok &= g_Pool->Free(blocks[i]);
160
161 ok &= g_Pool->IsEmpty();
162
163 printf("ExhaustPool: %s\n", ok ? "PASS" : "FAIL");
164 if (ok)
165 g_TestResult = 1;
166 }
167
169 }
170};
171
172// ---------------------------------------------------------------------------
173// Test 3 – Blocking Alloc: producer wakes a blocked consumer
174// ---------------------------------------------------------------------------
175
180template <EAccessMode _AccessMode>
181class BlockingAllocTask : public Task<_STK_POOL_STACK_SIZE, _AccessMode>
182{
183 uint8_t m_task_id;
184
185public:
186 BlockingAllocTask(uint8_t task_id, int32_t) : m_task_id(task_id)
187 {}
188
189private:
190 void Run() override
191 {
192 if (m_task_id == 0)
193 {
194 // Drain every block to force task 1 into blocking Alloc()
195 void *blocks[_STK_POOL_CAPACITY] = {};
196 for (size_t i = 0; i < _STK_POOL_CAPACITY; ++i)
197 blocks[i] = g_Pool->TryAlloc();
198
199 // Give task 1 time to enter Alloc() and block
201
202 // Free one block — this must wake task 1
203 g_Pool->Free(blocks[0]);
204 blocks[0] = nullptr;
205
206 // Wait for task 1 to finish then free the remaining blocks
208
209 for (size_t i = 1; i < _STK_POOL_CAPACITY; ++i)
210 g_Pool->Free(blocks[i]);
211 }
212 else
213 if (m_task_id == 1)
214 {
215 // Pool is full; Alloc() must block until task 0 frees a block
216 void *blk = g_Pool->Alloc(); // blocking
217
218 if (blk != nullptr)
219 {
220 g_SharedCounter = 1; // signal success
221 g_Pool->Free(blk);
222 }
223 }
224
226
227 if (m_task_id == 0)
228 {
229 while (g_InstancesDone < 2)
231
232 bool ok = (g_SharedCounter == 1) && g_Pool->IsEmpty();
233 printf("BlockingAlloc: %s\n", ok ? "PASS" : "FAIL");
234 if (ok)
235 g_TestResult = 1;
236 }
237 }
238};
239
240// ---------------------------------------------------------------------------
241// Test 4 – TimedAlloc timeout: expires when no block becomes available
242// ---------------------------------------------------------------------------
243
248template <EAccessMode _AccessMode>
249class TimedAllocTimeoutTask : public Task<_STK_POOL_STACK_SIZE, _AccessMode>
250{
251 uint8_t m_task_id;
252
253public:
254 TimedAllocTimeoutTask(uint8_t task_id, int32_t) : m_task_id(task_id)
255 {}
256
257private:
258 void Run() override
259 {
260 if (m_task_id == 0)
261 {
262 // Fill the pool
263 void *blocks[_STK_POOL_CAPACITY] = {};
264 for (size_t i = 0; i < _STK_POOL_CAPACITY; ++i)
265 blocks[i] = g_Pool->TryAlloc();
266
267 // Hold long enough for task 1 to time out, then release
268 stk::Sleep(200);
269
270 for (size_t i = 0; i < _STK_POOL_CAPACITY; ++i)
271 g_Pool->Free(blocks[i]);
272 }
273 else
274 if (m_task_id == 1)
275 {
276 stk::Sleep(_STK_POOL_TEST_SHORT_SLEEP); // let task 0 fill pool first
277
278 int64_t start = GetTimeNowMs();
279 void *blk = g_Pool->TimedAlloc(50); // 50 ms timeout
280 int64_t elapsed = GetTimeNowMs() - start;
281
282 bool ok = (blk == nullptr) && (elapsed >= 45) && (elapsed <= 65);
283 g_SharedCounter = ok ? 1 : 0;
284
285 printf("TimedAllocTimeout: blk=%s elapsed=%d %s\n",
286 blk ? "non-null" : "null", (int)elapsed, ok ? "PASS" : "FAIL");
287 }
288
290
291 if (m_task_id == 1)
292 {
293 if (g_SharedCounter == 1)
294 g_TestResult = 1;
295 }
296 }
297};
298
299// ---------------------------------------------------------------------------
300// Test 5 – TimedAlloc success: block released within timeout window
301// ---------------------------------------------------------------------------
302
307template <EAccessMode _AccessMode>
308class TimedAllocSuccessTask : public Task<_STK_POOL_STACK_SIZE, _AccessMode>
309{
310 uint8_t m_task_id;
311
312public:
313 TimedAllocSuccessTask(uint8_t task_id, int32_t) : m_task_id(task_id)
314 {}
315
316private:
317 void Run() override
318 {
319 if (m_task_id == 0)
320 {
321 void *blocks[_STK_POOL_CAPACITY] = {};
322 for (size_t i = 0; i < _STK_POOL_CAPACITY; ++i)
323 blocks[i] = g_Pool->TryAlloc();
324
325 stk::Sleep(40); // free before task 1's 150 ms timeout
326
327 g_Pool->Free(blocks[0]);
328 blocks[0] = nullptr;
329
331
332 for (size_t i = 1; i < _STK_POOL_CAPACITY; ++i)
333 g_Pool->Free(blocks[i]);
334 }
335 else
336 if (m_task_id == 1)
337 {
338 stk::Sleep(_STK_POOL_TEST_SHORT_SLEEP); // let task 0 fill pool first
339
340 void *blk = g_Pool->TimedAlloc(150); // ample timeout
341
342 bool ok = (blk != nullptr);
343 g_SharedCounter = ok ? 1 : 0;
344
345 if (blk) g_Pool->Free(blk);
346
347 printf("TimedAllocSuccess: %s\n", ok ? "PASS" : "FAIL");
348 }
349
351
352 if (m_task_id == 1)
353 {
354 if (g_SharedCounter == 1)
355 g_TestResult = 1;
356 }
357 }
358};
359
360// ---------------------------------------------------------------------------
361// Test 6 – Multi-task concurrent alloc/free: counter integrity
362// ---------------------------------------------------------------------------
363
368template <EAccessMode _AccessMode>
369class ConcurrentAllocFreeTask : public Task<_STK_POOL_STACK_SIZE, _AccessMode>
370{
371 uint8_t m_task_id;
373
374public:
375 ConcurrentAllocFreeTask(uint8_t task_id, int32_t iterations)
376 : m_task_id(task_id), m_iterations(iterations)
377 {}
378
379private:
380 void Run() override
381 {
382 for (int32_t i = 0; i < m_iterations; ++i)
383 {
384 // Blocking alloc – each task competes for pool blocks
385 int32_t *blk = g_Pool->AllocT<int32_t>();
386
387 if (blk)
388 {
389 *blk = 1;
390 g_SharedCounter += *blk; // intentionally unsynchronised to detect pool corruption
391 g_Pool->Free(blk);
392 }
393
394 stk::Yield();
395 }
396
398
399 if (m_task_id == 0)
400 {
403
404 int32_t expected = _STK_POOL_TEST_TASKS_MAX * m_iterations;
405
406 printf("ConcurrentAllocFree: counter=%d (expected %d) %s\n",
407 (int)g_SharedCounter, (int)expected,
408 (g_SharedCounter == expected) ? "PASS" : "FAIL");
409
410 if (g_SharedCounter == expected)
411 g_TestResult = 1;
412 }
413 }
414};
415
416// ---------------------------------------------------------------------------
417// Test 7 – Typed helpers: AllocT / TryAllocT / TimedAllocT
418// ---------------------------------------------------------------------------
419
421{
422 uint32_t id;
423 uint32_t value;
424};
425
431template <EAccessMode _AccessMode>
432class TypedAllocTask : public Task<_STK_POOL_STACK_SIZE, _AccessMode>
433{
434 uint8_t m_task_id;
435
436public:
437 TypedAllocTask(uint8_t task_id, int32_t) : m_task_id(task_id)
438 {}
439
440private:
441 void Run() override
442 {
443 if (m_task_id == 0)
444 {
445 bool ok = true;
446
447 // TryAllocT
448 TestRecord *rec = g_Pool->TryAllocT<TestRecord>();
449 ok &= (rec != nullptr);
450 if (rec)
451 {
452 rec->id = 42U;
453 rec->value = 0xDEADBEEFU;
454 ok &= (rec->id == 42U) && (rec->value == 0xDEADBEEFU);
455 g_Pool->Free(rec);
456 }
457
458 // TimedAllocT with infinite wait
459 TestRecord *rec2 = g_Pool->TimedAllocT<TestRecord>(WAIT_INFINITE);
460 ok &= (rec2 != nullptr);
461 if (rec2)
462 {
463 rec2->id = 7U;
464 rec2->value = 99U;
465 ok &= (rec2->id == 7U);
466 g_Pool->Free(rec2);
467 }
468
469 // AllocT
470 TestRecord *rec3 = g_Pool->AllocT<TestRecord>();
471 ok &= (rec3 != nullptr);
472 if (rec3) g_Pool->Free(rec3);
473
474 printf("TypedAlloc: %s\n", ok ? "PASS" : "FAIL");
475 if (ok)
476 g_TestResult = 1;
477 }
478
480 }
481};
482
483// ---------------------------------------------------------------------------
484// Test 8 – Free(nullptr) is a no-op and returns false
485// ---------------------------------------------------------------------------
486
490template <EAccessMode _AccessMode>
491class FreeNullTask : public Task<_STK_POOL_STACK_SIZE, _AccessMode>
492{
493 uint8_t m_task_id;
494
495public:
496 FreeNullTask(uint8_t task_id, int32_t) : m_task_id(task_id)
497 {}
498
499private:
500 void Run() override
501 {
502 if (m_task_id == 0)
503 {
504 bool ok = true;
505
506 size_t free_before = g_Pool->GetFreeCount();
507
508 // Free(nullptr) must be a safe no-op
509 bool result = g_Pool->Free(nullptr);
510 ok &= !result; // must return false
511
512 // Pool state must be unchanged
513 ok &= (g_Pool->GetFreeCount() == free_before);
514
515 printf("FreeNull: %s\n", ok ? "PASS" : "FAIL");
516 if (ok)
517 g_TestResult = 1;
518 }
519
521 }
522};
523
524// ---------------------------------------------------------------------------
525// Test 9 – AlignBlockSize helper
526// ---------------------------------------------------------------------------
527
532template <EAccessMode _AccessMode>
533class AlignBlockSizeTask : public Task<_STK_POOL_STACK_SIZE, _AccessMode>
534{
535 uint8_t m_task_id;
536
537public:
538 AlignBlockSizeTask(uint8_t task_id, int32_t) : m_task_id(task_id)
539 {}
540
541private:
542 void Run() override
543 {
544 if (m_task_id == 0)
545 {
546 bool ok = true;
547
548 const size_t align = sizeof(void *); // BLOCK_ALIGN = sizeof(MemoryBlock) = sizeof(void*)
549
550 // AlignBlockSize(1) must be at least BLOCK_ALIGN
551 size_t a1 = stk::memory::BlockMemoryPool::AlignBlockSize(1U);
552 ok &= (a1 >= align);
553 ok &= (a1 % align == 0U);
554
555 // AlignBlockSize(align) must equal align exactly (already aligned)
556 size_t a2 = stk::memory::BlockMemoryPool::AlignBlockSize(align);
557 ok &= (a2 == align);
558
559 // AlignBlockSize(align + 1) must round up to 2 * align
560 size_t a3 = stk::memory::BlockMemoryPool::AlignBlockSize(align + 1U);
561 ok &= (a3 == 2U * align);
562
563 // AlignBlockSize(3 * align) must equal 3 * align (already aligned)
564 size_t a4 = stk::memory::BlockMemoryPool::AlignBlockSize(3U * align);
565 ok &= (a4 == 3U * align);
566
567 // Pool's GetBlockSize() must equal AlignBlockSize of the raw size at construction
568 size_t expected_block_size = stk::memory::BlockMemoryPool::AlignBlockSize(_STK_POOL_BLOCK_SIZE);
569 ok &= (g_Pool->GetBlockSize() == expected_block_size);
570
571 printf("AlignBlockSize: a1=%u a2=%u a3=%u a4=%u expected_bs=%u %s\n",
572 a1, a2, a3, a4, expected_block_size, ok ? "PASS" : "FAIL");
573
574 if (ok)
575 g_TestResult = 1;
576 }
577
579 }
580};
581
582// ---------------------------------------------------------------------------
583// Test 10 – External vs heap storage: IsStorageValid, GetCapacity, GetBlockSize
584// ---------------------------------------------------------------------------
585
590template <EAccessMode _AccessMode>
591class StorageModeTask : public Task<_STK_POOL_STACK_SIZE, _AccessMode>
592{
593 uint8_t m_task_id;
594
595public:
596 StorageModeTask(uint8_t task_id, int32_t) : m_task_id(task_id)
597 {}
598
599private:
600 void Run() override
601 {
602 if (m_task_id == 0)
603 {
604 bool ok = true;
605
606 // External-storage pool (g_Pool) checks
607 ok &= g_Pool->IsStorageValid();
608 ok &= (g_Pool->GetCapacity() == _STK_POOL_CAPACITY);
609
610 // Heap-storage pool
611 const size_t heap_cap = 4U;
612 const size_t heap_bsz = 16U;
613 stk::memory::BlockMemoryPool heap_pool(heap_cap, heap_bsz);
614
615 ok &= heap_pool.IsStorageValid();
616 ok &= (heap_pool.GetCapacity() == heap_cap);
617 ok &= (heap_pool.GetBlockSize() >= heap_bsz);
618 ok &= (heap_pool.GetBlockSize() % sizeof(void *) == 0U);
619 ok &= heap_pool.IsEmpty();
620
621 // Quick alloc/free cycle on heap pool
622 void *blk = heap_pool.TryAlloc();
623 ok &= (blk != nullptr);
624 ok &= (heap_pool.GetUsedCount() == 1U);
625 heap_pool.Free(blk);
626 ok &= heap_pool.IsEmpty();
627
628 printf("StorageMode: %s\n", ok ? "PASS" : "FAIL");
629 if (ok)
630 g_TestResult = 1;
631 }
632
634 }
635};
636
637// ---------------------------------------------------------------------------
638// Test 11 – Stress test: mixed TryAlloc / Alloc / TimedAlloc across all tasks
639// ---------------------------------------------------------------------------
640
645template <EAccessMode _AccessMode>
646class StressTask : public Task<_STK_POOL_STACK_SIZE, _AccessMode>
647{
648 uint8_t m_task_id;
650
651public:
652 StressTask(uint8_t task_id, int32_t iterations)
653 : m_task_id(task_id), m_iterations(iterations)
654 {}
655
656private:
657 void Run() override
658 {
659 for (int32_t i = 0; i < m_iterations; ++i)
660 {
661 void *blk = nullptr;
662
663 switch (i % 3)
664 {
665 case 0: blk = g_Pool->TryAlloc(); break;
666 case 1: blk = g_Pool->Alloc(); break;
667 case 2: blk = g_Pool->TimedAlloc(20); break;
668 default: break;
669 }
670
671 if (blk)
672 {
673 memset(blk, (uint8_t)(m_task_id + i), _STK_POOL_BLOCK_SIZE);
674 g_Pool->Free(blk);
676 }
677
678 if ((i % 8) == 0)
679 stk::Delay(1);
680 }
681
683
685 {
688
689 // At least some allocations must have succeeded
690 bool ok = (g_SharedCounter > 0) && g_Pool->IsEmpty();
691
692 printf("Stress: counter=%d pool_empty=%s %s\n",
693 (int)g_SharedCounter,
694 g_Pool->IsEmpty() ? "yes" : "no",
695 ok ? "PASS" : "FAIL");
696
697 if (ok) g_TestResult = 1;
698 }
699 }
700};
701
702// ---------------------------------------------------------------------------
703// Helper – reset shared state between tests
704// ---------------------------------------------------------------------------
705
706static void ResetTestState()
707{
708 g_TestResult = 0;
709 g_InstancesDone = 0;
710 g_SharedCounter = 0;
711}
712
713} // namespace blockpool
714} // namespace test
715} // namespace stk
716
717// ---------------------------------------------------------------------------
718// Task-count predicates
719// ---------------------------------------------------------------------------
720
726static bool NeedsTwoTasks(const char *test_name)
727{
728 return (strcmp(test_name, "BlockingAlloc") == 0) ||
729 (strcmp(test_name, "TimedAllocTimeout") == 0) ||
730 (strcmp(test_name, "TimedAllocSuccess") == 0) ||
731 (strcmp(test_name, "ConcurrentAllocFree")== 0) ||
732 (strcmp(test_name, "Stress") == 0);
733}
734
740static bool NeedsAllTasks(const char *test_name)
741{
742 return (strcmp(test_name, "ConcurrentAllocFree") == 0) ||
743 (strcmp(test_name, "Stress") == 0);
744}
745
746// ---------------------------------------------------------------------------
747// RunTest helper
748// ---------------------------------------------------------------------------
749
753template <class TaskType>
754static int32_t RunTest(const char *test_name, int32_t param = 0)
755{
756 using namespace stk;
757 using namespace stk::test;
758 using namespace stk::test::blockpool;
759
760 printf("Test: %s\n", test_name);
761
763
768 sizeof(g_PoolStorage));
769
770 g_Pool = &pool;
771
772 STK_TASK TaskType task0(0, param);
773 STK_TASK TaskType task1(1, param);
774 TaskType task2(2, param);
775 TaskType task3(3, param);
776 TaskType task4(4, param);
777
778 g_Kernel.AddTask(&task0);
779
780 if (NeedsTwoTasks(test_name))
781 g_Kernel.AddTask(&task1);
782
783 if (NeedsAllTasks(test_name))
784 {
785 g_Kernel.AddTask(&task2);
786 g_Kernel.AddTask(&task3);
787 g_Kernel.AddTask(&task4);
788 }
789
790 g_Kernel.Start();
791
792 g_Pool = nullptr;
793
794 int32_t result = (g_TestResult
797
798 printf("Result: %s\n", result == TestContext::SUCCESS_EXIT_CODE ? "PASS" : "FAIL");
799 printf("--------------\n");
800
801 return result;
802}
803
804// ---------------------------------------------------------------------------
805// main
806// ---------------------------------------------------------------------------
807
811int main(int argc, char **argv)
812{
813 (void)argc;
814 (void)argv;
815
817
818 int total_failures = 0, total_success = 0;
819
820 printf("--------------\n");
821
823
824#define RUN(TestClass, name, param) \
825 do { \
826 if (RunTest<TestClass<ACCESS_PRIVILEGED>>(name, param) \
827 != TestContext::SUCCESS_EXIT_CODE) \
828 total_failures++; \
829 else \
830 total_success++; \
831 } while (0)
832
833#ifndef __ARM_ARCH_6M__
834
835 // Test 1: TryAlloc / Free basic cycle
837
838 // Test 2: Exhaust pool, verify IsFull, free all
840
841 // Test 3: Blocking Alloc unblocked by Free from another task
842 RUN(stk::test::blockpool::BlockingAllocTask, "BlockingAlloc", 0);
843
844 // Test 4: TimedAlloc expires when pool remains full
845 RUN(stk::test::blockpool::TimedAllocTimeoutTask, "TimedAllocTimeout", 0);
846
847 // Test 5: TimedAlloc succeeds when block freed within timeout
848 RUN(stk::test::blockpool::TimedAllocSuccessTask, "TimedAllocSuccess", 0);
849
850 // Test 6: Concurrent alloc/free counter integrity (20 iterations per task)
851 RUN(stk::test::blockpool::ConcurrentAllocFreeTask, "ConcurrentAllocFree", 20);
852
853 // Test 7: Typed helpers AllocT / TryAllocT / TimedAllocT
855
856 // Test 8: Free(nullptr) is a safe no-op
858
859 // Test 9: AlignBlockSize static helper
860 RUN(stk::test::blockpool::AlignBlockSizeTask, "AlignBlockSize", 0);
861
862 // Test 10: External vs heap storage constructors + accessors
864
865#endif // __ARM_ARCH_6M__
866
867 // Test 11: Stress test (ARM Cortex-M0 compatible)
869
870#undef RUN
871
872 int32_t final_result = (total_failures == 0
875
876 printf("##############\n");
877 printf("Total tests: %d\n", total_failures + total_success);
878 printf("Failures: %d\n", (int)total_failures);
879
881 return final_result;
882}
Implementation of fixed-size block memory pool: stk::memory::BlockMemoryPool.
Top-level STK include. Provides the Kernel class template and all built-in task-switching strategies.
#define STK_TICKLESS_IDLE
Enables tickless (dynamic-tick) low-power operation during idle periods.
Definition stk_defs.h:36
#define _STK_POOL_TEST_SHORT_SLEEP
static bool NeedsTwoTasks(const char *test_name)
int main(int argc, char **argv)
static bool NeedsAllTasks(const char *test_name)
static int32_t RunTest(const char *test_name, int32_t param=0)
#define _STK_POOL_BLOCK_SIZE
#define _STK_POOL_CAPACITY
#define _STK_POOL_TEST_TASKS_MAX
#define _STK_POOL_TEST_LONG_SLEEP
#define RUN(TestClass, name, param)
#define STK_TASK
#define STK_TEST_DECL_ASSERT
Declare assertion redirector in the source file.
Namespace of STK package.
static int64_t GetTimeNowMs()
Get current time in milliseconds since kernel start.
Definition stk_helper.h:291
void Sleep(Timeout ticks)
Put calling process into a sleep state.
Definition stk_helper.h:326
void Yield()
Notify scheduler to switch to the next runnable task.
Definition stk_helper.h:359
PlatformArmCortexM PlatformDefault
Default platform implementation.
static constexpr Timeout WAIT_INFINITE
Timeout value: block indefinitely until the synchronization object is signaled.
Definition stk_common.h:171
void Delay(Timeout ticks)
Delay calling process by busy-waiting until the deadline expires.
Definition stk_helper.h:370
SwitchStrategyRoundRobin SwitchStrategyRR
Shorthand alias for SwitchStrategyRoundRobin.
@ KERNEL_TICKLESS
Tickless mode. To use this mode STK_TICKLESS_IDLE must be defined to 1 in stk_config....
Definition stk_common.h:45
@ KERNEL_SYNC
Synchronization support (see Event).
Definition stk_common.h:44
@ KERNEL_DYNAMIC
Tasks can be added or removed and therefore exit when done.
Definition stk_common.h:42
BlockMemoryPool(size_t capacity, size_t raw_block_size, uint8_t *storage, size_t storage_size, const char *name=nullptr)
Construct a pool backed by caller-supplied (external) storage.
Namespace of the test inventory.
Namespace of BlockMemoryPool test.
static volatile int32_t g_InstancesDone
static void ResetTestState()
static Kernel< KERNEL_DYNAMIC|KERNEL_SYNC|(STK_TICKLESS_IDLE ? KERNEL_TICKLESS :0), 5, SwitchStrategyRR, PlatformDefault > g_Kernel
static volatile int32_t g_SharedCounter
static stk::memory::BlockMemoryPool * g_Pool
static volatile int32_t g_TestResult
static uint8_t g_PoolStorage[8U *stk::memory::BlockMemoryPool::AlignBlockSize(32U)]
Concrete implementation of IKernel.
Definition stk.h:81
Task(const Task &)=delete
Verifies TryAlloc returns a valid block, Free recycles it, and pool accounting stays consistent throu...
void Run() override
Entry point of the user task.
TryAllocFreeTask(uint8_t task_id, int32_t)
Drains the entire pool via TryAlloc, verifies IsFull() and that a further TryAlloc returns nullptr,...
void Run() override
Entry point of the user task.
ExhaustPoolTask(uint8_t task_id, int32_t)
Task 0 holds all blocks; Task 1 blocks in Alloc(); Task 0 frees one block and verifies Task 1 unblock...
BlockingAllocTask(uint8_t task_id, int32_t)
void Run() override
Entry point of the user task.
Task 0 holds all blocks; Task 1 calls TimedAlloc with a short timeout that must expire,...
void Run() override
Entry point of the user task.
Task 0 holds all blocks; Task 1 calls TimedAlloc with a generous timeout; Task 0 frees a block before...
void Run() override
Entry point of the user task.
All tasks race to alloc a block, increment a shared counter inside the block, copy it out and free; t...
ConcurrentAllocFreeTask(uint8_t task_id, int32_t iterations)
void Run() override
Entry point of the user task.
Verifies that the typed wrappers AllocT<T>(), TryAllocT<T>(), and TimedAllocT<T>() return correctly t...
void Run() override
Entry point of the user task.
TypedAllocTask(uint8_t task_id, int32_t)
Ensures Free(nullptr) returns false and does not corrupt the pool.
FreeNullTask(uint8_t task_id, int32_t)
void Run() override
Entry point of the user task.
Verifies AlignBlockSize() rounds up to BLOCK_ALIGN multiples and never returns a value smaller than B...
AlignBlockSizeTask(uint8_t task_id, int32_t)
void Run() override
Entry point of the user task.
Creates a second pool using heap storage and verifies accessors report correct values for both storag...
StorageModeTask(uint8_t task_id, int32_t)
void Run() override
Entry point of the user task.
All tasks hammer the pool with a mix of TryAlloc, blocking Alloc, and TimedAlloc operations; verifies...
StressTask(uint8_t task_id, int32_t iterations)
void Run() override
Entry point of the user task.
static void ShowTestSuitePrologue()
Show text string as prologue before tests start.
@ DEFAULT_FAILURE_EXIT_CODE
default exit code for exit() to denote failure of the test
@ SUCCESS_EXIT_CODE
exit code for exit() to denote the success of the test
static void ShowTestSuiteEpilogue(int32_t result)
Show text string as epilogue after tests end.