Skip to content

feat: S11.02 extract PoolAllocator helper and migrate CircularBuffer#396

Merged
DavidCozens merged 3 commits into
mainfrom
feat/s11-02-pool-allocator
May 18, 2026
Merged

feat: S11.02 extract PoolAllocator helper and migrate CircularBuffer#396
DavidCozens merged 3 commits into
mainfrom
feat/s11-02-pool-allocator

Conversation

@DavidCozens

@DavidCozens DavidCozens commented May 18, 2026

Copy link
Copy Markdown
Owner

Purpose

Closes #395.

S11.01 (#394) landed the E11 canonical pool pattern in SolidSyslogCircularBufferStatic.c — 13 pool-management functions plus a struct Slot wrapper. Every other E11 class (BlockStore, StreamSender, the Platform Mutex/Stream/Datagram/File family, AtomicCounter, TLS, FatFs, …) is about to copy that same shape. S11.02 hoists everything except the per-class reverse lookup into one TU-internal SolidSyslogPoolAllocator helper, so each *Static.c lands at ~3 functions instead of ~13.

Change Description

New helper (Core/Source/SolidSyslogPoolAllocator.{h,c}, TU-internal — never in Core/Interface/):

  • _AcquireFirstFree(self) — walks the pool with LockConfig wrapping every per-slot probe; returns first claimed index or Count on exhaustion. Matches S11.01's locking invariant exactly.
  • _FreeIfInUse(self, index, cleanup, ctx) — locks once; invokes cleanup inside the lock before clearing InUse. The lock-held-during-cleanup invariant prevents a concurrent _AcquireFirstFree from grabbing the slot mid-cleanup and racing Initialise vs Cleanup on the same memory.
  • _IndexIsValid(self, index) — static-inline predicate.

Struct is just { bool* InUse; size_t Count; } — each *Static.c declares the array and the allocator at file scope. No _Create for the helper, no storage cast anywhere.

Migration of CircularBufferStatic.c:

  • struct Slot retired — Pool is bare SolidSyslogCircularBuffer[], InUse[] separate.
  • 10 file-scope helpers deleted (AcquireFirstFree, AcquireIfFree, Acquire, PoolItemIsFree, PoolItemIsInUse, MarkInUse, MarkFree, HandleFromIndex, HandleIsValid, FreeIfInUse).
  • What stays per-class: _Create, _Destroy, IndexFromHandle (reverse lookup — hoisting needed a typeless callback that would break the no-cast invariant), CleanupAtIndex (3-line bridge), and the two Fallback_* vtable entries.
  • 107 deletions, 17 insertions.

Decisions kept from the design discussion:

  • Helper does not report exhaustion errors itself — each class wants its own SOLIDSYSLOG_ERROR_MSG_<CLASS>_POOL_EXHAUSTED string.
  • No public-header audience-table entry — helper is TU-internal.
  • _Create/_Destroy public signatures byte-identical to pre-migration.

Test Evidence

New Tests/SolidSyslogPoolAllocatorTest.cpp — 12 tests across five behaviours, driven happy-then-locking:

  1. IndexIsValid true/false.
  2. AcquireFirstFree empty / walks-past-in-use / exhausted-returns-Count.
  3. FreeIfInUse happy-with-cleanup / already-free-skips-cleanup / NULL-cleanup-still-frees.
  4. Lock counts: Acquire once for one probe, Acquire POOL_SIZE times for full pool, Free exactly once.
  5. Cleanup invocation under the lock — the spy captures LockCount - UnlockCount at the moment of invocation and asserts it is exactly 1. Would catch any regression that moves cleanup out of the critical section.

Regression net: Tests/SolidSyslogCircularBufferTest.cpp untouched — all 9 Pool tests including CreateAcquiresAndReleasesConfigLockOnFirstFreeSlot, CreateLocksOncePerSlotProbedWhenPoolIsFull, DestroyOfPooledHandleLocksOnce, DestroyOfUnknownHandleDoesNotLock, DestroyOfStaleHandleReportsWarning pass against the new implementation.

Validation step (AC #7): full suite re-run at SOLIDSYSLOG_CIRCULAR_BUFFER_POOL_SIZE=3 — 1139 tests pass (default POOL_SIZE=1 also green).

Coverage: 100% line on both SolidSyslogPoolAllocator.c and the migrated SolidSyslogCircularBufferStatic.c.

cppcheck-misra: 60 hits (was 61 pre-migration baseline — one fewer line eligible for an 8.9 diagnostic). Zero new findings against any of the three modified files.

Areas Affected

  • Core/Source/SolidSyslogPoolAllocator.{h,c} — new TU-internal helper.
  • Core/Source/SolidSyslogCircularBufferStatic.c — rewritten against the helper.
  • Tests/SolidSyslogPoolAllocatorTest.cpp — new.
  • Core/Source/CMakeLists.txt, Tests/CMakeLists.txt — one-line additions each.
  • DEVLOG.md — session entry.

No public-header or integrator-facing change. Public SolidSyslogCircularBuffer_Create / _Destroy signatures byte-identical. BDD targets and examples unaffected.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Refactor

    • Refactored internal pool allocation and slot management operations.
  • Tests

    • Added comprehensive test suite covering pool allocation behavior, index validation, cleanup operations, and synchronization.
  • Chores

    • Updated build configuration to include new allocator component.

Review Change Stack

TU-internal helper that owns the slot-walk and per-iteration LockConfig
over a caller-supplied bool[]. Two operations + a static-inline predicate:

- AcquireFirstFree: locks per probe; first claim wins; Count on exhaustion.
- FreeIfInUse: locks once; invokes cleanup INSIDE the lock before clearing
  InUse; the lock-held-during-cleanup invariant prevents a concurrent
  Acquire from grabbing the slot mid-cleanup and racing Initialise vs
  Cleanup on the same memory.
- IndexIsValid: static-inline predicate over Count.

Not wired into CircularBufferStatic yet -- the migration is the next
commit. 100% line coverage; zero new MISRA findings.

Refs #395.
Drops 10 file-scope helpers (AcquireFirstFree/AcquireIfFree/Acquire/
PoolItemIsFree/PoolItemIsInUse/MarkInUse/MarkFree/HandleFromIndex/
HandleIsValid/FreeIfInUse) and retires the `struct Slot` wrapper.
`Pool` is now a bare array of SolidSyslogCircularBuffer; the
`InUse[]` flags live alongside it and are owned by a TU-static
`SolidSyslogPoolAllocator` instance.

Public API (_Create / _Destroy signatures) and per-instance state
(SolidSyslogCircularBuffer struct, Initialise / Cleanup contracts)
are byte-identical. The existing SolidSyslogCircularBufferTest suite
passes unchanged, including the lock-count assertions that pin the
per-probe LockConfig invariant -- the helper preserves that
behaviour exactly.

What stays per-class: IndexFromHandle (the reverse lookup is the one
genuinely typed bit), CleanupAtIndex (3-line bridge from the helper's
typeless callback to CircularBuffer_Cleanup), and the Fallback vtable.
Two helpers plus two vtable entries -- the rest is the public API.

Validated by running the full test suite at
SOLIDSYSLOG_CIRCULAR_BUFFER_POOL_SIZE=3 (1139 tests pass; default
POOL_SIZE=1 also green). Zero new cppcheck-misra findings (count
60 vs 61 pre-migration -- one fewer line eligible for an 8.9
diagnostic). 100% line coverage on both
SolidSyslogCircularBufferStatic.c and SolidSyslogPoolAllocator.c.

Refs #395.
@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4506e551-1979-4e5d-9ece-320be3b1f72f

📥 Commits

Reviewing files that changed from the base of the PR and between 29c6cd1 and 58cf5f7.

📒 Files selected for processing (7)
  • Core/Source/CMakeLists.txt
  • Core/Source/SolidSyslogCircularBufferStatic.c
  • Core/Source/SolidSyslogPoolAllocator.c
  • Core/Source/SolidSyslogPoolAllocator.h
  • DEVLOG.md
  • Tests/CMakeLists.txt
  • Tests/SolidSyslogPoolAllocatorTest.cpp

📝 Walkthrough

Walkthrough

This PR extracts a TU-internal pool allocator helper (SolidSyslogPoolAllocator) providing atomic slot acquire/free operations under lock, then migrates SolidSyslogCircularBufferStatic to use it instead of inline pool-management code, replacing ~90 lines of file-scope helpers with allocator API calls. A comprehensive test suite verifies index validation, acquisition/exhaustion behavior, cleanup semantics, and critical-section locking invariants.

Changes

Pool Allocator Extraction and CircularBuffer Migration

Layer / File(s) Summary
Pool Allocator Public Contract
Core/Source/SolidSyslogPoolAllocator.h
Defines SolidSyslogPoolAllocator struct (tracking bool* InUse array and size_t Count), SolidSyslogPoolCleanup callback type, and declares public operations: AcquireFirstFree, FreeIfInUse with optional cleanup, and inline IndexIsValid validator; establishes C linkage via EXTERN_C_BEGIN/END.
Pool Allocator Implementation
Core/Source/SolidSyslogPoolAllocator.c
AcquireFirstFree scans from index 0 and atomically claims the first free slot via PoolAllocator_TryClaim (check-and-set under SolidSyslog_LockConfig); FreeIfInUse conditionally releases only in-use slots, invoking optional cleanup(index, context) callback while holding the lock, then marks free. Inline slot-state helpers encapsulate InUse array access.
CircularBuffer Pool Migration
Core/Source/SolidSyslogCircularBufferStatic.c
Replaces struct Slot and ~10 file-scope pool helpers with separated InUse[] and Pool[] arrays and SolidSyslogPoolAllocator instance. Create acquires index via AcquireFirstFree, initializes Pool[index].Base on success, or returns Fallback on exhaustion. Destroy computes index via CircularBuffer_IndexFromHandle, frees via FreeIfInUse with CircularBuffer_CleanupAtIndex callback. Pool lookup now compares against &Pool[poolIndex].Base.
Pool Allocator Test Suite
Tests/SolidSyslogPoolAllocatorTest.cpp
Covers index validation (boundary cases), AcquireFirstFree behavior (empty pool, walk past occupied, exhaustion returning Count), FreeIfInUse behavior (release used slots, cleanup invocation/semantics, null cleanup support), and lock-critical assertions (per-probe LockConfig wrapping, cleanup execution inside lock verified via lock-delta spy).
Build Integration and Documentation
Core/Source/CMakeLists.txt, Tests/CMakeLists.txt, DEVLOG.md
Adds SolidSyslogPoolAllocator.c to library sources and SolidSyslogPoolAllocatorTest.cpp to test executable. DEVLOG entry (2026-05-18, S11.02) documents allocator design, CircularBuffer migration details, and test coverage of locking and cleanup invariants.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • DavidCozens/solid-syslog#395: This PR fully implements the S11.02 task: extracting the TU-internal SolidSyslogPoolAllocator with the specified API, migrating SolidSyslogCircularBufferStatic, and delivering comprehensive test coverage with DEVLOG documentation.
  • DavidCozens/solid-syslog#392: Related through shared pool slot management and locking patterns in the circular buffer static allocation; this PR refactors the same pool acquire/free logic into a reusable allocator.
  • DavidCozens/solid-syslog#29: This PR implements the E11 "Static-Allocation Variant" pilot by introducing the pool-allocation pattern (SolidSyslogPoolAllocator) and applying it to SolidSyslogCircularBufferStatic.

Possibly related PRs

  • DavidCozens/solid-syslog#393: The new SolidSyslogPoolAllocator implementation wraps slot operations with SolidSyslog_LockConfig()/SolidSyslog_UnlockConfig(), directly depending on the config-lock injection API introduced in that PR.

Poem

🐰 A pool of slots, now clean and bright,
Extracted helpers, locked just right,
CircularBuffer hops around the bend,
Reusable code from start to end! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main changes: extracting a PoolAllocator helper and migrating CircularBuffer to use it, following Conventional Commits format.
Description check ✅ Passed The description is comprehensive and covers all required template sections: Purpose (closes #395), Change Description (detailed new helper and migration), Test Evidence (12 tests, coverage metrics, validation), and Areas Affected (all modified files listed).
Linked Issues check ✅ Passed All coding requirements from issue #395 are met: TU-internal helper with specified API, per-slot locking, caller-supplied InUse array, CircularBuffer migration completed, comprehensive tests with cleanup-inside-lock verification, lock-count assertions passing, and no MISRA suppressions introduced.
Out of Scope Changes check ✅ Passed All changes are in scope: new helper implementation, CircularBuffer migration, test suite additions, CMakeLists updates for build integration, and DEVLOG documentation. No unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/s11-02-pool-allocator

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

Copy link
Copy Markdown
Contributor

☀️   Quality Summary

   🚦   build-linux-gcc: 100% successful (✔️ 1145 passed, 🙈 2 skipped)
   🚦   build-freertos-host-tdd: 100% successful (✔️ 1265 passed, 🙈 2 skipped)
   🚦   build-linux-clang: 100% successful (✔️ 1097 passed, 🙈 2 skipped)
   🚦   sanitize-linux-gcc: 100% successful (✔️ 1097 passed, 🙈 2 skipped)
   🚦   integration-linux-openssl: 100% successful (✔️ 9 passed)
   🚦   integration-windows-openssl: 100% successful (✔️ 9 passed)
   🚦   bdd-linux-syslog-ng: 94% successful (✔️ 46 passed, 🙈 3 skipped)
   🚦   bdd-windows-otel: 90% successful (✔️ 44 passed, 🙈 5 skipped)
   🚦   bdd-freertos-qemu: 73% successful (✔️ 36 passed, 🙈 13 skipped)
   🚦   build-windows-msvc: 100% successful (✔️ 1001 passed, 🙈 1 skipped)
   🚦   build-linux-tunable-override: 100% successful (✔️ 1097 passed, 🙈 2 skipped)
   ⚠️   Clang-Tidy: 9 warnings (normal: 9)
   ⚠️   CPPCheck: No warnings


Created by Quality Monitor v1.14.0 (#f3859fd). More details are shown in the GitHub Checks Result.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

S11.02: Extract SolidSyslogPoolAllocator and migrate CircularBuffer onto it

1 participant