Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions DEVLOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,44 @@
# Dev Log

## 2026-05-07 — S12.15 — Service thread idle yield

### Decision

`Example/Common/ExampleServiceThread.c` was busy-spinning at 100% CPU when the
buffer and store were empty. Original story scoped this as a `SolidSyslog_Service`
return-value change so the loop could yield only when truly idle, but the user
simplified: yield 1 ms after **every** tick, unconditionally. The library API
stays untouched; this lives entirely in example code.

The other simplification in the spec was that the original story text suggested
`#ifdef _WIN32` for the platform sleep. We already have `SolidSyslogSleepFunction`
plus POSIX/Windows wirings (added for the TLS handshake retry), so the cleaner
path is to inject the sleep callback into `ExampleServiceThread_Run` and let
each platform main pass `SolidSyslogPosixSleep` / `SolidSyslogWindowsSleep`.
That keeps `Example/Common/` platform-free and makes the test trivially fakeable.

A future story can investigate signalling-based fully-idle wait (eventfd /
SetEvent / RTOS task notifications via the buffer abstraction) — out of scope
here.

### What landed

- `ExampleServiceThread_Run` takes a second arg `SolidSyslogSleepFunction sleep`
and calls it with `IDLE_YIELD_MILLISECONDS = 1` after every `SolidSyslog_Service()`.
- `Example/Threaded/main.c` (Linux) wires `SolidSyslogPosixSleep`.
- `Example/Windows/SolidSyslogWindowsExample.c` wires `SolidSyslogWindowsSleep`.
- `Tests/Example/ExampleServiceThreadTest.cpp` — `SleepFake` mirroring the
existing pattern in `SolidSyslogTlsStreamTest.cpp`; the fake flips the
shutdown flag on first call so the new test
`YieldsOneMillisecondAfterEachServiceTick` can prove the yield happens once
per tick at 1 ms.

### Local validation

Linux gcc / clang / sanitize / coverage (100% lines, 100% functions) /
clang-tidy / cppcheck / clang-format — all green via the devcontainer.
MSVC built clean and `SolidSyslogTests.exe` ran 973/973 on the Windows host.

## 2026-05-06 — S13.20 follow-up — Slice C: structural assertions for discard-policy scenarios

### Decision
Expand Down
8 changes: 7 additions & 1 deletion Example/Common/ExampleServiceThread.c
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
#include "ExampleServiceThread.h"
#include "SolidSyslog.h"

enum
{
IDLE_YIELD_MILLISECONDS = 1
};

// cppcheck-suppress constParameter -- volatile bool written by another thread; const would be incorrect
// NOLINTNEXTLINE(readability-non-const-parameter) -- volatile bool written by another thread; const would be incorrect
void ExampleServiceThread_Run(volatile bool* shutdown)
void ExampleServiceThread_Run(volatile bool* shutdown, SolidSyslogSleepFunction sleep)
{
while (!(*shutdown))
{
SolidSyslog_Service();
sleep(IDLE_YIELD_MILLISECONDS);
}
}
3 changes: 2 additions & 1 deletion Example/Common/ExampleServiceThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
#define EXAMPLESERVICETHREAD_H

#include "ExternC.h"
#include "SolidSyslogSleep.h"

#include <stdbool.h>

EXTERN_C_BEGIN

void ExampleServiceThread_Run(volatile bool* shutdown);
void ExampleServiceThread_Run(volatile bool* shutdown, SolidSyslogSleepFunction sleep);

EXTERN_C_END

Expand Down
3 changes: 2 additions & 1 deletion Example/Threaded/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "SolidSyslogPosixHostname.h"
#include "SolidSyslogPosixMessageQueueBuffer.h"
#include "SolidSyslogPosixProcessId.h"
#include "SolidSyslogPosixSleep.h"
#include "SolidSyslogPosixSysUpTime.h"
#include "SolidSyslogStdAtomicOps.h"
#include "SolidSyslogGetAddrInfoResolver.h"
Expand Down Expand Up @@ -67,7 +68,7 @@ static volatile bool shutdown_flag;
static void* ServiceThreadEntry(void* arg)
{
volatile bool* shutdown = (volatile bool*) arg;
ExampleServiceThread_Run(shutdown);
ExampleServiceThread_Run(shutdown, SolidSyslogPosixSleep);
return NULL;
}

Expand Down
3 changes: 2 additions & 1 deletion Example/Windows/SolidSyslogWindowsExample.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "SolidSyslogWindowsHostname.h"
#include "SolidSyslogWindowsMutex.h"
#include "SolidSyslogWindowsProcessId.h"
#include "SolidSyslogWindowsSleep.h"
#include "SolidSyslogWindowsSysUpTime.h"
#include "SolidSyslogWinsockDatagram.h"
#include "SolidSyslogWinsockResolver.h"
Expand Down Expand Up @@ -83,7 +84,7 @@ static struct SolidSyslogBlockDevice* storeBlockDevice;
// NOLINTNEXTLINE(readability-non-const-parameter) -- _beginthreadex thread-entry signature requires void*
static unsigned __stdcall ServiceThreadEntry(void* arg)
{
ExampleServiceThread_Run((volatile bool*) arg);
ExampleServiceThread_Run((volatile bool*) arg, SolidSyslogWindowsSleep);
return 0;
}

Expand Down
32 changes: 30 additions & 2 deletions Tests/Example/ExampleServiceThreadTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,20 @@
#include "SolidSyslogPrival.h"
#include "CppUTest/TestHarness.h"

static int sleepCallCount;
static int lastSleepMs;
static volatile bool* sleepShutdownFlag;

static void SleepFake(int milliseconds)
{
sleepCallCount++;
lastSleepMs = milliseconds;
if (sleepShutdownFlag != nullptr)
{
*sleepShutdownFlag = true;
}
}

static void ExampleEndpoint(struct SolidSyslogEndpoint* endpoint)
{
SolidSyslogFormatter_BoundedString(endpoint->host, ExampleUdpConfig_GetHost(), SOLIDSYSLOG_MAX_HOST_SIZE);
Expand All @@ -41,7 +55,10 @@ TEST_GROUP(ExampleServiceThread)
SocketFake_Reset();
ClockFake_Reset();
ClockFake_SetTime(1743768600, 0);
shutdown = true;
shutdown = true;
sleepCallCount = 0;
lastSleepMs = 0;
sleepShutdownFlag = nullptr;

SolidSyslogUdpSenderConfig udpConfig = {SolidSyslogGetAddrInfoResolver_Create(), SolidSyslogPosixDatagram_Create(), ExampleEndpoint, ExampleEndpointVersion};
sender = SolidSyslogUdpSender_Create(&udpConfig);
Expand Down Expand Up @@ -73,6 +90,17 @@ TEST_GROUP(ExampleServiceThread)

TEST(ExampleServiceThread, DoesNotSendWhenBufferEmpty)
{
ExampleServiceThread_Run(&shutdown);
ExampleServiceThread_Run(&shutdown, SleepFake);
LONGS_EQUAL(0, SocketFake_SendtoCallCount());
}

TEST(ExampleServiceThread, YieldsOneMillisecondAfterEachServiceTick)
{
shutdown = false;
sleepShutdownFlag = &shutdown;

ExampleServiceThread_Run(&shutdown, SleepFake);

LONGS_EQUAL(1, sleepCallCount);
LONGS_EQUAL(1, lastSleepMs);
}
Loading