Parent epic: #573
Purpose
Change SolidSyslog_Service() from void to returning enum SolidSyslogServiceStatus, so a host can drive an event-driven loop (wake on work, back off on a down sender) instead of a fixed-delay poll. No new OS surface — the integrator owns the wake/sleep mechanism in a thin wrapper around Log/Service.
Contract
enum SolidSyslogServiceStatus:
| State |
Meaning |
Returned when |
Host action |
SOLIDSYSLOG_SERVICE_HALTED |
store wedged, nothing can be processed |
store is halted (existing IsServiceEnabled guard) |
integrator-defined (alarm / slow heartbeat) |
SOLIDSYSLOG_SERVICE_READY |
work is ready now |
this call's buffer drain moved ≥1 record, or the store still has unsent after a successful send |
loop again immediately, no wait |
SOLIDSYSLOG_SERVICE_BLOCKED |
only the sender is stuck |
buffer was idle this call, a send was attempted and failed |
back off (wakeable by a Log signal) |
SOLIDSYSLOG_SERVICE_IDLE |
nothing anywhere |
buffer idle, no send attempted/needed, store has no unsent |
wait for the next Log |
Decision order inside Service (after the halt guard):
drained = DrainBufferIntoStore() -> bool: did any Buffer_Read succeed
sendFailed = SendOneFromStore() -> reports "send attempted and failed"
if drained -> READY (buffer activity dominates a send failure)
if sendFailed -> BLOCKED
if Store_HasUnsent -> READY
else -> IDLE
Key invariant: buffer activity out-ranks send failure — a down sender can never demote the loop out of "keep draining buffer→store" while the buffer has anything to move. BLOCKED is only reached once the buffer is idle and the sole stuck thing is the socket.
Notes
void→enum is source-compatible: existing poll loops that ignore the result compile unchanged. We deliberately do not add warn_unused_result.
Buffer gets no new API — "buffer not idle" is inferred from the drain's read results (the Buffer vtable has only Write/Read).
- Internal:
DrainBufferIntoStore and SendOneFromStore go from void to returning their outcome; Service composes them.
- Contract documented in the
Service() header comment in SolidSyslog.h. No new .md.
- NULL handle →
IDLE (the Error handler already fires).
- Enum constants
SOLIDSYSLOG_SERVICE_* (matches SOLIDSYSLOG_TRANSPORT_* shape).
TDD order (each path tested explicitly)
- Empty buffer + empty store →
IDLE.
- Buffer holds a message; drain moves it →
READY.
- Buffer holds a message and the sender fails → still
READY (dominates BLOCKED).
- Buffer empty, store has unsent, send fails →
BLOCKED.
- Halted store →
HALTED.
Test Evidence
debug preset green; explicit coverage of all four return paths.
Parent epic: #573
Purpose
Change
SolidSyslog_Service()fromvoidto returningenum SolidSyslogServiceStatus, so a host can drive an event-driven loop (wake on work, back off on a down sender) instead of a fixed-delay poll. No new OS surface — the integrator owns the wake/sleep mechanism in a thin wrapper aroundLog/Service.Contract
enum SolidSyslogServiceStatus:SOLIDSYSLOG_SERVICE_HALTEDIsServiceEnabledguard)SOLIDSYSLOG_SERVICE_READYSOLIDSYSLOG_SERVICE_BLOCKEDLogsignal)SOLIDSYSLOG_SERVICE_IDLELogDecision order inside
Service(after the halt guard):Key invariant: buffer activity out-ranks send failure — a down sender can never demote the loop out of "keep draining buffer→store" while the buffer has anything to move.
BLOCKEDis only reached once the buffer is idle and the sole stuck thing is the socket.Notes
void→enumis source-compatible: existing poll loops that ignore the result compile unchanged. We deliberately do not addwarn_unused_result.Buffergets no new API — "buffer not idle" is inferred from the drain's read results (theBuffervtable has onlyWrite/Read).DrainBufferIntoStoreandSendOneFromStorego fromvoidto returning their outcome;Servicecomposes them.Service()header comment inSolidSyslog.h. No new.md.IDLE(theErrorhandler already fires).SOLIDSYSLOG_SERVICE_*(matchesSOLIDSYSLOG_TRANSPORT_*shape).TDD order (each path tested explicitly)
IDLE.READY.READY(dominatesBLOCKED).BLOCKED.HALTED.Test Evidence
debugpreset green; explicit coverage of all four return paths.