You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
S11.09 (#412) closes the per-class migration sweep — every stateful _Create/_Destroy class outside the SolidSyslog core itself now lives on SolidSyslogPoolAllocator. This story retrofits the core: SolidSyslog.c becomes
the canonical 3-TU split with PoolAllocator and a handle-taking API.
Today (Core/Source/SolidSyslog.c):
File-scope static struct SolidSyslog instance initialised from a NilInstance
template at file load, plus a static bool instanceInitialised flag.
_Create(const SolidSyslogConfig*) returns void; double-Create reports SolidSyslog_Error(ERROR, ALREADY_INITIALISED) and ignores.
_Destroy(void) resets the live instance to NilInstance, re-arms the
Nil-collaborator once-only reporter flags.
_Log / _Service reach into the file-static instance directly — there is no
vtable downcast (no SelfFromBase) because SolidSyslog isn't dispatched through a
vtable, it owns the algorithm.
Five private "Nil collaborators" live in the same TU:
NilStore — silent no-ops (functionally identical to SolidSyslogNullStore_Get).
NilBuffer — reports NIL_BUFFER_USED once on first Write, then silent.
NilSender — reports NIL_SENDER_USED once on first Send, then silent.
NilClock / NilStringFunction — TU-local no-op callbacks; no public Null
sibling exists for the function-pointer typedefs.
The retrofit (a) gets SolidSyslog onto the same canonical shape as every other
E11 class, (b) admits multi-instance SolidSyslog by passing a handle on every
runtime call, and (c) collapses the Nil → Null collaborator duplication.
Decisions locked in pre-raise
1. Handle-taking API. Public signatures change to mirror every other pool
class. _Create returns the slot handle; _Destroy, _Log, _Service take it.
Pool default size is 1U; the change preserves the single-instance use case
unchanged for integrators who only ever create one, but admits multi-instance
where it's useful.
The five process-global library functions stay process-global: SolidSyslog_SetErrorHandler, SolidSyslog_Error, SolidSyslog_SetConfigLock, SolidSyslog_LockConfig, SolidSyslog_UnlockConfig. Multi-instance does not
change library-wide error/lock semantics; the ConfigLock-protects-the-Pool
chicken-and-egg forces them global anyway.
2. Three-TU split. Same shape as every other E11 class:
Core/Source/SolidSyslog.c — algorithm + SolidSyslog_Initialise(self, config) + SolidSyslog_Cleanup(self). Public functions _Log / _Service reach through
the passed handle rather than a file-static instance pointer.
Core/Source/SolidSyslogStatic.c — Pool, Allocator, _Create, _Destroy, IndexFromHandle, CleanupAtIndex, and the NilInstance exhaustion-fallback.
3. Pool size tunable — SOLIDSYSLOG_POOL_SIZE, default 1U. Lands in Core/Interface/SolidSyslogTunablesDefaults.h alongside every other class's tunable.
4. Pool exhaustion → NilInstance fallback. No new public Null SolidSyslog
class. The TU-private NilInstance template (in SolidSyslogStatic.c) holds
the same struct shape as a real SolidSyslog instance, with Buffer/Sender/ Store wired to SolidSyslogNullBuffer_Get() / SolidSyslogNullSender_Get() / SolidSyslogNullStore_Get() and the function-pointer fields wired to the
TU-local nil callbacks. On exhaustion, _Create fires SolidSyslog_Error(ERROR, POOL_EXHAUSTED) and returns &NilInstance. Caller code
keeps running — _Log(&NilInstance, msg) and _Service(&NilInstance) execute
through the Null vtables, dropping the message and the service tick silently.
5. _Destroy(unknown_handle) and _Destroy(&NilInstance) → WARNING + ignore.
Standard per the bad-setup contract (feedback_bad_setup_contract). The IndexFromHandle invalid-pointer path covers both cases naturally — &NilInstance
sits outside the Pool[] address range, so IndexFromHandle returns invalid.
6. NULL-handle defensive on _Log / _Service → ERROR + return. Integrator
bug; report and no-op. Same severity as a NULL-config to _Create today.
7. Nil → Null collaborator collapse. Replace the three private Nil
collaborator instances with their public Null siblings inside the NilInstance
template:
NilStore → SolidSyslogNullStore_Get() (identical behaviour today; pure dedup).
NilBuffer → SolidSyslogNullBuffer_Get() (loud-once NIL_BUFFER_USED removed
— _InstallBuffer's Create-time SolidSyslog_Error(ERROR, NULL_BUFFER) is the
authoritative signal).
NilSender → SolidSyslogNullSender_Get() (same as Buffer — loud-once NIL_SENDER_USED removed; Create-time NULL_SENDER is the signal).
Deletes ~120 lines from SolidSyslog.c (the entire Nil-collaborators section)
plus the SOLIDSYSLOG_ERROR_MSG_NIL_BUFFER_USED and SOLIDSYSLOG_ERROR_MSG_NIL_SENDER_USED strings from SolidSyslogErrorMessages.h. NilClock and NilStringFunction stay TU-local — no public Null equivalent
exists for function-pointer typedefs.
8. No new MISRA suppressions. SolidSyslog has no storage cast today (it's
file-static, not caller-supplied-storage), so no D.002 entries evaporate. But
the retrofit must not introduce any new suppressions either — the pattern is
already MISRA-clean per S11.02 / S11.07 / S11.08 precedent.
9. File-scope statics carry the class-prefix shape — SolidSyslog_InUse, SolidSyslog_Pool, SolidSyslog_Allocator, SolidSyslog_NilInstance —
matching every other E11 class.
Sequencing on the work branch
Recommended commit shape on refactor/s11-10-solidsyslog-pool:
refactor: S11.10 collapse Nil collaborators onto public Null siblings
feat!: S11.10 migrate SolidSyslog core onto PoolAllocator with handle API
refactor: S11.10 update BDD targets and tests for handle API
docs: update DEVLOG for S11.10
Squash-merge title: feat!: S11.10 SolidSyslog core onto PoolAllocator (handle API).
The ! marks the breaking change for release-please.
Commit 1 is the Nil→Null cleanup standalone — large diff, mechanical, no API
change. Commit 2 is the architectural change (3-TU split + handle parameter).
Commit 3 is the consumer-side update for the new API. Splitting (2) from (3)
means (2) compiles but doesn't link end-to-end until (3); reviewers see the
core change isolated from the caller blast radius.
Scope
Core/Source/SolidSyslog.c — strip instance file-static, strip Nil
collaborators, public functions become handle-dispatching, introduce _Initialise/_Cleanup helpers.
Bdd/Targets/Common/*.c (every TU that calls _Log or _Service)
All Tests/SolidSyslogTest.cpp tests
New Pool TEST_GROUP — 9 tests mirroring SolidSyslogWindowsMutexPool.
Existing SolidSyslog tests — update for handle API; drop assertions on the
removed loud-once reporters; add NULL-handle defensive coverage on _Log / _Service.
CLAUDE.md audience-table rows for SolidSyslog.h and SolidSyslogConfig.h
updated.
feedback_simple_runtime_api memory entry refreshed (app code calls _Log(handle, msg) and _Service(handle) — still only two runtime
functions, plus the Create-time handle to thread).
Public API changes
/* Before */voidSolidSyslog_Create(conststructSolidSyslogConfig*config);
voidSolidSyslog_Destroy(void);
voidSolidSyslog_Log(conststructSolidSyslogMessage*message);
voidSolidSyslog_Service(void);
/* After */structSolidSyslog*SolidSyslog_Create(conststructSolidSyslogConfig*config);
voidSolidSyslog_Destroy(structSolidSyslog*handle);
voidSolidSyslog_Log(structSolidSyslog*handle, conststructSolidSyslogMessage*message);
voidSolidSyslog_Service(structSolidSyslog*handle);
SOLIDSYSLOG_POOL_SIZE tunable added (default 1U).
MSG_NIL_BUFFER_USED and MSG_NIL_SENDER_USED error-message strings removed.
Acceptance criteria
SolidSyslog core lives on SolidSyslogPoolAllocator with the canonical 3-TU
split. SolidSyslogStatic.c lands at ≤ ~3 file-scope functions + CleanupAtIndex + NilInstance template, mirroring S11.07 / S11.08 baseline.
Public SolidSyslog_Create returns a handle; _Destroy, _Log, _Service
take a handle. The five global library functions (_SetErrorHandler, _Error, _SetConfigLock, _LockConfig, _UnlockConfig) are unchanged.
Pool exhaustion: _Create fires ERROR / POOL_EXHAUSTED and returns &NilInstance. _Log / _Service against &NilInstance are silent no-ops
(Null vtables on Buffer/Sender/Store).
NULL handle to _Log / _Service fires ERROR and returns; no crash.
_Destroy(unknown) and _Destroy(&NilInstance) fire WARNING and return;
no crash, no slot mutation.
The three Nil collaborators (NilStore, NilBuffer, NilSender) are gone
from SolidSyslog.c; the NilInstance template wires those slots to the
public SolidSyslogNull*_Get() siblings. NIL_BUFFER_USED and NIL_SENDER_USED error-message strings are gone. NilClock and NilStringFunction stay TU-local.
Pool TEST_GROUP — 9 tests mirroring SolidSyslogWindowsMutexPool.
All previously existing tests continue to pass under the handle API.
All BDD targets (Linux, Windows, FreeRTOS, Common) and BDD-target tests
compile and run against the handle API.
All host gates green from the gcc devcontainer: debug, sanitize, coverage,
analyze-tidy, analyze-cppcheck, analyze-format. Clang-debug + IWYU via the clang container; Windows MSVC + integration jobs + BDD jobs via CI.
cppcheck-misra produces no new findings vs main.
Coverage at 100% line for SolidSyslog.c, SolidSyslogStatic.c, and any
consumer that gained NULL-handle / fallback paths.
Validation step: re-run the full suite at SOLIDSYSLOG_POOL_SIZE=3 via SOLIDSYSLOG_USER_TUNABLES_FILE to exercise multi-instance.
DEVLOG entry on the work branch BEFORE the PR per feedback_devlog_in_pr,
documenting the migration plus an explicit "breaking change" note for the
release-please changelog.
Out of scope
SolidSyslog_SetErrorHandler, SolidSyslog_SetConfigLock — single global
function-pointer slots, not stateful Created objects. Pattern doesn't apply.
NilInstance naming post-Nil→Null collapse — once its Buffer/Sender/Store
slots wire to public Null siblings, "Nil" is a historical name. Rename to NullInstance for consistency, or keep NilInstance for stability against
git history? Default: keep, decide at review.
SOLIDSYSLOG_ERROR_MSG_POOL_EXHAUSTED wording — every other class fires
the same severity but with class-specific wording. Decide a uniform format
vs class-specific.
Parent epic: #29
Context
S11.09 (#412) closes the per-class migration sweep — every stateful
_Create/_Destroyclass outside the SolidSyslog core itself now lives onSolidSyslogPoolAllocator. This story retrofits the core:SolidSyslog.cbecomesthe canonical 3-TU split with PoolAllocator and a handle-taking API.
Today (
Core/Source/SolidSyslog.c):static struct SolidSyslog instanceinitialised from aNilInstancetemplate at file load, plus a
static bool instanceInitialisedflag._Create(const SolidSyslogConfig*)returnsvoid; double-Create reportsSolidSyslog_Error(ERROR, ALREADY_INITIALISED)and ignores._Destroy(void)resets the liveinstancetoNilInstance, re-arms theNil-collaborator once-only reporter flags.
_Log/_Servicereach into the file-staticinstancedirectly — there is novtable downcast (no
SelfFromBase) because SolidSyslog isn't dispatched through avtable, it owns the algorithm.
NilStore— silent no-ops (functionally identical toSolidSyslogNullStore_Get).NilBuffer— reportsNIL_BUFFER_USEDonce on firstWrite, then silent.NilSender— reportsNIL_SENDER_USEDonce on firstSend, then silent.NilClock/NilStringFunction— TU-local no-op callbacks; no public Nullsibling exists for the function-pointer typedefs.
The retrofit (a) gets SolidSyslog onto the same canonical shape as every other
E11 class, (b) admits multi-instance SolidSyslog by passing a handle on every
runtime call, and (c) collapses the Nil → Null collaborator duplication.
Decisions locked in pre-raise
1. Handle-taking API. Public signatures change to mirror every other pool
class.
_Createreturns the slot handle;_Destroy,_Log,_Servicetake it.Pool default size is
1U; the change preserves the single-instance use caseunchanged for integrators who only ever create one, but admits multi-instance
where it's useful.
_Createvoid _Create(const SolidSyslogConfig*)struct SolidSyslog* _Create(const SolidSyslogConfig*)_Destroyvoid _Destroy(void)void _Destroy(struct SolidSyslog*)_Logvoid _Log(const SolidSyslogMessage*)void _Log(struct SolidSyslog*, const SolidSyslogMessage*)_Servicevoid _Service(void)void _Service(struct SolidSyslog*)The five process-global library functions stay process-global:
SolidSyslog_SetErrorHandler,SolidSyslog_Error,SolidSyslog_SetConfigLock,SolidSyslog_LockConfig,SolidSyslog_UnlockConfig. Multi-instance does notchange library-wide error/lock semantics; the ConfigLock-protects-the-Pool
chicken-and-egg forces them global anyway.
2. Three-TU split. Same shape as every other E11 class:
Core/Source/SolidSyslog.c— algorithm +SolidSyslog_Initialise(self, config)+SolidSyslog_Cleanup(self). Public functions_Log/_Servicereach throughthe passed handle rather than a file-static
instancepointer.Core/Source/SolidSyslogPrivate.h— concretestruct SolidSyslogdefinition +private signatures. TU-internal.
Core/Source/SolidSyslogStatic.c— Pool, Allocator,_Create,_Destroy,IndexFromHandle,CleanupAtIndex, and theNilInstanceexhaustion-fallback.3. Pool size tunable —
SOLIDSYSLOG_POOL_SIZE, default1U. Lands inCore/Interface/SolidSyslogTunablesDefaults.halongside every other class's tunable.4. Pool exhaustion →
NilInstancefallback. No new public Null SolidSyslogclass. The TU-private
NilInstancetemplate (inSolidSyslogStatic.c) holdsthe same struct shape as a real
SolidSysloginstance, withBuffer/Sender/Storewired toSolidSyslogNullBuffer_Get()/SolidSyslogNullSender_Get()/SolidSyslogNullStore_Get()and the function-pointer fields wired to theTU-local nil callbacks. On exhaustion,
_CreatefiresSolidSyslog_Error(ERROR, POOL_EXHAUSTED)and returns&NilInstance. Caller codekeeps running —
_Log(&NilInstance, msg)and_Service(&NilInstance)executethrough the Null vtables, dropping the message and the service tick silently.
5.
_Destroy(unknown_handle)and_Destroy(&NilInstance)→ WARNING + ignore.Standard per the bad-setup contract (
feedback_bad_setup_contract). TheIndexFromHandleinvalid-pointer path covers both cases naturally —&NilInstancesits outside the Pool[] address range, so
IndexFromHandlereturns invalid.6. NULL-handle defensive on
_Log/_Service→ ERROR + return. Integratorbug; report and no-op. Same severity as a NULL-config to
_Createtoday.7. Nil → Null collaborator collapse. Replace the three private Nil
collaborator instances with their public Null siblings inside the
NilInstancetemplate:
NilStore→SolidSyslogNullStore_Get()(identical behaviour today; pure dedup).NilBuffer→SolidSyslogNullBuffer_Get()(loud-onceNIL_BUFFER_USEDremoved—
_InstallBuffer's Create-timeSolidSyslog_Error(ERROR, NULL_BUFFER)is theauthoritative signal).
NilSender→SolidSyslogNullSender_Get()(same as Buffer — loud-onceNIL_SENDER_USEDremoved; Create-timeNULL_SENDERis the signal).Deletes ~120 lines from
SolidSyslog.c(the entire Nil-collaborators section)plus the
SOLIDSYSLOG_ERROR_MSG_NIL_BUFFER_USEDandSOLIDSYSLOG_ERROR_MSG_NIL_SENDER_USEDstrings fromSolidSyslogErrorMessages.h.NilClockandNilStringFunctionstay TU-local — no public Null equivalentexists for function-pointer typedefs.
8. No new MISRA suppressions. SolidSyslog has no storage cast today (it's
file-static, not caller-supplied-storage), so no D.002 entries evaporate. But
the retrofit must not introduce any new suppressions either — the pattern is
already MISRA-clean per S11.02 / S11.07 / S11.08 precedent.
9. File-scope statics carry the class-prefix shape —
SolidSyslog_InUse,SolidSyslog_Pool,SolidSyslog_Allocator,SolidSyslog_NilInstance—matching every other E11 class.
Sequencing on the work branch
Recommended commit shape on
refactor/s11-10-solidsyslog-pool:refactor: S11.10 collapse Nil collaborators onto public Null siblingsfeat!: S11.10 migrate SolidSyslog core onto PoolAllocator with handle APIrefactor: S11.10 update BDD targets and tests for handle APIdocs: update DEVLOG for S11.10Squash-merge title:
feat!: S11.10 SolidSyslog core onto PoolAllocator (handle API).The
!marks the breaking change for release-please.Commit 1 is the Nil→Null cleanup standalone — large diff, mechanical, no API
change. Commit 2 is the architectural change (3-TU split + handle parameter).
Commit 3 is the consumer-side update for the new API. Splitting (2) from (3)
means (2) compiles but doesn't link end-to-end until (3); reviewers see the
core change isolated from the caller blast radius.
Scope
Core/Source/SolidSyslog.c— stripinstancefile-static, strip Nilcollaborators, public functions become handle-dispatching, introduce
_Initialise/_Cleanuphelpers.Core/Source/SolidSyslogPrivate.h— TU-internal struct + private signatures.Core/Source/SolidSyslogStatic.c— Pool, Allocator,_Create,_Destroy,IndexFromHandle,CleanupAtIndex,NilInstanceexhaustion-fallback.Core/Interface/SolidSyslog.h—_Log/_Servicesignatures updated.Core/Interface/SolidSyslogConfig.h—_Create/_Destroysignaturesupdated.
Core/Interface/SolidSyslogTunablesDefaults.h—SOLIDSYSLOG_POOL_SIZEdefault
1U.Core/Source/SolidSyslogErrorMessages.h— addMSG_POOL_EXHAUSTED,MSG_DESTROY_UNKNOWN_HANDLE,MSG_LOG_NULL_HANDLE,MSG_SERVICE_NULL_HANDLE; removeMSG_NIL_BUFFER_USED,MSG_NIL_SENDER_USED.Core/CMakeLists.txt— addSolidSyslogStatic.c.Bdd/Targets/Linux/main.cBdd/Targets/Windows/BddTargetWindows.cBdd/Targets/FreeRtos/main.cBdd/Targets/Common/*.c(every TU that calls_Logor_Service)Tests/SolidSyslogTest.cpptestsSolidSyslogWindowsMutexPool.removed loud-once reporters; add NULL-handle defensive coverage on
_Log/_Service.CLAUDE.mdaudience-table rows forSolidSyslog.handSolidSyslogConfig.hupdated.
feedback_simple_runtime_apimemory entry refreshed (app code calls_Log(handle, msg)and_Service(handle)— still only two runtimefunctions, plus the Create-time handle to thread).
Public API changes
SOLIDSYSLOG_POOL_SIZEtunable added (default1U).MSG_NIL_BUFFER_USEDandMSG_NIL_SENDER_USEDerror-message strings removed.Acceptance criteria
SolidSyslogcore lives onSolidSyslogPoolAllocatorwith the canonical 3-TUsplit.
SolidSyslogStatic.clands at ≤ ~3 file-scope functions +CleanupAtIndex+NilInstancetemplate, mirroring S11.07 / S11.08 baseline.SolidSyslog_Createreturns a handle;_Destroy,_Log,_Servicetake a handle. The five global library functions (
_SetErrorHandler,_Error,_SetConfigLock,_LockConfig,_UnlockConfig) are unchanged._CreatefiresERROR / POOL_EXHAUSTEDand returns&NilInstance._Log/_Serviceagainst&NilInstanceare silent no-ops(Null vtables on Buffer/Sender/Store).
_Log/_ServicefiresERRORand returns; no crash._Destroy(unknown)and_Destroy(&NilInstance)fireWARNINGand return;no crash, no slot mutation.
NilStore,NilBuffer,NilSender) are gonefrom
SolidSyslog.c; theNilInstancetemplate wires those slots to thepublic
SolidSyslogNull*_Get()siblings.NIL_BUFFER_USEDandNIL_SENDER_USEDerror-message strings are gone.NilClockandNilStringFunctionstay TU-local.SolidSyslogWindowsMutexPool.compile and run against the handle API.
gccdevcontainer: debug, sanitize, coverage,analyze-tidy, analyze-cppcheck, analyze-format. Clang-debug + IWYU via the
clangcontainer; Windows MSVC + integration jobs + BDD jobs via CI.SolidSyslog.c,SolidSyslogStatic.c, and anyconsumer that gained NULL-handle / fallback paths.
SOLIDSYSLOG_POOL_SIZE=3viaSOLIDSYSLOG_USER_TUNABLES_FILEto exercise multi-instance.feedback_devlog_in_pr,documenting the migration plus an explicit "breaking change" note for the
release-please changelog.
Out of scope
SolidSyslog_SetErrorHandler,SolidSyslog_SetConfigLock— single globalfunction-pointer slots, not stateful Created objects. Pattern doesn't apply.
SolidSyslogFormatter— transient stack-built object; lifecycle isfundamentally different. Stays unchanged.
Memory pointers for the next session
CLAUDE.md+SKILL.md.MEMORY.md, especially:project_e11_static_pool_designproject_e11_three_tu_splitproject_pool_allocator_helperproject_configlock_injectionfeedback_bad_setup_contract— fallback severity vocabularyfeedback_simple_runtime_api— refresh during this storyfeedback_discuss_production_changesfeedback_tdd_and_review_stylefeedback_no_premature_generalisationfeedback_cppcheck_misra_invariantfeedback_devlog_in_prfeedback_pr_templatefeedback_no_git_commit_dash_sfeedback_no_mergeOpen questions for the next session
NilInstancenaming post-Nil→Null collapse — once its Buffer/Sender/Storeslots wire to public Null siblings, "Nil" is a historical name. Rename to
NullInstancefor consistency, or keepNilInstancefor stability againstgit history? Default: keep, decide at review.
SOLIDSYSLOG_ERROR_MSG_POOL_EXHAUSTEDwording — every other class firesthe same severity but with class-specific wording. Decide a uniform format
vs class-specific.