Skip to content

feat: S11.05 part A — public storage-cast classes onto PoolAllocator + shared null objects#402

Merged
DavidCozens merged 5 commits into
mainfrom
refactor/s11-05-storage-cast-sweep
May 19, 2026
Merged

feat: S11.05 part A — public storage-cast classes onto PoolAllocator + shared null objects#402
DavidCozens merged 5 commits into
mainfrom
refactor/s11-05-storage-cast-sweep

Conversation

@DavidCozens

@DavidCozens DavidCozens commented May 19, 2026

Copy link
Copy Markdown
Owner

Purpose

First half of S11.05 (#401). Migrates the two public storage-cast classes
(StreamSender, FileBlockDevice) onto SolidSyslogPoolAllocator, ships two
new shared GoF nulls that the migrations depend on, and retrofits every
previously-migrated class so use-after-destroy is a safe no-op rather than
a NULL-fn-pointer crash.

The remaining BlockStore composition migration (RecordStore + BlockSequence

Refs: #401, parent epic #29.

Change Description

Four commits, three architectural threads:

Public-class migrations (the headline E11 work)

refactor: S11.05 migrate StreamSender onto PoolAllocator and
feat: S11.05 migrate FileBlockDevice onto PoolAllocator. Both adopt the
canonical 3-TU split (<Class>.c + <Class>Private.h + <Class>Static.c),
delete the public SolidSyslog<Class>Storage typedef and
SOLIDSYSLOG_<*>_*_SIZE enum from the audience headers, and drop the
storage parameter from _Create. Pool size tunables ship as
SOLIDSYSLOG_STREAM_SENDER_POOL_SIZE and
SOLIDSYSLOG_FILE_BLOCK_DEVICE_POOL_SIZE (default 1; floor 1 enforced
at compile time). Pool exhaustion resolves to the shared NullSender /
NullBlockDevice; bad-config errors emit SolidSyslog_Error(ERROR, ...).

Callers updated: BDD targets (Linux / Windows / FreeRTOS / two TLS
common variants), the relevant test fixtures, the public-header
audience-table rows in CLAUDE.md.

Use-after-destroy crash-safety

fix: S11.05 install null-object vtable on _Cleanup for crash-safety.
A separate commit because it touches eight classes — six S11.04
migrations plus the S11.01 pilot plus my new StreamSender. The previous
shape NULL'd the abstract-base function pointers in _Cleanup, so
calling Send / Disconnect / Write / Read / Format through a stale
handle was an immediate crash. The pre-E11 *self = DESTROYED_INSTANCE
pattern had the same hazard.

Replaced by *base = *SolidSyslogNull<Family>_Get(); — copy the shared
GoF null's vtable into the slot's abstract base. Derived fields are
TU-private and the next _Initialise overwrites them on slot reuse, so
no need to wipe them on Cleanup. UdpSender and StreamSender call their
own Disconnect first (still need the live config); the rest is a
one-liner. One UseAfterDestroyIsCrashSafeVia…Vtable test per class
exercises Send / Format / Write / Read through a stale handle.

Two new shared GoF nulls

SolidSyslogNullBlockDevice and SolidSyslogNullBuffer ship as public
`Get`-only objects joining the NullSender / NullSd / NullStore family.
NullBlockDevice methods all return false / 0; NullBuffer.Write swallows
and NullBuffer.Read returns false with bytesRead=0. The same instance
serves two consumers in this PR: the pool-exhaustion fallback on
<Class>_Create and the vtable target on <Class>_Cleanup.

PassthroughBuffer + CircularBuffer also retrofit to use the new
NullBuffer — their class-private Fallback static (added in the
crash-safety commit as an interim shape) is deleted entirely.
FileBlockDevice arrives fresh with no class-private fallback at all.

Test Evidence

  • 23 new tests across the new TUs and the touched classes:
    • 8× use-after-destroy tests (one per migrated class touched by
      the crash-safety retrofit + StreamSender + FileBlockDevice)
    • 9× NullBlockDevice tests (Get + each vtable method + identity)
    • 4× NullBuffer tests (Get + Write + Read + identity)
    • 2× pool TEST_GROUPs added for StreamSender and FileBlockDevice
      (`FillingPoolThenOverflowReturnsDistinctFallback` per the
      S11.05 acceptance criterion)
  • All previously-existing tests carry forward. Branch test count:
    1178 in the Core executable (was 1155 on `main`).
  • Validated at `SOLIDSYSLOG__POOL_SIZE=3` per AC E07: Structured Data #9, via
    the `SOLIDSYSLOG_USER_TUNABLES_FILE` override mechanism, for both
    StreamSender and FileBlockDevice. Full suite green at the bumped
    pool sizes.
  • Verified in `cpputest-freertos` per
    `feedback_verify_in_freertos_host_image`. All eight test binaries
    green with `FREERTOS_KERNEL_PATH=/opt/freertos/kernel` set:
    Core (1178), FatFs (28), Cmsdk (15), FreeRtosDatagram (21),
    FreeRtosMutex (5), FreeRtosStaticResolver (10), FreeRtosSysUpTime
    (4), FreeRtosTcpStream (37). 1298 tests total.

MISRA

`cppcheck-misra` count: 92 (was 98 on `main`). Six findings touched:

  • Five evaporated

    • `SolidSyslogCircularBufferStatic.c` 5.9 + 8.9 (the bare
      `Fallback_*` statics are gone)
    • `SolidSyslogPassthroughBufferStatic.c` 5.9 + 8.9 (same)
    • `SolidSyslogStreamSender.c` 8.9 (DEFAULT_INSTANCE /
      DESTROYED_INSTANCE deleted by the migration)
  • Two new (same shape as existing main findings) —

    • `SolidSyslogNullBlockDevice.c` 8.9
    • `SolidSyslogNullBuffer.c` 8.9

    Both fire on the file-scope `instance` variable that every Null*
    class uses; NullSender / NullSd / NullStore already have the same
    8.9 finding on `main`. The new shared nulls inherit the established
    pattern.

Zero new architectural findings; the cleanup is strictly net-positive.

Local gates run

All green: `debug`, `clang-debug`, `sanitize`, `coverage` (99.6%
line, 99.1% function), `tidy`, `cppcheck` (clean), `cppcheck-misra`
(diff above), `clang-format` (dry-run clean), `iwyu`. Plus the
cpputest-freertos cross-check above.

Areas Affected

  • Core/Interface/: two new public headers
    (`SolidSyslogNullBlockDevice.h`, `SolidSyslogNullBuffer.h`); the
    `SolidSyslogStreamSender.h` and `SolidSyslogFileBlockDevice.h`
    audience headers lose their `Storage` typedef + `_SIZE` enum and
    the `_Create` storage parameter; new pool-size tunables in
    `SolidSyslogTunablesDefaults.h`.
  • Core/Source/: two new shared-null implementations; two new
    3-TU splits per migrated public class; the eight `_Cleanup`
    bodies across the previously-migrated classes; the new error-message
    symbols.
  • Tests/: two new test files for the shared nulls; eight new
    use-after-destroy tests; two new pool TEST_GROUPs.
  • Bdd/Targets/: five caller sites lose the `SolidSyslog<*>Storage`
    local declarations and the storage argument to `_Create`.
  • CLAUDE.md: two new audience-table rows; three rows updated.

Out of scope (for part B / future)

  • BlockStore composition migration (RecordStore + BlockSequence +
    BlockStore migrations and the embedded-struct → pointer rewrite).
    Follow-up PR.
  • NullMutex flip to `_Get`-only (currently still on `_Create` /
    `_Destroy`). Belongs with the broader Null* family but the buffer
    tests' Mutex lifecycle needs a careful read before flipping.
  • MetaSd / OriginSd NULL-callback validation symmetry. Surfaced
    but explicitly deferred to E12 — not appropriate inside a
    storage-cast sweep PR.

Closes #401 is NOT added — this PR delivers part A only; #401
stays open until part B merges.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Refactor

    • Simplified creation APIs for stream senders and file block devices by removing explicit storage requirements
    • Enhanced robustness with fallback null implementations that prevent crashes from accessing destroyed resources
  • Tests

    • Added comprehensive coverage for use-after-destroy safety and resource pool capacity limits

Review Change Stack

DavidCozens and others added 4 commits May 19, 2026 05:57
Per the E11 canonical 3-TU split. Internal struct moves to
SolidSyslogStreamSenderPrivate.h; vtable + Initialise/Cleanup stay
in SolidSyslogStreamSender.c; pool, public Create/Destroy,
IndexFromHandle, CleanupAtIndex live in SolidSyslogStreamSenderStatic.c.
Pool exhaustion resolves to the shared SolidSyslogNullSender that
shipped with S11.04 — no class-private fallback needed.

Public API: drops the SolidSyslogStreamSenderStorage typedef + the
SOLIDSYSLOG_STREAM_SENDER_SIZE enum from the header; _Create loses
the storage parameter. _Destroy keeps the (struct SolidSyslogSender*)
shape established by S11.04.

Pool size tunable: SOLIDSYSLOG_STREAM_SENDER_POOL_SIZE, default 1.
Error messages: STREAMSENDER_POOL_EXHAUSTED (ERROR severity) and
STREAMSENDER_UNKNOWN_DESTROY (WARNING severity).

Pool test: new TEST_GROUP(SolidSyslogStreamSenderPool) with
FillingPoolThenOverflowReturnsDistinctFallback. Generic per-probe
lock and stale-handle behaviour is covered by
SolidSyslogPoolAllocatorTest.cpp.

NoEndpointConfiguredConnectsToPortZero rewired — the original
implicitly relied on singleton semantics (second Create silently
overwrote the slot). Pool semantics require explicit Destroy then
reassign so the second Create draws a fresh slot.

CreateReturnsHandleInsideCallerSuppliedStorage dropped — it verified
the storage-cast invariant that no longer exists.

Caller updates: Bdd/Targets/{Linux,Windows,FreeRtos}/main.c drop
their `static SolidSyslogStreamSenderStorage` lines; the two
BddTargetTlsSender_OpenSsl_*Tcp.c variants likewise.

MISRA suppressions: deletes the old SelfFromStorage 11.3 entry and
the header 5.7 entry (both refer to constructs that no longer
exist); updates SelfFromBase 11.3 line to :150 and adds the
anonymous-enum 2.4/5.7 entries at the new line. Branch vs main
cppcheck-misra diff: zero new findings, one evaporated (8.9 on
the deleted DEFAULT_INSTANCE / DESTROYED_INSTANCE).

Validated at SOLIDSYSLOG_STREAM_SENDER_POOL_SIZE=3 via
SOLIDSYSLOG_USER_TUNABLES_FILE override — full suite green
(1155 tests). 100% line coverage on both new TUs.

CLAUDE.md audience-table row updated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pool slots previously NULL'd their abstract-base vtable in _Cleanup, so
calling Send/Disconnect/Write/Read/Format through a stale handle after
Destroy was a NULL-fn-pointer dereference. The original pre-E11 pattern
(`*self = DESTROYED_INSTANCE;`) had the same hazard at the vtable site.

Replace the NULL-out with a copy of the relevant null-object vtable
into the slot's abstract base. Use-after-destroy now routes through
the GoF null implementation — Send returns true (drop-on-floor),
Disconnect/Write/Format no-op, Read returns false with bytesRead=0.

Only the abstract base (the public-facing first member of every
derived slot struct) needs the swap. Derived fields (Config,
Connected, LastEndpointVersion, Sender, Ring, ...) are private to
each TU; no public API can reach them, and the next _Initialise
overwrites the whole struct on slot reuse.

Per-class shape:

- StreamSender/UdpSender: Disconnect first (still using the live
  Config.Stream/Datagram), then `*base = *SolidSyslogNullSender_Get()`.
- SwitchingSender: bare swap — does not own inner-sender connections.
- MetaSd/TimeQualitySd/OriginSd: bare swap to NullSd.
- PassthroughBuffer/CircularBuffer: no shared GoF NullBuffer exists,
  so the existing class-private Fallback (lived as `static struct
  SolidSyslogBuffer Fallback` in *Static.c) is exposed as
  `<Class>_Fallback` via the Private.h header (drops `static`, gains
  the Class prefix). _Cleanup copies it into the slot's Base, and
  _Create's pool-exhausted fallback path uses the same symbol.

Tests: one UseAfterDestroyIsCrashSafeViaXxxVtable test per class
exercises Send/Format/Write/Read after Destroy and asserts the
null-object semantics. For groups whose teardown auto-destroys the
slot, the test re-creates via the same Create at the end so teardown
still releases a live slot.

MISRA delta (cppcheck-misra vs main): zero new findings, three
evaporated (rule 8.9 on StreamSender's DEFAULT/DESTROYED_INSTANCE
deletion and on PassthroughBuffer's / CircularBuffer's Fallback losing
its `static` qualifier). Total findings 94 vs main's 98.

Coverage 99.6% — unchanged from commit 1 (the new tests exercise the
null-object paths that were already covered by existing pool-exhaustion
tests; the new Cleanup bodies are shorter).

All gates green: debug (1163 tests), clang-debug (1163), sanitize
(1163), coverage, tidy, cppcheck, cppcheck-misra, clang-format, IWYU.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ckDevice + NullBuffer

Three things land together because they are the same architectural choice:

1. FileBlockDevice migration to the canonical E11 3-TU split.
   - SolidSyslogFileBlockDevice.c: vtable + Initialise/Cleanup.
   - SolidSyslogFileBlockDevicePrivate.h: struct + Initialise/Cleanup decls.
   - SolidSyslogFileBlockDeviceStatic.c: pool + Create/Destroy +
     IndexFromHandle + CleanupAtIndex.
   - Public header: SolidSyslogFileBlockDeviceStorage typedef and
     SOLIDSYSLOG_FILE_BLOCK_DEVICE_STORAGE_SIZE enum deleted; _Create loses
     the storage parameter; _Destroy keeps the (struct *BlockDevice*) shape.
   - Pool size tunable: SOLIDSYSLOG_FILE_BLOCK_DEVICE_POOL_SIZE, default 1.
   - Error messages: FILEBLOCKDEVICE_POOL_EXHAUSTED (ERROR) and
     FILEBLOCKDEVICE_UNKNOWN_DESTROY (WARNING).
   - Pool test: TEST_GROUP(SolidSyslogFileBlockDevicePool) with
     FillingPoolThenOverflowReturnsDistinctFallback.
   - Use-after-destroy test exercises the post-Cleanup vtable.

2. Two new shared GoF nulls — SolidSyslogNullBlockDevice and
   SolidSyslogNullBuffer. Both are public Get-only objects mirroring the
   existing NullSender / NullSd / NullStore family.
   - NullBlockDevice: every method returns false / 0 (disk doesn't exist).
   - NullBuffer: Write swallows; Read returns false with bytesRead=0.

3. PassthroughBuffer / CircularBuffer retrofit. Their class-private
   `Fallback` static (defined in *Static.c, exposed by *Private.h in
   commit 1b) is deleted; their _Cleanup now installs NullBuffer's vtable,
   and their pool-exhausted _Create returns NullBuffer_Get() instead.
   Same retrofit for FileBlockDevice landing fresh — no class-private
   Fallback ever exists; it uses NullBlockDevice from day one.

Why these three land together:
- S11.04 had explicitly deferred the shared NullBuffer because
  "PassthroughBuffer is itself the closest thing to a 'just forward'
  Buffer". Commit 1b changed the calculus: every Cleanup now needs a
  null-object vtable, so a duplicated class-private Fallback per class
  becomes copy-paste pressure. A shared null absorbs that cleanly.
- FileBlockDevice arriving fresh would have introduced the same
  class-private Fallback pattern — and brought one MORE bare-prefixed
  set of Fallback_* statics colliding with the existing two on MISRA
  5.9 (advisory: internal-linkage uniqueness across TUs). Reaching for
  a shared NullBlockDevice avoids the new collision AND lets us
  retrofit the existing two buffer classes in the same touch.

MISRA cppcheck-misra delta vs main: 92 findings (was 98). Five
evaporated (CircularBufferStatic 5.9 + 8.9, PassthroughBufferStatic 5.9
+ 8.9, StreamSender 8.9 from commit 1's DEFAULT/DESTROYED_INSTANCE
deletion). Two new (NullBlockDevice 8.9 + NullBuffer 8.9 — the static
`instance` variable, matching the existing pattern for NullSender /
NullSd / NullStore on main).

Caller updates: drop SolidSyslogFileBlockDeviceStorage locals from
Tests/SolidSyslog{BlockStore,BlockStoreDrainOrdering,BlockStorePosix,
FileBlockDevice}Test.cpp and Bdd/Targets/{Linux,Windows,FreeRtos}/*.
The _Create call sites drop the storage argument.

Coverage 99.6% (2515 of 2525 lines, no uncovered lines in the four new
TUs). Validated at SOLIDSYSLOG_FILE_BLOCK_DEVICE_POOL_SIZE=3 via
SOLIDSYSLOG_USER_TUNABLES_FILE override — full suite green.

All gates green: debug (1178 tests), clang-debug, sanitize, coverage,
tidy, cppcheck, cppcheck-misra, clang-format, IWYU.

CLAUDE.md updates: two new audience-table rows for the shared nulls;
FileBlockDevice / PassthroughBuffer / CircularBuffer rows updated to
reference the new shared fallback target.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captures the three commits (StreamSender + crash-safety retrofit +
FileBlockDevice migration + shared NullBlockDevice / NullBuffer) and
flags what's deferred to part B (the BlockStore composition migration).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@DavidCozens has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 20 minutes and 18 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 09c89c4c-24da-4fc4-9e81-78838f930eca

📥 Commits

Reviewing files that changed from the base of the PR and between 8446f93 and 84f0edf.

📒 Files selected for processing (8)
  • CLAUDE.md
  • Core/Interface/SolidSyslogTunablesDefaults.h
  • Core/Source/SolidSyslogCircularBufferStatic.c
  • Core/Source/SolidSyslogFileBlockDevice.c
  • Core/Source/SolidSyslogPassthroughBufferStatic.c
  • Core/Source/SolidSyslogStreamSender.c
  • DEVLOG.md
  • Tests/SolidSyslogNullBufferTest.cpp
📝 Walkthrough

Walkthrough

This PR migrates SolidSyslogStreamSender and SolidSyslogFileBlockDevice from caller-supplied storage to internal pool allocation, introduces shared null-object singletons (NullBlockDevice, NullBuffer), and retrofits cleanup paths to overwrite base objects with null vtables for crash-safe use-after-destroy behavior.

Changes

Pool Allocator Migration with Null-Object Crash-Safety Retrofits

Layer / File(s) Summary
Public API Contract Changes
Core/Interface/SolidSyslogStreamSender.h, Core/Interface/SolidSyslogFileBlockDevice.h, Core/Interface/SolidSyslogNullBlockDevice.h, Core/Interface/SolidSyslogNullBuffer.h
Removed SolidSyslogStreamSender_Create(storage, config) overload and SolidSyslogStreamSenderStorage type; added config-only Create(config). Removed SolidSyslogFileBlockDevice_Create(storage, file, pathPrefix) and storage type; added Create(file, pathPrefix). Added public SolidSyslogNullBlockDevice_Get() and SolidSyslogNullBuffer_Get() accessor functions to new headers.
Null-Object Singleton Implementations
Core/Source/SolidSyslogNullBlockDevice.c, Core/Source/SolidSyslogNullBuffer.c
Implemented static vtable instances for NullBlockDevice (all block operations return false; Size returns 0) and NullBuffer (Read returns false with 0 bytes; Write discards input). Exported via _Get() functions for reuse as pool-exhaustion fallback and post-destroy safe dispatch.
Pool Allocator Implementations
Core/Source/SolidSyslogStreamSenderStatic.c, Core/Source/SolidSyslogFileBlockDeviceStatic.c
Implemented fixed-size pools (default size 1U) with in-use bitmaps and SolidSyslogPoolAllocator. Create() acquires first free pool slot, initializes instance, and returns null-object fallback on exhaustion. Destroy() locates pool index, conditionally frees if valid/in-use, and logs warning on unknown handle.
Private Structures and Lifecycle
Core/Source/SolidSyslogStreamSenderPrivate.h, Core/Source/SolidSyslogFileBlockDevicePrivate.h, Core/Source/SolidSyslogStreamSender.c, Core/Source/SolidSyslogFileBlockDevice.c
Defined internal struct layouts (with cached state) and non-static _Initialise/_Cleanup functions for pool allocators to invoke. Refactored core implementations to use lifecycle functions instead of public Create/Destroy.
Crash-Safety Vtable Overwriting
Core/Source/SolidSyslogCircularBuffer.c, Core/Source/SolidSyslogPassthroughBuffer.c, Core/Source/SolidSyslogMetaSd.c, Core/Source/SolidSyslogOriginSd.c, Core/Source/SolidSyslogTimeQualitySd.c, Core/Source/SolidSyslogUdpSender.c, Core/Source/SolidSyslogSwitchingSender.c
Updated cleanup/destroy paths to overwrite base abstract-object structs with shared null-object vtables (e.g., *base = *SolidSyslogNullBuffer_Get()) instead of manually nulling function pointers, ensuring stale-handle calls safely dispatch as no-ops.
Pool Size Tunables and Build
Core/Interface/SolidSyslogTunablesDefaults.h, Core/Source/CMakeLists.txt
Added compile-time pool-size tunable macros SOLIDSYSLOG_STREAM_SENDER_POOL_SIZE and SOLIDSYSLOG_FILE_BLOCK_DEVICE_POOL_SIZE (default 1U with lower-bound validation). Added new source files to CMakeLists.txt.
Test Coverage
Tests/CMakeLists.txt, Tests/SolidSyslogNullBlockDeviceTest.cpp, Tests/SolidSyslogNullBufferTest.cpp, Tests/SolidSyslogCircularBufferTest.cpp, Tests/SolidSyslogFileBlockDeviceTest.cpp, Tests/SolidSyslogMetaSdTest.cpp, Tests/SolidSyslogOriginSdTest.cpp, Tests/SolidSyslogPassthroughBufferTest.cpp, Tests/SolidSyslogStreamSenderTest.cpp, Tests/SolidSyslogSwitchingSenderTest.cpp, Tests/SolidSyslogTimeQualitySdTest.cpp, Tests/SolidSyslogUdpSenderTest.cpp
Added unit tests for null-object singletons (verifying no-op behavior and idempotence). Added pool capacity and overflow tests (verify fallback is distinct from pooled slots). Added use-after-destroy regression tests across components verifying stale-handle calls are crash-safe.
BDD Targets and Consumer Updates
Bdd/Targets/Common/BddTargetTlsSender_OpenSsl_PosixTcp.c, Bdd/Targets/Common/BddTargetTlsSender_OpenSsl_WinsockTcp.c, Bdd/Targets/FreeRtos/main.c, Bdd/Targets/Linux/main.c, Bdd/Targets/Windows/BddTargetWindows.c, Tests/SolidSyslogBlockStoreDrainOrderingTest.cpp, Tests/SolidSyslogBlockStorePosixTest.cpp, Tests/SolidSyslogBlockStoreTest.cpp
Removed caller-supplied SolidSyslogStreamSenderStorage and SolidSyslogFileBlockDeviceStorage local variables. Updated all Create() call sites to use new signatures (config-only for senders; file+prefix for block devices).
Documentation and Development Log
CLAUDE.md, DEVLOG.md, misra_suppressions.txt
Updated public-header documentation reference list to reflect new API signatures and null-object fallback semantics. Added detailed development log entry for S11.05 part A covering commits, design decisions, test expectations, and deferrals. Updated MISRA suppression line targets for affected modules.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

  • #401 (S11.05: Sweep — Core storage-cast classes onto PoolAllocator): The changes in this PR directly implement the first phase (part A) of the linked issue's storage-to-pool migration strategy for StreamSender and FileBlockDevice, including removal of public storage types, pool allocator wiring, null-object fallbacks, and crash-safety retrofits.

Possibly related PRs

  • DavidCozens/solid-syslog#187: Introduced caller-supplied storage for SolidSyslogStreamSender_Create, which this PR now removes and replaces with internal pool allocation.
  • DavidCozens/solid-syslog#354: Modified SolidSyslogFileBlockDevice_Create signature and call sites, overlapping with the main PR's API migration and consumer updates.
  • DavidCozens/solid-syslog#394: Introduced pool/fallback patterns for circular buffers that this PR extends to stream senders and file block devices, and now retrofits cleanup across all components to use shared null-object vtables.

Poem

🐰 Pools hold the senders, null-objects stand guard,
Storage-bound storage? Now simply discarded!
Stale handles whisper through vtable-safe air,
No crashes, no chaos—just silence and care.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: migrating public storage-cast classes (StreamSender, FileBlockDevice) onto PoolAllocator and adding shared null objects.
Description check ✅ Passed The PR description is comprehensive, covering purpose, architectural threads, test evidence, MISRA findings, areas affected, and explicit scope delineation for part B.
Linked Issues check ✅ Passed The PR successfully migrates StreamSender and FileBlockDevice to the PoolAllocator 3-TU pattern [#401], removes public Storage typedefs [#401], adds pool-size tunables with compile-time floor enforcement [#401], introduces shared nulls NullBlockDevice and NullBuffer [#401], retrofits classes for crash-safe use-after-destroy via null vtables [#401], updates all callers [#401], adds 23 tests including pool and use-after-destroy coverage [#401], and maintains green gates [#401]. BlockStore composition migration deferred to part B as documented [#401].
Out of Scope Changes check ✅ Passed All changes align with part A scope: two public-class migrations (StreamSender, FileBlockDevice), crash-safety retrofits for eight classes, two new shared nulls, pool tunables, test additions, and caller updates. BlockStore composition migration is explicitly documented as out of scope for part B [#401].

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/s11-05-storage-cast-sweep

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 (✔️ 1182 passed, 🙈 2 skipped)
   🚦   build-freertos-host-tdd: 100% successful (✔️ 1302 passed, 🙈 2 skipped)
   🚦   build-linux-clang: 100% successful (✔️ 1134 passed, 🙈 2 skipped)
   🚦   sanitize-linux-gcc: 100% successful (✔️ 1134 passed, 🙈 2 skipped)
   🚦   integration-linux-openssl: 100% successful (✔️ 9 passed)
   🚦   integration-windows-openssl: 100% successful (✔️ 9 passed)
   🚦   bdd-linux-syslog-ng: 86% successful (❌ 4 failed, ✔️ 42 passed, 🙈 3 skipped)
   🚦   bdd-windows-otel: 82% successful (❌ 4 failed, ✔️ 40 passed, 🙈 5 skipped)
   🚦   bdd-freertos-qemu: 73% successful (✔️ 36 passed, 🙈 13 skipped)
   🚦   build-windows-msvc: 100% successful (✔️ 1035 passed, 🙈 1 skipped)
   🚦   build-linux-tunable-override: 100% successful (✔️ 1134 passed, 🙈 2 skipped)
   ⚠️   Clang-Tidy: 7 warnings (normal: 7)
   ⚠️   CPPCheck: No warnings


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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLAUDE.md`:
- Line 343: Pick one English variant for “synchronization/synchronised” and
apply it consistently across the document; search for all occurrences (e.g., the
phrase in the SolidSyslogCircularBuffer.h description that currently reads
“mutex-injected synchronization” and any instances of “synchronised” elsewhere)
and replace them to the chosen spelling, keeping other terms and symbols
unchanged (references like SOLIDSYSLOG_CIRCULAR_BUFFER_HEADER_BYTES,
SOLIDSYSLOG_CIRCULAR_BUFFER_RING_BYTES, SolidSyslogCircularBuffer_Create,
_Destroy, SolidSyslogNullBuffer should remain exact).

In `@Core/Source/SolidSyslogFileBlockDeviceStatic.c`:
- Around line 43-52: The destroy routine SolidSyslogFileBlockDevice_Destroy
currently warns when freeing a handle that is actually the overflow fallback
returned by SolidSyslogNullBlockDevice_Get(); update the function to detect that
fallback (compare the incoming base to SolidSyslogNullBlockDevice_Get() or
otherwise detect the null/fallback handle) and return without logging an error,
otherwise proceed with the existing index lookup and call to
SolidSyslogPoolAllocator_FreeIfInUse (using FileBlockDevice_IndexFromHandle,
SolidSyslogPoolAllocator_IndexIsValid, SolidSyslogPoolAllocator_FreeIfInUse and
FileBlockDevice_CleanupAtIndex) so valid pool frees still occur and the
SOLIDSYSLOG_ERROR_MSG_FILEBLOCKDEVICE_UNKNOWN_DESTROY warning is only emitted
for truly unknown/unmanaged handles.

In `@Core/Source/SolidSyslogStreamSenderStatic.c`:
- Around line 42-51: SolidSyslogStreamSender_Destroy currently logs a warning
when called with the overflow fallback/null sender returned by Create; update
the function to detect the null/fallback sender (compare the passed base pointer
to SolidSyslogNullSender_Get() or use an equivalent "is null sender" check) and
silently return without logging; keep the existing allocator/index checks
(StreamSender_IndexFromHandle and
SolidSyslogPoolAllocator_IndexIsValid/FreeIfInUse) for normal senders, but skip
the warning path when base is the null/fallback sender.

In `@DEVLOG.md`:
- Around line 33-34: Replace the typo "user-after-destroy" with the correct
technical term "use-after-destroy" in the DEVLOG entry so the sentence reads
"...the use-after-destroy crash-safety gap..." (search for the exact string
"user-after-destroy" in the document to locate and update it).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f764b2fa-d2f9-4e8d-83f9-bf95b087e7f4

📥 Commits

Reviewing files that changed from the base of the PR and between 21f4272 and 8446f93.

📒 Files selected for processing (47)
  • Bdd/Targets/Common/BddTargetTlsSender_OpenSsl_PosixTcp.c
  • Bdd/Targets/Common/BddTargetTlsSender_OpenSsl_WinsockTcp.c
  • Bdd/Targets/FreeRtos/main.c
  • Bdd/Targets/Linux/main.c
  • Bdd/Targets/Windows/BddTargetWindows.c
  • CLAUDE.md
  • Core/Interface/SolidSyslogFileBlockDevice.h
  • Core/Interface/SolidSyslogNullBlockDevice.h
  • Core/Interface/SolidSyslogNullBuffer.h
  • Core/Interface/SolidSyslogStreamSender.h
  • Core/Interface/SolidSyslogTunablesDefaults.h
  • Core/Source/CMakeLists.txt
  • Core/Source/SolidSyslogCircularBuffer.c
  • Core/Source/SolidSyslogCircularBufferStatic.c
  • Core/Source/SolidSyslogErrorMessages.h
  • Core/Source/SolidSyslogFileBlockDevice.c
  • Core/Source/SolidSyslogFileBlockDevicePrivate.h
  • Core/Source/SolidSyslogFileBlockDeviceStatic.c
  • Core/Source/SolidSyslogMetaSd.c
  • Core/Source/SolidSyslogNullBlockDevice.c
  • Core/Source/SolidSyslogNullBuffer.c
  • Core/Source/SolidSyslogOriginSd.c
  • Core/Source/SolidSyslogPassthroughBuffer.c
  • Core/Source/SolidSyslogPassthroughBufferStatic.c
  • Core/Source/SolidSyslogStreamSender.c
  • Core/Source/SolidSyslogStreamSenderPrivate.h
  • Core/Source/SolidSyslogStreamSenderStatic.c
  • Core/Source/SolidSyslogSwitchingSender.c
  • Core/Source/SolidSyslogTimeQualitySd.c
  • Core/Source/SolidSyslogUdpSender.c
  • DEVLOG.md
  • Tests/CMakeLists.txt
  • Tests/SolidSyslogBlockStoreDrainOrderingTest.cpp
  • Tests/SolidSyslogBlockStorePosixTest.cpp
  • Tests/SolidSyslogBlockStoreTest.cpp
  • Tests/SolidSyslogCircularBufferTest.cpp
  • Tests/SolidSyslogFileBlockDeviceTest.cpp
  • Tests/SolidSyslogMetaSdTest.cpp
  • Tests/SolidSyslogNullBlockDeviceTest.cpp
  • Tests/SolidSyslogNullBufferTest.cpp
  • Tests/SolidSyslogOriginSdTest.cpp
  • Tests/SolidSyslogPassthroughBufferTest.cpp
  • Tests/SolidSyslogStreamSenderTest.cpp
  • Tests/SolidSyslogSwitchingSenderTest.cpp
  • Tests/SolidSyslogTimeQualitySdTest.cpp
  • Tests/SolidSyslogUdpSenderTest.cpp
  • misra_suppressions.txt
💤 Files with no reviewable changes (1)
  • Core/Interface/SolidSyslogFileBlockDevice.h

Comment thread CLAUDE.md Outdated
Comment on lines +43 to +52
void SolidSyslogFileBlockDevice_Destroy(struct SolidSyslogBlockDevice* base)
{
size_t index = FileBlockDevice_IndexFromHandle(base);
bool released =
SolidSyslogPoolAllocator_IndexIsValid(&FileBlockDevice_Allocator, index) &&
SolidSyslogPoolAllocator_FreeIfInUse(&FileBlockDevice_Allocator, index, FileBlockDevice_CleanupAtIndex, NULL);
if (!released)
{
SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_WARNING, SOLIDSYSLOG_ERROR_MSG_FILEBLOCKDEVICE_UNKNOWN_DESTROY);
}

@coderabbitai coderabbitai Bot May 19, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid warning on destroy of the overflow fallback block device.

If Create exhausts the pool (Line 29/Line 38), it returns SolidSyslogNullBlockDevice_Get(). Destroying that fallback currently always emits SOLIDSYSLOG_ERROR_MSG_FILEBLOCKDEVICE_UNKNOWN_DESTROY (Line 51), although the lifecycle is valid.

Proposed fix
 void SolidSyslogFileBlockDevice_Destroy(struct SolidSyslogBlockDevice* base)
 {
+    if (base == SolidSyslogNullBlockDevice_Get())
+    {
+        return;
+    }
+
     size_t index = FileBlockDevice_IndexFromHandle(base);
     bool released =
         SolidSyslogPoolAllocator_IndexIsValid(&FileBlockDevice_Allocator, index) &&
         SolidSyslogPoolAllocator_FreeIfInUse(&FileBlockDevice_Allocator, index, FileBlockDevice_CleanupAtIndex, NULL);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Core/Source/SolidSyslogFileBlockDeviceStatic.c` around lines 43 - 52, The
destroy routine SolidSyslogFileBlockDevice_Destroy currently warns when freeing
a handle that is actually the overflow fallback returned by
SolidSyslogNullBlockDevice_Get(); update the function to detect that fallback
(compare the incoming base to SolidSyslogNullBlockDevice_Get() or otherwise
detect the null/fallback handle) and return without logging an error, otherwise
proceed with the existing index lookup and call to
SolidSyslogPoolAllocator_FreeIfInUse (using FileBlockDevice_IndexFromHandle,
SolidSyslogPoolAllocator_IndexIsValid, SolidSyslogPoolAllocator_FreeIfInUse and
FileBlockDevice_CleanupAtIndex) so valid pool frees still occur and the
SOLIDSYSLOG_ERROR_MSG_FILEBLOCKDEVICE_UNKNOWN_DESTROY warning is only emitted
for truly unknown/unmanaged handles.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declined — keeping the warning. The Create-time error already flags a misconfigured pool with SOLIDSYSLOG_ERROR_MSG_FILEBLOCKDEVICE_POOL_EXHAUSTED; a Destroy-time warning on the same bad setup makes the misuse more discoverable, not less. Suppressing it would hide a real signal when an integrator destroys something they didn't actually own. No crash risk either way — destroying the null-object handle is safe; we just won't pretend the call was correct.

Rationale captured in DEVLOG (commit 84f0edf).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DavidCozens, understood — the dual-signal pattern (Create-time POOL_EXHAUSTED + Destroy-time UNKNOWN_DESTROY) deliberately amplifies the misuse signal for integrators with undersized pools. That's a reasonable design choice. I'll stand down on this.

(  ̄ε ̄)


✏️ Learnings added
Learnt from: DavidCozens
Repo: DavidCozens/solid-syslog PR: 402
File: Core/Source/SolidSyslogFileBlockDeviceStatic.c:43-52
Timestamp: 2026-05-19T06:57:12.659Z
Learning: In DavidCozens/solid-syslog, when a pool-backed Create (e.g., SolidSyslogFileBlockDevice_Create, SolidSyslogStreamSender_Create) falls back to a shared null-object on pool exhaustion, the corresponding Destroy function intentionally still emits an UNKNOWN_DESTROY warning when given that null-object handle. This is a deliberate dual-signal design: Create-time POOL_EXHAUSTED + Destroy-time UNKNOWN_DESTROY together make pool misconfiguration maximally discoverable. Do not suggest suppressing the Destroy-time warning for the null-object fallback handle.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: DavidCozens
Repo: DavidCozens/solid-syslog PR: 283
File: Platform/OpenSsl/Source/SolidSyslogTlsStream.c:363-363
Timestamp: 2026-05-07T08:22:59.856Z
Learning: In this solid-syslog codebase, follow the documented contract: do not add NULL/defensive checks inside library/internal code paths (e.g., `_Create` functions) when the NULL case cannot occur under normal internal usage and is covered by framework or internal wiring guarantees. Only add validation at true system boundaries such as user input and external APIs. Where setup-time wiring contracts between the integrator and the library require specific function-pointer fields (e.g., `SolidSyslogTlsStreamConfig.sleep`), assume those required fields are present and intentionally omit NULL guards in internal creation functions.

Learnt from: DavidCozens
Repo: DavidCozens/solid-syslog PR: 291
File: Example/FreeRtos/HelloWorld/CmsdkUart.c:17-42
Timestamp: 2026-05-08T17:19:38.382Z
Learning: In this codebase (DavidCozens/solid-syslog), internal driver/config/access structs may intentionally store raw pointers without NULL checks, `isInitialized` flags, or defensive struct copies. Treat this as an explicit project policy: do not flag store-by-pointer usage or missing NULL/initialization guards for these internal config/access structs when the pointer is provided by production callers and is guaranteed to point to `static const` objects with program lifetime (per CLAUDE.md). Only flag NULL/initialization issues when the pointer originates from potentially invalid/external/untrusted sources or when the code does not establish lifetime/guarantees.

Learnt from: DavidCozens
Repo: DavidCozens/solid-syslog PR: 303
File: Example/Common/ExampleInteractive.h:14-17
Timestamp: 2026-05-09T15:56:13.853Z
Learning: During code review, ensure solid-syslog Tier 1/2 library callbacks in Core/ and Platform/ follow the CLAUDE.md “Callback Conventions”: the callback API must take a `void*` context parameter and the implementation must use the paired configuration field associated with that callback context. Do not apply this requirement to Tier 3 example handlers (e.g., Example/Common/, Example/FreeRtos/, such as `ExampleInteractiveSwitchHandler`/`ExampleInteractiveSetHandler`), which are allowed to use file-static globals per the documented example-tier pattern. If a multi-instance use-case arises, migrate Switch and Set together in a single cohesive change rather than piecemeal updates.

Learnt from: DavidCozens
Repo: DavidCozens/solid-syslog PR: 372
File: Platform/FreeRtos/Source/SolidSyslogFreeRtosDatagram.c:0-0
Timestamp: 2026-05-15T13:09:11.811Z
Learning: When reviewing PRs that bulk-rename identifiers (especially “static function rename” changes) in this repo, check for unintended renames inside FreeRTOS macro usages. A too-broad regex can incorrectly prefix macro tokens used in `static const` initializers (e.g., `pdMS_TO_TICKS` from FreeRTOS). Look for signs like a renamed macro name being incorrectly prefixed (e.g., `FreeRtosDatagram_pdMS_TO_TICKS`), and if present, treat it as evidence the identifier-extraction regex was overly broad and requires correction/revert of the affected rename changes.

Learnt from: DavidCozens
Repo: DavidCozens/solid-syslog PR: 400
File: Core/Source/SolidSyslogOriginSdStatic.c:11-11
Timestamp: 2026-05-18T17:07:19.499Z
Learning: In this repo (DavidCozens/solid-syslog), an IWYU CI gate enforces strict direct-header inclusion: for every symbol (types, enums, functions, etc.) that is directly referenced in a translation unit’s `.c` or `.h` file, include the specific header that declares that symbol. Do not rely on transitive includes from other headers.

Example: if a `.c`/`.h` file uses `SolidSyslogSeverity` enums, it must directly `#include` `SolidSyslogPrival.h` even if another included header (e.g., `SolidSyslogError.h`) pulls it in transitively.

When reviewing, do not flag or recommend removing includes solely because they are “transitive” in this codebase—removing them may still compile locally but can fail the IWYU CI check.

Comment on lines +42 to +51
void SolidSyslogStreamSender_Destroy(struct SolidSyslogSender* base)
{
size_t index = StreamSender_IndexFromHandle(base);
bool released =
SolidSyslogPoolAllocator_IndexIsValid(&StreamSender_Allocator, index) &&
SolidSyslogPoolAllocator_FreeIfInUse(&StreamSender_Allocator, index, StreamSender_CleanupAtIndex, NULL);
if (!released)
{
SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_WARNING, SOLIDSYSLOG_ERROR_MSG_STREAMSENDER_UNKNOWN_DESTROY);
}

@coderabbitai coderabbitai Bot May 19, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid warning on destroy of the overflow fallback sender.

When Create overflows (Line 28/Line 37), it returns SolidSyslogNullSender_Get(). Destroying that valid fallback currently always logs SOLIDSYSLOG_ERROR_MSG_STREAMSENDER_UNKNOWN_DESTROY (Line 50), which is noisy and misleading.

Proposed fix
 void SolidSyslogStreamSender_Destroy(struct SolidSyslogSender* base)
 {
+    if (base == SolidSyslogNullSender_Get())
+    {
+        return;
+    }
+
     size_t index = StreamSender_IndexFromHandle(base);
     bool released =
         SolidSyslogPoolAllocator_IndexIsValid(&StreamSender_Allocator, index) &&
         SolidSyslogPoolAllocator_FreeIfInUse(&StreamSender_Allocator, index, StreamSender_CleanupAtIndex, NULL);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Core/Source/SolidSyslogStreamSenderStatic.c` around lines 42 - 51,
SolidSyslogStreamSender_Destroy currently logs a warning when called with the
overflow fallback/null sender returned by Create; update the function to detect
the null/fallback sender (compare the passed base pointer to
SolidSyslogNullSender_Get() or use an equivalent "is null sender" check) and
silently return without logging; keep the existing allocator/index checks
(StreamSender_IndexFromHandle and
SolidSyslogPoolAllocator_IndexIsValid/FreeIfInUse) for normal senders, but skip
the warning path when base is the null/fallback sender.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declined for the same reason as the FileBlockDevice short-circuit above — the noisy warning on Destroy is correct signal, not noise. A bad-setup Create already emits ERR (SOLIDSYSLOG_ERROR_MSG_STREAMSENDER_POOL_EXHAUSTED); the WARNING on Destroy points at the same misconfiguration from the other side of the lifecycle. The null-object vtable means there's no crash risk — destroying it is safe, we just don't pretend it was a real handle.

Also note: the BDD CI failures on this PR were caused by exactly the silent-fallback path this comment proposes to make even quieter. Bumping SOLIDSYSLOG_STREAM_SENDER_POOL_SIZE default 1→2 in 84f0edf addresses the actual root cause (pool too small for typical TCP+TLS-fallback wiring); keeping the Destroy warning helps surface the next case.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DavidCozens, understood — the Create ERR + Destroy WARNING pair is a deliberate two-sided diagnostic for pool misconfiguration, not redundant noise. The null-object vtable makes the destroy safe, but the warning intentionally surfaces the misconfiguration from the lifecycle's exit side. Makes sense, and good context on the BDD CI failure being the motivator for the SOLIDSYSLOG_STREAM_SENDER_POOL_SIZE default bump rather than silencing the signal. I'll withdraw this suggestion.


✏️ Learnings added
Learnt from: DavidCozens
Repo: DavidCozens/solid-syslog PR: 402
File: Core/Source/SolidSyslogStreamSenderStatic.c:42-51
Timestamp: 2026-05-19T06:57:24.805Z
Learning: In DavidCozens/solid-syslog, the WARNING emitted by `SolidSyslogStreamSender_Destroy` (and `SolidSyslogFileBlockDevice_Destroy`) when called with a handle not issued by the pool (including the null-object fallback returned on pool exhaustion) is intentional and must NOT be short-circuited. The Create ERROR (pool exhaustion) and Destroy WARNING together form a correlated two-sided diagnostic for pool misconfiguration. The null-object vtable ensures crash safety; the warning ensures the misconfiguration is visible from both sides of the lifecycle. Do not suggest silencing either signal.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: DavidCozens
Repo: DavidCozens/solid-syslog PR: 283
File: Platform/OpenSsl/Source/SolidSyslogTlsStream.c:363-363
Timestamp: 2026-05-07T08:22:59.856Z
Learning: In this solid-syslog codebase, follow the documented contract: do not add NULL/defensive checks inside library/internal code paths (e.g., `_Create` functions) when the NULL case cannot occur under normal internal usage and is covered by framework or internal wiring guarantees. Only add validation at true system boundaries such as user input and external APIs. Where setup-time wiring contracts between the integrator and the library require specific function-pointer fields (e.g., `SolidSyslogTlsStreamConfig.sleep`), assume those required fields are present and intentionally omit NULL guards in internal creation functions.

Learnt from: DavidCozens
Repo: DavidCozens/solid-syslog PR: 291
File: Example/FreeRtos/HelloWorld/CmsdkUart.c:17-42
Timestamp: 2026-05-08T17:19:38.382Z
Learning: In this codebase (DavidCozens/solid-syslog), internal driver/config/access structs may intentionally store raw pointers without NULL checks, `isInitialized` flags, or defensive struct copies. Treat this as an explicit project policy: do not flag store-by-pointer usage or missing NULL/initialization guards for these internal config/access structs when the pointer is provided by production callers and is guaranteed to point to `static const` objects with program lifetime (per CLAUDE.md). Only flag NULL/initialization issues when the pointer originates from potentially invalid/external/untrusted sources or when the code does not establish lifetime/guarantees.

Learnt from: DavidCozens
Repo: DavidCozens/solid-syslog PR: 303
File: Example/Common/ExampleInteractive.h:14-17
Timestamp: 2026-05-09T15:56:13.853Z
Learning: During code review, ensure solid-syslog Tier 1/2 library callbacks in Core/ and Platform/ follow the CLAUDE.md “Callback Conventions”: the callback API must take a `void*` context parameter and the implementation must use the paired configuration field associated with that callback context. Do not apply this requirement to Tier 3 example handlers (e.g., Example/Common/, Example/FreeRtos/, such as `ExampleInteractiveSwitchHandler`/`ExampleInteractiveSetHandler`), which are allowed to use file-static globals per the documented example-tier pattern. If a multi-instance use-case arises, migrate Switch and Set together in a single cohesive change rather than piecemeal updates.

Learnt from: DavidCozens
Repo: DavidCozens/solid-syslog PR: 372
File: Platform/FreeRtos/Source/SolidSyslogFreeRtosDatagram.c:0-0
Timestamp: 2026-05-15T13:09:11.811Z
Learning: When reviewing PRs that bulk-rename identifiers (especially “static function rename” changes) in this repo, check for unintended renames inside FreeRTOS macro usages. A too-broad regex can incorrectly prefix macro tokens used in `static const` initializers (e.g., `pdMS_TO_TICKS` from FreeRTOS). Look for signs like a renamed macro name being incorrectly prefixed (e.g., `FreeRtosDatagram_pdMS_TO_TICKS`), and if present, treat it as evidence the identifier-extraction regex was overly broad and requires correction/revert of the affected rename changes.

Learnt from: DavidCozens
Repo: DavidCozens/solid-syslog PR: 400
File: Core/Source/SolidSyslogOriginSdStatic.c:11-11
Timestamp: 2026-05-18T17:07:19.499Z
Learning: In this repo (DavidCozens/solid-syslog), an IWYU CI gate enforces strict direct-header inclusion: for every symbol (types, enums, functions, etc.) that is directly referenced in a translation unit’s `.c` or `.h` file, include the specific header that declares that symbol. Do not rely on transitive includes from other headers.

Example: if a `.c`/`.h` file uses `SolidSyslogSeverity` enums, it must directly `#include` `SolidSyslogPrival.h` even if another included header (e.g., `SolidSyslogError.h`) pulls it in transitively.

When reviewing, do not flag or recommend removing includes solely because they are “transitive” in this codebase—removing them may still compile locally but can fail the IWYU CI check.

Comment thread DEVLOG.md Outdated
Part A's CI surfaced two red gates after push:

- analyze-iwyu: five include adjustments. Two Static.c files were
  pulling in SolidSyslogBufferDefinition.h when only the pointer was
  used (the full definition arrives transitively via the Private.h);
  FileBlockDevice.c carried an unused SolidSyslogMacros.h and was
  missing <stdint.h> for the uint8_t cast in _FormatBlockFilename;
  StreamSender.c was using SolidSyslogStreamSenderConfig without a
  direct include of its public header; NullBufferTest.cpp used size_t
  without <stddef.h>. All fixed; iwyu now clean.

- bdd-linux-syslog-ng / bdd-windows-otel: 4+4 TLS/mTLS scenarios
  silently dropping ("received 0 of 1 messages"). Root cause: the
  Linux and Windows BDD targets each wire two StreamSenders into a
  SwitchingSender (plain TCP at one branch, TLS at another) but
  SOLIDSYSLOG_STREAM_SENDER_POOL_SIZE defaulted to 1 — the second
  Create returned SolidSyslogNullSender_Get() and dropped on Send.
  Bumped the default to 2; the previous tunable comment ("almost
  all integrators wire a single stream-framed sender … as one
  branch of a SwitchingSender") was too optimistic. A
  TCP-with-TLS-fallback wiring is a realistic shape and one extra
  pool slot (~64B/64-bit) is trivial against silent message drop.
  Comment rewritten to match.

Also absorbs two CodeRabbit doc nits — CLAUDE.md BrE consistency on
"synchronise"; DEVLOG typo "user-after-destroy" → "use-after-destroy".

CodeRabbit also proposed two production-code suggestions to silence
the UNKNOWN_DESTROY warning when the caller passes the Create-time
null-object fallback back through _Destroy (FileBlockDevice and
StreamSender). Declined — the warning is correct signal: the
Create-time error already flagged a misconfigured pool, and a
Destroy-time warning on the same bad setup makes the misuse *more*
discoverable. Suppressing it would hide a real signal when the
integrator destroys something they didn't actually own. No crash
risk either way — the null-object vtable is safe to destroy.

DEVLOG updated with rationale on the same.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

☀️   Quality Summary

   🚦   build-linux-gcc: 100% successful (✔️ 1182 passed, 🙈 2 skipped)
   🚦   build-freertos-host-tdd: 100% successful (✔️ 1302 passed, 🙈 2 skipped)
   🚦   build-linux-clang: 100% successful (✔️ 1134 passed, 🙈 2 skipped)
   🚦   sanitize-linux-gcc: 100% successful (✔️ 1134 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 (✔️ 1035 passed, 🙈 1 skipped)
   🚦   build-linux-tunable-override: 100% successful (✔️ 1134 passed, 🙈 2 skipped)
   ⚠️   Clang-Tidy: 7 warnings (normal: 7)
   ⚠️   CPPCheck: No warnings


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

@DavidCozens DavidCozens merged commit 9e1294c into main May 19, 2026
20 checks passed
@DavidCozens DavidCozens deleted the refactor/s11-05-storage-cast-sweep branch May 19, 2026 07:01
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.05: Sweep — Core storage-cast classes onto PoolAllocator

1 participant