|
SuperTinyKernel™ RTOS 1.08.x
Lightweight, high-performance, deterministic, bare-metal C++ RTOS for resource-constrained embedded systems. MIT Open Source License.
|
Variable-size (first-fit) allocator with block coalescing and proper task-blocking semantics. More...
#include <stk_memory_bytepool.h>
Classes | |
| struct | BlockHeader |
| Header preceding every block (free or used) inside the pool area. More... | |
Public Member Functions | |
| BytePool (uint8_t *storage, size_t storage_size, const char *name=nullptr) | |
| Construct a pool backed by caller-supplied (external) storage. | |
| BytePool (size_t storage_size, const char *name=nullptr) | |
| Construct a pool with heap-allocated storage. | |
| STK_VIRT_DTOR | ~BytePool () |
| Destructor. | |
| EWaitResult | TimedAllocEx (void **block, size_t size, Timeout timeout_ticks=WAIT_INFINITE) |
| Allocate a block, preserving the full wait outcome. | |
| void * | TimedAlloc (size_t size, Timeout timeout_ticks=WAIT_INFINITE) |
| Allocate a block, blocking until memory becomes available or the timeout expires. | |
| template<typename T> | |
| T * | TimedAllocT (Timeout timeout_ticks=WAIT_INFINITE) |
| Allocate one typed block, blocking until memory becomes available or the timeout expires. | |
| void * | Alloc (size_t size) |
| Allocate a block, blocking indefinitely until memory is available. | |
| template<typename T> | |
| T * | AllocT () |
| Allocate one typed block, blocking indefinitely until memory is available. | |
| void * | TryAlloc (size_t size) |
| Non-blocking allocation attempt. | |
| template<typename T> | |
| T * | TryAllocT () |
| Non-blocking typed allocation attempt. | |
| bool | Free (void *ptr) |
| Return a previously allocated block to the pool. | |
| void | GetInfo (size_t &available, size_t &largest, size_t &fragments) const |
| Collect pool statistics. | |
| bool | IsStorageValid () const |
| Verify that the backing storage is valid and the pool is ready for use. | |
| size_t | GetSize () const |
| Get the total size of the managed (aligned) area in bytes, headers included. | |
| size_t | GetMaxRequest () const |
| Get the largest request that can ever succeed on this pool. | |
| void | SetTraceName (const char *name) |
| Set name. | |
| const char * | GetTraceName () const |
| Get name. | |
Static Public Member Functions | |
| static constexpr size_t | AlignSize (size_t raw_size) |
| Round a size up to the nearest multiple of the block alignment (8 bytes). | |
| static constexpr size_t | RequiredSize (size_t payload_size) |
| Compute the pool space consumed by a single allocation of payload_size bytes. | |
Static Public Attributes | |
| static const size_t | HEADER_SIZE = 8U |
| Size of the per-block header in bytes. | |
Private Member Functions | |
| STK_NONCOPYABLE_CLASS (BytePool) | |
| void | Init () |
| Align the area and create the initial single free block. | |
| bool | IsSane (const BlockHeader *h) const |
| Check that the header h (which must be inside the pool) looks intact. | |
| void | Coalesce (BlockHeader *h) |
| Merge h (a free block) with the free blocks that directly follow it. | |
| void * | FindFree (size_t need) |
| Find (and claim) the first free block that can hold need bytes. | |
| bool | IsBlockStart (const BlockHeader *h) const |
| Check that h is the header of an existing block (walks the list from the start). | |
Static Private Member Functions | |
| static BlockHeader * | Next (const BlockHeader *h) |
| Get the header of the block that follows h. | |
| static void | AccountRun (size_t run, size_t &available, size_t &largest, size_t &fragments) |
| Add one free run of run bytes (headers included) to the statistics. | |
Private Attributes | |
| uint8_t * | m_storage |
| raw storage as supplied or allocated (before alignment) | |
| BlockHeader * | m_first |
| first block header (aligned), nullptr if the pool is invalid | |
| BlockHeader * | m_end |
| one past the last block (aligned), nullptr if the pool is invalid | |
| sync::ConditionVariable | m_cv |
| signalled by Free() to wake tasks blocked in TimedAlloc() | |
| size_t | m_storage_size |
| size of the raw storage in bytes | |
| bool | m_storage_owned |
| true -> storage is heap-allocated; free in destructor | |
Static Private Attributes | |
| static const size_t | BLOCK_ALIGN = 8U |
| Payload alignment and block size granularity in bytes. | |
| static const size_t | MIN_BLOCK_SIZE = (HEADER_SIZE + BLOCK_ALIGN) |
Smallest block: a header plus the minimum payload (BLOCK_ALIGN). | |
| static const size_t | POOL_SIZE_MAX = static_cast<size_t>(0xFFFFFFF8UL) |
| Largest managed area: block sizes are stored as 32-bit values. | |
| static const uint32_t | STATE_FREE = 0x46524545UL |
| "FREE" | |
| static const uint32_t | STATE_USED = 0x55534544UL |
| "USED" | |
| static const uint32_t | STATE_NONE = 0x00000000UL |
| stale header absorbed by a merge | |
Variable-size (first-fit) allocator with block coalescing and proper task-blocking semantics.
BytePool manages a single contiguous memory area and hands out blocks of arbitrary size from it. It is the counterpart of BlockMemoryPool for workloads where allocation sizes vary (message payloads, strings, protocol frames), at the price of a non-constant allocation time and the possibility of fragmentation.
The pool keeps an implicit block list inside the storage area. Every block (free or used) is preceded by an 8-byte header holding the total block size and a state marker. Blocks are laid out back to back, so the next block is always found at header + size and no separate metadata is required.
Memory layout (pool storage, contiguous):
[ hdr | payload ][ hdr | payload (free) ][ hdr | payload ] ... ^ ^ m_first m_end Payload pointers returned to the caller are aligned to \c BLOCK_ALIGN (8 bytes).
Allocation policy**: first-fit. While scanning, adjacent free blocks are merged, so a failed scan always leaves the pool fully coalesced. A found block is split when the remainder is large enough to hold a header and a minimum payload.
Freeing**: the block is marked free and merged with the free blocks that follow it. Merging with a free block that precedes it is done lazily by the next scan, so every free run is always reported and allocated as a single block.
Two storage modes** allow fully static (zero-heap) deployments:
| Mode | Constructor | Frees storage? |
|---|---|---|
| External storage | BytePool(buf, bufsz [, name]) | No - caller |
| Heap storage | BytePool(bufsz [, name]) | Yes - dtor |
Blocking semantics**: TimedAlloc() suspends the calling task via a ConditionVariable until enough contiguous memory is released by Free(), or the timeout expires. All waiters are woken on every Free() (their requests differ in size, so waking only one could leave a satisfiable request sleeping); each re-checks its own request and goes back to sleep if it still does not fit. The original timeout is converted to an absolute deadline so repeated wake-ups never extend it.
ISR safety**: TryAlloc() and Free() may be called from an ISR (guarded by a critical section) but, unlike BlockMemoryPool, their execution time is not constant: it is proportional to the number of blocks scanned. Do not use them from time-critical ISRs on pools with many blocks. Alloc() and TimedAlloc() with a non-zero timeout must only be called from task context.
TryAlloc() and Free() are always available regardless of kernel mode. GetMaxRequest() can never succeed and fails immediately instead of blocking forever. Definition at line 105 of file stk_memory_bytepool.h.
|
inlineexplicit |
Construct a pool backed by caller-supplied (external) storage.
The pool references storage directly without taking ownership. The caller must keep the buffer alive for the entire lifetime of this object. The storage is not freed on destruction. A misaligned start address is corrected internally (the unaligned head and tail bytes are not used).
| [in] | storage | Pointer to a caller-owned byte buffer. Should be aligned to BLOCK_ALIGN (8 bytes) to avoid wasting bytes. |
| [in] | storage_size | Size of storage in bytes. Must be large enough to hold at least one header and a minimum payload; otherwise IsStorageValid() returns false. |
| [in] | name | Optional name forwarded to ITraceable::SetTraceName(). |
Definition at line 390 of file stk_memory_bytepool.h.
References Init(), m_cv, m_end, m_first, m_storage, m_storage_owned, m_storage_size, MIN_BLOCK_SIZE, stk::ITraceable::SetTraceName(), STK_ASSERT, and STK_UNUSED.
Referenced by STK_NONCOPYABLE_CLASS().
|
inlineexplicit |
Construct a pool with heap-allocated storage.
Allocates a flat byte buffer of storage_size bytes via MemoryAllocator::AllocateArrayT (non-throwing). When exceptions are disabled (embedded default), check IsStorageValid() immediately after construction to detect allocation failure before first use.
| [in] | storage_size | Size of the pool area in bytes (headers included). |
| [in] | name | Optional name forwarded to ITraceable::SetTraceName(). |
Definition at line 411 of file stk_memory_bytepool.h.
References Init(), m_cv, m_end, m_first, m_storage, m_storage_owned, m_storage_size, MIN_BLOCK_SIZE, stk::ITraceable::SetTraceName(), STK_ASSERT, and STK_UNUSED.
|
inline |
Destructor.
Frees heap-allocated storage if m_storage_owned is true. External storage is never touched.
Alloc() or TimedAlloc() is a logical error (dangling waiters). An assertion is triggered in debug builds inside the ConditionVariable destructor. Definition at line 432 of file stk_memory_bytepool.h.
References stk::memory::MemoryAllocator::FreeArrayT(), m_end, m_first, m_storage, m_storage_owned, and m_storage_size.
|
inlinestaticprivate |
Add one free run of run bytes (headers included) to the statistics.
Definition at line 802 of file stk_memory_bytepool.h.
References HEADER_SIZE.
Referenced by GetInfo().
|
inlinestaticconstexpr |
Round a size up to the nearest multiple of the block alignment (8 bytes).
| [in] | raw_size | Size in bytes. |
Definition at line 157 of file stk_memory_bytepool.h.
References BLOCK_ALIGN.
Referenced by Init(), and RequiredSize().
|
inline |
Allocate a block, blocking indefinitely until memory is available.
| [in] | size | Requested payload size in bytes. |
nullptr only if the wait was cancelled via IKernel::CancelTaskWait() or the request is invalid (never on timeout). Use TimedAllocEx() to tell these apart. Definition at line 566 of file stk_memory_bytepool.h.
References TimedAlloc(), and stk::WAIT_INFINITE.
|
inline |
Allocate one typed block, blocking indefinitely until memory is available.
nullptr (see Alloc()). Definition at line 572 of file stk_memory_bytepool.h.
References TimedAllocT(), and stk::WAIT_INFINITE.
|
inlineprivate |
Merge h (a free block) with the free blocks that directly follow it.
Definition at line 735 of file stk_memory_bytepool.h.
References IsSane(), m_end, Next(), stk::memory::BytePool::BlockHeader::size, STATE_FREE, and STATE_NONE.
Referenced by FindFree(), and Free().
|
inlineprivate |
Find (and claim) the first free block that can hold need bytes.
| [in] | need | Total block size including header (already aligned). |
nullptr if no free block is large enough. Definition at line 747 of file stk_memory_bytepool.h.
References Coalesce(), IsSane(), m_end, m_first, MIN_BLOCK_SIZE, Next(), stk::memory::BytePool::BlockHeader::size, stk::memory::BytePool::BlockHeader::state, STATE_FREE, STATE_USED, and STK_ASSERT.
Referenced by TimedAllocEx().
|
inline |
Return a previously allocated block to the pool.
Marks the block free, merges it with the free blocks that follow it and wakes all tasks blocked inside Alloc() or TimedAlloc() so each can re-check its own request. A woken task may find that the memory was taken by a racing TryAlloc() and continue waiting.
| [in] | ptr | Pointer previously returned by an allocation function of this pool instance. Must not be nullptr. Range, alignment and the block state marker are validated; failures trigger an assertion in debug builds and return false in release builds. In debug builds the pointer is additionally verified to be the start of a real block. |
true - block successfully returned. false - ptr is nullptr, does not belong to this pool, is misaligned or the block is not in use (double-free); each case indicates a caller logic error. TryAlloc()). Free(). Definition at line 592 of file stk_memory_bytepool.h.
References BLOCK_ALIGN, Coalesce(), HEADER_SIZE, IsBlockStart(), IsSane(), m_cv, m_end, m_first, stk::hw::PtrToWord(), stk::memory::BytePool::BlockHeader::state, STATE_FREE, STATE_USED, and STK_ASSERT.
|
inline |
Collect pool statistics.
Walks the block list under a critical section, so the three values are mutually consistent. Adjacent free blocks are counted as one fragment.
| [out] | available | Total payload bytes that can still be allocated, summed over all free fragments (a single request can use at most largest of them). |
| [out] | largest | Payload size of the largest free fragment, i.e. the biggest request that would succeed right now. |
| [out] | fragments | Number of free fragments (1 means no external fragmentation). |
Definition at line 658 of file stk_memory_bytepool.h.
References AccountRun(), IsSane(), m_end, m_first, Next(), STATE_FREE, and STK_ASSERT.
Referenced by tx_byte_pool_info_get().
|
inline |
Get the largest request that can ever succeed on this pool.
Equals the payload of a single block spanning the entire pool.
Definition at line 302 of file stk_memory_bytepool.h.
References GetSize(), HEADER_SIZE, and MIN_BLOCK_SIZE.
Referenced by TimedAllocEx().
|
inline |
Get the total size of the managed (aligned) area in bytes, headers included.
Definition at line 291 of file stk_memory_bytepool.h.
References m_end, and m_first.
Referenced by GetMaxRequest().
|
inlineinherited |
Get name.
NULL if not set or if STK_SYNC_DEBUG_NAMES is 0. Definition at line 582 of file stk_common.h.
|
inlineprivate |
Align the area and create the initial single free block.
m_first and m_end as nullptr if the area is unusable. Definition at line 697 of file stk_memory_bytepool.h.
References AlignSize(), BLOCK_ALIGN, HEADER_SIZE, m_end, m_first, m_storage, m_storage_size, MIN_BLOCK_SIZE, POOL_SIZE_MAX, stk::hw::PtrToWord(), STATE_FREE, and stk::hw::WordToPtr().
Referenced by BytePool(), and BytePool().
|
inlineprivate |
Check that h is the header of an existing block (walks the list from the start).
Definition at line 786 of file stk_memory_bytepool.h.
References IsSane(), m_end, m_first, and Next().
Referenced by Free().
|
inlineprivate |
Check that the header h (which must be inside the pool) looks intact.
Verifies the state marker, the size granularity and that the block does not extend beyond the pool end. Protects the list walkers from endless loops and out-of-bounds reads if a caller overruns a payload.
Definition at line 725 of file stk_memory_bytepool.h.
References BLOCK_ALIGN, m_end, MIN_BLOCK_SIZE, stk::memory::BytePool::BlockHeader::size, stk::memory::BytePool::BlockHeader::state, STATE_FREE, and STATE_USED.
Referenced by Coalesce(), FindFree(), Free(), GetInfo(), and IsBlockStart().
|
inline |
Verify that the backing storage is valid and the pool is ready for use.
Always true for pools constructed with sufficiently large external storage. For heap-constructed pools, false if the allocation failed. false as well if the supplied area is too small to hold even a single block.
true if the pool is ready for use. Definition at line 285 of file stk_memory_bytepool.h.
References m_first.
|
inlinestaticprivate |
Get the header of the block that follows h.
m_end, which must not be dereferenced. Definition at line 345 of file stk_memory_bytepool.h.
References stk::memory::BytePool::BlockHeader::size.
Referenced by Coalesce(), FindFree(), GetInfo(), and IsBlockStart().
|
inlinestaticconstexpr |
Compute the pool space consumed by a single allocation of payload_size bytes.
Equals the aligned payload plus the block header. Use it to size external storage, adding the value for every block that must be alive at the same time:
| [in] | payload_size | Requested payload size in bytes. |
Definition at line 171 of file stk_memory_bytepool.h.
References AlignSize(), and HEADER_SIZE.
Referenced by TimedAllocEx(), and tx_byte_pool_create().
|
inlineinherited |
Set name.
| [in] | name | Null-terminated string or NULL. |
Definition at line 570 of file stk_common.h.
References STK_UNUSED.
Referenced by stk::memory::BlockMemoryPool::BlockMemoryPool(), stk::memory::BlockMemoryPool::BlockMemoryPool(), stk::memory::BytePool::BytePool(), stk::memory::BytePool::BytePool(), tx_block_pool_create(), tx_event_flags_create(), tx_mutex_create(), tx_queue_create(), and tx_semaphore_create().
|
private |
|
inline |
Allocate a block, blocking until memory becomes available or the timeout expires.
| [in] | size | Requested payload size in bytes. |
| [in] | timeout_ticks | Maximum time to wait (ticks). Same semantics as TimedAllocEx(). |
nullptr on timeout, cancellation or invalid arguments. TimedAllocEx() to tell them apart. NO_WAIT; ISR-unsafe otherwise. Definition at line 546 of file stk_memory_bytepool.h.
References TimedAllocEx(), and stk::WAIT_RESULT_SIGNAL.
Referenced by Alloc(), TimedAllocT(), and TryAlloc().
|
inline |
Allocate a block, preserving the full wait outcome.
Searches for a free block of at least size bytes. If none exists the calling task is suspended via the internal ConditionVariable and re-tries after every Free() until the request is satisfied, the timeout expires or the wait is cancelled. The timeout is a total budget, not per wake-up.
| [out] | block | Receives the allocated block on WAIT_RESULT_SIGNAL, nullptr on any other result. Must not be nullptr. |
| [in] | size | Requested payload size in bytes (must be > 0 and <= GetMaxRequest()). |
| [in] | timeout_ticks | Maximum time to wait (ticks). WAIT_INFINITE - block indefinitely (default). NO_WAIT - fail immediately if no suitable block exists (ISR-safe). |
WAIT_RESULT_SIGNAL - a block was allocated (immediately or after waiting), WAIT_RESULT_TIMEOUT - no suitable block became available before the timeout (or NO_WAIT was passed); a block freed at the very moment the timeout expired is still returned as SIGNAL, WAIT_RESULT_CANCELED - the wait was cancelled via IKernel::CancelTaskWait(), WAIT_RESULT_FAIL - invalid arguments (block is nullptr, size is 0 or exceeds GetMaxRequest(), no valid storage) or the kernel rejected the wait. NO_WAIT; ISR-unsafe otherwise. Definition at line 451 of file stk_memory_bytepool.h.
References FindFree(), GetMaxRequest(), stk::GetTicks(), stk::hw::IsInsideISR(), m_cv, stk::NO_WAIT, RequiredSize(), STK_ASSERT, stk::WAIT_INFINITE, stk::WAIT_RESULT_FAIL, stk::WAIT_RESULT_SIGNAL, and stk::WAIT_RESULT_TIMEOUT.
Referenced by TimedAlloc().
|
inline |
Allocate one typed block, blocking until memory becomes available or the timeout expires.
Thin typed wrapper around TimedAlloc() for sizeof(T) bytes. The memory is uninitialized; no constructor is run. T must not require alignment above 8 bytes.
| [in] | timeout_ticks | Maximum time to wait (ticks). |
nullptr if the timeout expired. NO_WAIT; ISR-unsafe otherwise. Definition at line 559 of file stk_memory_bytepool.h.
References BLOCK_ALIGN, STK_ASSERT, and TimedAlloc().
Referenced by AllocT(), and TryAllocT().
|
inline |
Non-blocking allocation attempt.
| [in] | size | Requested payload size in bytes. |
nullptr if no suitable free block exists. Definition at line 577 of file stk_memory_bytepool.h.
References stk::NO_WAIT, and TimedAlloc().
|
inline |
Non-blocking typed allocation attempt.
nullptr if no suitable free block exists. TryAlloc()). Definition at line 583 of file stk_memory_bytepool.h.
References stk::NO_WAIT, and TimedAllocT().
|
staticprivate |
Payload alignment and block size granularity in bytes.
Definition at line 322 of file stk_memory_bytepool.h.
Referenced by AlignSize(), Free(), Init(), IsSane(), and TimedAllocT().
|
static |
Size of the per-block header in bytes.
Every allocation consumes HEADER_SIZE bytes of the pool in addition to its (aligned) payload.
Definition at line 112 of file stk_memory_bytepool.h.
Referenced by AccountRun(), Free(), GetMaxRequest(), Init(), and RequiredSize().
|
private |
signalled by Free() to wake tasks blocked in TimedAlloc()
Definition at line 381 of file stk_memory_bytepool.h.
Referenced by BytePool(), BytePool(), Free(), and TimedAllocEx().
|
private |
one past the last block (aligned), nullptr if the pool is invalid
Definition at line 380 of file stk_memory_bytepool.h.
Referenced by BytePool(), BytePool(), Coalesce(), FindFree(), Free(), GetInfo(), GetSize(), Init(), IsBlockStart(), IsSane(), and ~BytePool().
|
private |
first block header (aligned), nullptr if the pool is invalid
Definition at line 379 of file stk_memory_bytepool.h.
Referenced by BytePool(), BytePool(), FindFree(), Free(), GetInfo(), GetSize(), Init(), IsBlockStart(), IsStorageValid(), and ~BytePool().
|
private |
raw storage as supplied or allocated (before alignment)
Definition at line 378 of file stk_memory_bytepool.h.
Referenced by BytePool(), BytePool(), Init(), and ~BytePool().
|
private |
true -> storage is heap-allocated; free in destructor
Definition at line 383 of file stk_memory_bytepool.h.
Referenced by BytePool(), BytePool(), and ~BytePool().
|
private |
size of the raw storage in bytes
Definition at line 382 of file stk_memory_bytepool.h.
Referenced by BytePool(), BytePool(), Init(), and ~BytePool().
|
staticprivate |
Smallest block: a header plus the minimum payload (BLOCK_ALIGN).
Also the smallest remainder worth splitting off.
Definition at line 327 of file stk_memory_bytepool.h.
Referenced by BytePool(), BytePool(), FindFree(), GetMaxRequest(), Init(), and IsSane().
|
staticprivate |
Largest managed area: block sizes are stored as 32-bit values.
Definition at line 331 of file stk_memory_bytepool.h.
Referenced by Init().
|
staticprivate |
"FREE"
Definition at line 333 of file stk_memory_bytepool.h.
Referenced by Coalesce(), FindFree(), Free(), GetInfo(), Init(), and IsSane().
|
staticprivate |
stale header absorbed by a merge
Definition at line 335 of file stk_memory_bytepool.h.
Referenced by Coalesce().
|
staticprivate |
"USED"
Definition at line 334 of file stk_memory_bytepool.h.
Referenced by FindFree(), Free(), and IsSane().