Zephyr Ring-Buffer Claims: Finish Only the Bytes Actually Produced or Consumed
A ring-buffer claim reserves a contiguous span; it does not promise the requested size and does not publish completed data. In Zephyr 3.7.0 byte mode, the finish call commits a valid prefix of all outstanding claims on that side. Carry the returned lengths through the producer and consumer paths. Passing the original request size to finish can publish unwritten bytes, release unread bytes or produce an error.
This guide covers the upstream 3.7.0 byte-oriented ring_buf claim/finish API. It excludes item mode, multiple simultaneous producers or consumers, DMA and cache-coherency protocols. The executed fixture described below is a sequential host test of the upstream ring algorithm with minimal utility shims. It is not a Zephyr scheduler or MCU test.
Keep request, reservation and completion lengths distinct
Use three conceptual variables: requested bytes, claimed bytes and actually completed bytes. The first is an application wish; the second is the contiguous span the API grants; the third is what the operation really wrote or consumed. The versioned ring-buffer guide explains that the physical end of the backing array can shorten a claim even when total capacity is sufficient.
For one outstanding claim, completion must be between zero and the claimed length. With several consecutive claims before finish, completion is bounded by their sum and refers to the logical prefix across those spans. Do not interpret it as an arbitrary set of completed bytes. If the second span is filled but the first is not, committing their combined length would expose a hole.

What finish changes
The tagged header permits finish counts no larger than the sum of preceding claims, including zero. A put finish publishes that many bytes and returns the surplus reservation to available space. A get finish releases that many consumed bytes and leaves the remainder available for a later read. Neither operation erases the old storage contents.
The implementation rejects a finish count larger than the outstanding reservation with -EINVAL. Treat this as an accounting defect. Do not repeatedly retry the same invalid count or assume the claim automatically vanished. In a controlled error path where no operation still uses the span, finishing zero can release the reservation without publishing or consuming data.
After a successful finish, stop using the old claim pointer. Another operation may reuse that storage. In particular, finishing a get allows the producer to overwrite the released bytes. Passing that pointer to later asynchronous processing requires a separate lifetime solution; the ring has already been told the data is no longer needed.
A concrete wrap example
Consider an eight-byte ring. Put the six bytes ABCDEF, then consume ABCDE. F remains at physical offset five, while the next put position is six. Seven bytes of capacity are free, but a request to claim five returns only two contiguous bytes at offsets six and seven. A subsequent claim of three can return offsets zero through two.
If the producer writes GH in the first span and IJK in the second, then finishes four, only GHIJ is published. K is outside the committed prefix. Reading the available stream returns FGHIJ. The total free count and the first contiguous claim length answer different questions; equating them is the bug this example is designed to expose.
Use a bounded claim transaction
- Begin with no outstanding reservation on that side. Maintain the cumulative claimed length locally.
- Request a span, then use only its returned length. A zero result means no span was granted; do not dereference it as usable storage.
- Perform work on that span and record actual completion. If it is partial, stop before claiming later bytes unless the application can still guarantee a contiguous valid prefix.
- If wrap requires another span, repeat while the operation has remaining work and a bounded progress policy. Never spin indefinitely on a zero-length claim.
- Finish exactly the completed prefix and check the return value. Clear local claim bookkeeping only after the finish outcome is understood.
- Notify the consumer only after publication. For a consumer, release bytes only after their processing or necessary copy has completed.
A parser can deliberately get-claim several spans and finish zero when it needs more input without consuming the inspected prefix. However, it must avoid treating unconsumed bytes as permanently reserved storage. It must also ensure that its maximum lookahead fits the ring, or producer and parser can wait for conditions that cannot coexist.
Executed checks and their limits
An original host fixture compiled the unmodified Zephyr 3.7.0 ring source and header with GCC 14.2.0, C11, optimization enabled and warnings treated as errors. Small local shims supplied standard types, assertions and utility macros; no kernel scheduling functions were simulated. Four groups of assertions passed:
- The eight-byte wrap sequence granted spans of two and three, then returned FGHIJ after finishing four.
- A zero put finish published nothing and restored the reservation’s capacity.
- Claiming 12345 and consuming only two left 345 available.
- Oversized put and get finishes returned
-EINVAL; a subsequent zero finish released those outstanding reservations in the fixture.
These results support sequential byte-accounting behavior only. They do not establish concurrent memory ordering, interrupt safety of an application wrapper, cache visibility or transfer latency. The API itself supplies no general concurrency control. A production qualification must additionally test the actual producer/consumer synchronization and notification scheme on its target.
Keep a compact trace of request, each granted length, actual completion and finish result. When corruption occurs only near buffer boundaries, replay those values before changing buffer size. A larger ring can hide a wrap bug for longer; correct length accounting removes its cause.