FreeRTOS xQueueOverwrite: Make a One-Slot Latest-Value Mailbox Correct
Use xQueueOverwrite() only with a queue created to hold exactly one item. It replaces that slot even when occupied; it is not a general operation that removes the oldest member of a longer queue. Make the length-one restriction part of the mailbox’s construction contract, then test both replacement and consumption order.
This guide targets upstream FreeRTOS Kernel V11.1.0. The executable checks described below used that release’s GCC POSIX port on a Linux host. They establish selected kernel API behavior, not MCU interrupt timing, vendor-port behavior or a performance bound. Queue sizing and general overload-policy selection are outside this article.
Audit the object behind the wrapper
The V11.1.0 queue header defines overwrite as a copy operation on a length-one queue. Its task-context macro passes zero block time to the generic send implementation. There is a separate ISR form. An existing wrapper named “publish latest” is insufficient evidence: follow its handle to the actual creation site.
| Precondition | Evidence to retain | Failure to reject |
|---|---|---|
| Capacity is exactly one | Creation argument and wrapper-owned handle | Reusing an ordinary multi-item command queue |
| Item size matches the snapshot | Declared queue item type and copy size | Passing a pointer where a structure was expected |
| Object exists and remains valid | Creation result and lifetime owner | Publishing before initialization or after deletion |
| Caller uses the appropriate API | Task/ISR call-site inventory | Task API called from an interrupt |
| Producer bytes remain stable during copying | Snapshot assembly and synchronization rule | DMA or another writer modifying the source concurrently |
Prefer a mailbox-specific constructor that fixes capacity to one instead of exposing a configurable queue depth. Keep the handle private to the component if possible. A later “increase all queue depths” maintenance change must not silently invalidate this object’s contract.
Understand the two occupancy transitions
The matching queue implementation asserts the length-one precondition. In its copy path, overwriting an occupied slot compensates the item count so occupancy remains one. An empty slot becomes occupied. A successful receive removes the copied item and makes the slot empty again.
These transitions give a compact diagnostic invariant: between controlled operations, occupancy is zero or one, and repeated overwrites without a receive do not create a backlog. Do not use an independently sampled occupancy value as a lock or a prediction about a later receive. In production, another caller may act between the observation and the operation.

Disabling assertions does not expand the supported API. If an overwrite appears to work on a longer queue in one build, that observation is not permission to use it as drop-oldest. Keep invalid construction out of released code rather than depending on an assertion being present at runtime.
Test histories, not just the final value
- Empty publication: start empty, overwrite with snapshot A, then receive immediately. Require A and an empty queue afterward.
- Unread replacement: publish A and then B without allowing consumption. Require occupancy one, receipt of B and a subsequent empty receive.
- Consumption before replacement: receive A before publishing B. Both values can legitimately be processed because they belong to separate receive operations.
- Consumer already processing: let the consumer receive A and pause its processing. Publish B. Require that the consumer’s copied A remains its own value and that B remains available for a later receive.
- Construction regression: verify the wrapper still creates one slot and uses the exact intended item size. Exercise the invalid-length assertion only in an isolated development test, never as a production recovery technique.
Use gates or a scripted sequence to establish each ordering. Sleeping randomly and hoping the desired race occurs does not tell you which history was tested. For several producers, define “latest” by the serialized publication order; it need not mean the newest sensor timestamp if an older sample can arrive late.
Separate replacement from processing cancellation
An overwrite changes the item still stored in the queue. It cannot revoke a value already copied into the consumer’s local storage. If the requirement is to cancel processing of obsolete work, that needs an application cancellation or generation protocol after receipt. Do not interpret the mailbox operation as such a protocol.
For an owning pointer payload, overwriting can also erase the only queued reference to the previous allocation. The mailbox API does not return the displaced item for reclamation. Prefer a small by-value snapshot for this particular interface, or design a separately reviewed lifetime mechanism. This is a consequence to check, not a substitute for the length-one proof.
A sequence field can make skipped snapshots visible, but assign it coherently with publication and define restart behavior. A gap may be expected replacement; it does not automatically indicate kernel loss. Conversely, a successful publication means the snapshot reached the slot at that instant, not that a consumer processed it.
Keep wakeup evidence distinct from publication counts
Do not require one task wakeup for every overwrite. Several publications can replace the same unread slot before the consumer runs. If this mailbox participates in a queue set, V11.1.0 deliberately does not add another set notification when overwriting an already occupied member. Qualify that integration separately; this article does not replace the set’s selection-and-consumption contract.
For an ISR producer, use the documented ISR overwrite form and the port’s required wake/yield handling. The host checks here did not exercise hardware interrupts, permitted interrupt priorities or DMA coherence. Those remain explicit target qualification cases.
Hypothetical example and checked scope
Imagine a hypothetical status display receiving a fixed-size operating snapshot. A developer increases its queue depth from one to four while leaving overwrite calls unchanged. The correct repair starts with the object’s contract: restore a dedicated one-slot mailbox if only the newest unread snapshot is required, or select a different, explicitly implemented multi-item policy.
A local C harness compiled the unmodified V11.1.0 kernel sources and POSIX port with GCC 14.2.0. It checked empty publication, occupied replacement with unchanged occupancy, receipt of the newest value and refill after consumption. Those assertions passed. The processing-overlap, invalid-length and MCU ISR cases above remain proposed tests. Acceptance requires both the construction audit and the histories relevant to the product; no throughput or scheduling guarantee follows from these functional checks.