Q095FreeFirmware
Build an interrupt-safe sliding-maximum telemetry filter
Question
Complete the supplied C17 fixed-memory sliding maximum. Exactly one non-reentrant ISR owns the deque and performs pushes; one task reads only the atomic published fields. Initialize each object once with the supplied telemetry_max_initialize before any concurrent access, then configure successfully before enabling either caller. Before every reconfiguration, including resetting the generation, the platform must quiesce both the ISR and the task reader and synchronize their restart after configure returns. Only the owning ISR accesses non-atomic deque state. The target probe checks the actual atomic objects for lock freedom; the platform must also certify that these atomic instructions are supported in its ISR context. This is an embedded platform contract, not portable ISO C signal-handler code. A snapshot attempt spans strictly fewer than 2^31 completed pushes between its first and last generation loads; count every intervening publication. This excludes generation ABA while allowing ordinary 32-bit sample-sequence wrap. Caller output objects are writable, mutually disjoint and separate from the filter; they are not concurrently accessed while read is running.
Implementation scaffold
#include <stdatomic.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
typedef struct { int32_t value; uint32_t seq; } tm_node_t;
typedef struct {
tm_node_t queue[64]; // ISR-owned monotonic deque
uint32_t front, count;
uint32_t next_seq, sample_count;
_Atomic uint32_t window;
_Atomic uint32_t pub_gen;
_Atomic bool pub_full;
_Atomic int32_t pub_max;
_Atomic uint32_t pub_seq;
} telemetry_max_ref;
// Supplied one-time C17 initialization, before any caller can access the object.
static void telemetry_max_initialize(telemetry_max_ref *f) {
f->front=0; f->count=0; f->next_seq=0; f->sample_count=0;
atomic_init(&f->window,0); atomic_init(&f->pub_gen,0);
atomic_init(&f->pub_full,false); atomic_init(&f->pub_max,0);
atomic_init(&f->pub_seq,0);
}
// Supplied target probe; the platform must additionally guarantee that these
// lock-free operations and the generated instructions are supported in its ISR.
static bool tm_atomics_lock_free(const telemetry_max_ref *f) {
return atomic_is_lock_free(&f->window) && atomic_is_lock_free(&f->pub_gen) &&
atomic_is_lock_free(&f->pub_full) && atomic_is_lock_free(&f->pub_max) &&
atomic_is_lock_free(&f->pub_seq);
}
bool telemetry_max_configure(telemetry_max_ref *f, uint32_t window) {
// TODO: implement telemetry_max_configure.
}
static uint32_t tm_slot(const telemetry_max_ref *f, uint32_t offset) {
return (f->front + offset) & 63u;
}
void telemetry_max_isr_push(telemetry_max_ref *f, int32_t value) {
// TODO: implement telemetry_max_isr_push.
}
bool telemetry_max_read(const telemetry_max_ref *f,
int32_t *maximum, uint32_t *maximum_seq) {
// TODO: implement telemetry_max_read.
}
Trace one case
Configure window=3. ISR samples at sequences 0,1,2,3 are [4,2,5,5]; call read after each completed push with sentinel outputs unchanged on failure.Read results are unavailable, unavailable, (maximum=5,maximum_seq=2), then (maximum=5,maximum_seq=3).The first two samples do not fill the window. The final equal 5 removes the older 5, so the newest sample owns the reported maximum.
Requirements
- Configure windows 1..64. Reject a null filter, an invalid window or an unsupported atomic target without changing the object. Under the supplied quiescence contract, clear all queue/publication state and publish window last with release ordering. The object becomes warm only after window new samples; every earlier read fails without changing either output.
- Maintain a strictly decreasing candidate deque in fixed 64-node storage. Expire a front when unsigned now-front.seq is at least window. Remove every older back value less than or equal to the incoming value, so ties choose the newest sample. Increment the 32-bit sample sequence modulo 2^32 and saturate the warm-up count at window.
- Publish full, maximum and the sequence belonging to that maximum between an odd generation update and its following even generation update. The supplied reference uses sequentially consistent operations for every publication and reader load; these include the required release/acquire ordering and share one total order. Keep every concurrently read payload field atomic. Do not weaken this multi-field snapshot to a release-only generation around plain or unrelated relaxed payload accesses.
- Try at most three reader snapshots. Each attempt first loads generation and skips odd values; otherwise read all payload fields and the final generation, accepting only equal even generations and full=true. Write the two caller outputs only on success. Null pointers, an unfilled window, all-odd generations or all-changing attempts return false with both outputs unchanged.
