FreeRTOS Queue Reset: Define What Happens to Pending Producers and Stored Payloads
xQueueReset() empties the queue’s stored-item state, but it is not a cancellation request to every task using that queue. In FreeRTOS Kernel V11.1.0 it can unblock a pending sender, which may immediately repopulate the queue. A reset call returning successfully therefore does not prove that no old producer can publish afterward.
This guide examines ordinary queues in upstream V11.1.0, using a single-core preemptive host test for the illustrated ordering. It excludes queue-set coordination, semaphore/mutex internals and a complete shutdown protocol. The specific goal is to distinguish discarded items, blocked send attempts and newly admitted items across reset.
Read the reset as a kernel-state transition
The versioned queue header maps the public reset macro to the existing-queue reset path. In the matching implementation, that path sets the item count to zero, reinitializes read/write positions and unlock-state fields, and examines the waiting-sender list.
If a sender is waiting, one is removed from that wait list because space now exists. Waiting receivers remain blocked at this point because the reset itself supplies no data. They can later unblock through normal data arrival or timeout. Reset does not turn their waits into a universal “operation canceled” result.
| State before reset | Immediate reset effect | Possible later observation |
|---|---|---|
| Queue contains A | Stored-item count becomes zero | A is no longer returned by receive |
| Sender waits to publish B | One waiting sender can become ready | B is admitted when that sender runs |
| Receiver waits on an empty queue | No item is created for it | It still needs data or normal timeout handling |
| Consumer already owns C | No reversal of its earlier receive | C can remain in processing |
The implementation resets queue bookkeeping; it does not walk pointer payloads and call their destructors. It also does not provide a secure byte-erasure guarantee. Do not use reset as evidence that confidential payload bytes were scrubbed from backing storage.

Build the blocked-sender counterexample
- Create a one-item queue and place A in it. Prevent unrelated producers and consumers from acting.
- Run a higher-priority sender that tries to publish B with a blocking wait. Establish that it is blocked on the full queue.
- From the lower-priority controller, reset the queue and record the call boundary.
- Record the sender’s successful return and then inspect the queue through a receive. If B is returned, identify it as the previously pending publication.
- Compare with a control case having no blocked sender. Reset should leave no item unless another producer publishes.
This counterexample was executed locally using unmodified V11.1.0 kernel sources, the official GCC POSIX port and GCC 14.2.0. The test confirmed the sender was blocked before reset. Reset readied that higher-priority sender, which completed its send and repopulated the queue before the controller continued. The following receive returned B.
The result is a functional ordering from that host configuration, not a universal latency claim. Other priorities or preemption settings can change when the sender runs. The underlying review question remains: does the application permit that outstanding send to succeed after reset?
Classify attempts separately from admitted items
A producer blocked in send still owns an unresolved admission attempt. Its item is not yet one of the queue’s stored entries. An inventory that counts only current queue occupancy misses this work, while an inventory that treats every attempted send as accepted overstates what the consumer owns.
For the reset boundary, track four classes: stored items being discarded, pending producer attempts, items already received for processing and publications admitted afterward. Give each attempt a synthetic identifier in tests. Record the API result that changes its ownership state, rather than guessing from the reset timestamp.
An application generation field can identify B as belonging to an obsolete session after it eventually arrives. That supports rejection by the consumer, but rejection must still have a defined release owner. Generation labeling does not prevent a blocked send from succeeding or reclaim its memory automatically.
Decide whether reset is actually the right operation
If the requirement is simply to discard buffered by-value samples while continuing the same producer stream, allowing the pending sender to refill may be exactly right. The acceptance condition is then that retained post-reset data follows the declared freshness rule, not that the queue remains empty.
If the requirement is to cancel an old session, close its new admission path and explicitly reconcile sends already pending. Merely preventing new calls does not resolve calls that are already blocked inside the kernel. Choose bounded send waits or another supported application control design, then wait for their results before declaring the old publication path retired.
If every stored pointer needs reclamation, use a controlled ownership mechanism that can enumerate and dispose of those items, or retain an authoritative external ledger. Resetting first and hoping to recover addresses afterward discards the queue’s references. This supporting ownership concern does not change the separate blocked-sender behavior.
Hypothetical example: a reconnect flush admits an old message
Imagine a hypothetical transport owner that resets its transmit queue when a connection closes. Another task is blocked trying to send a message for that connection. The reset frees space, the blocked send succeeds, and the transport opens a new connection before reading the queue again.
Without a session identity and retirement policy, the old message may be treated as work for the new connection. A targeted repair defines whether that message is rejected, retried under a new request or intentionally retained. The old sender must receive an outcome consistent with the chosen policy; reset alone cannot provide it.
Qualification matrix and stop conditions
Test a full queue with no waiter, one blocked sender, several blocked senders, a blocked receiver, an in-service item and a producer arriving near the reset boundary. Vary the resetting task’s priority in a controlled build. Add pointer-payload accounting only if the real interface uses pointers; keep the basic kernel-state test by value.
At a declared checkpoint, reconcile stored, pending, in-service, rejected and released objects. Stop the test on any unexplained admission, double disposition or assumption that reset returns an always-empty queue. No target hardware, ISR or multi-sender campaign was executed here. Deliver the observed ordering and the application’s admission policy together so a future refactor cannot turn a deliberate flush into an accidental cancellation promise.