Circular buffer full and empty states 2026-10-18
With front-plus-count storage, empty means and full means ; every allocated slot is usable. With only the two wrapped array indices , the equality describes both possibilities. Indeed , and the two allowed counts have the same remainder. Recovering the count as therefore loses the full state. A count, a separate full flag, or a different index representation must resolve that ambiguity.
Another convention reserves one slot: for a physical array of length , empty means and full means . Its usable capacity is , and now recovers its count because . A five-slot array then stores at most four items.
This is the convention of the kernel's documented
CIRC_* helpers, which additionally require power-of-two array lengths. It is not a universal property of circular buffers; our capacity-five count-based example uses all five slots and does not use those helpers.[ref]Circular Buffers by David Howells and Paul E. McKenney. David Howells and Paul E. McKenney, Circular Buffers, What is a circular buffer?; Measuring power-of-2 buffers: full/empty convention and helper result range
Circular buffer indices Created 2026-10-18 Updated 2026-10-24
Let the capacity be a positive integer , and allocate exactly array slots . Store the front array index and count . The representation invariant isThe logical sequence and next-write array index areHere denotes the nonnegative remainder from modular arithmetic. The count determines which slots are live: leftover bits in other slots are not members of the FIFO queue. If , has no current front item; it identifies the position where the next successful insertion begins. If , points at a live item, so writing there requires an explicit overwrite policy.
For ,
enqueue(x) writes , then increments , leaving fixed. For , dequeue() saves and returns , then sets and . These are sequential updates, completed before the next call. A rejecting interface tests or first and returns full or empty, respectively, without changing state. peek returns only when ; size returns .One can instead store and , recovering . On insertion, advance and increment ; on removal, keep fixed and decrement . Thus the next-write array index is not necessarily the front array index.
Names such as
head and tail require checking the actual API: the kernel documentation calls the insertion position head and the removal position tail.[ref]Circular Buffers by David Howells and Paul E. McKenney. David Howells and Paul E. McKenney, Circular Buffers, What is a circular buffer?: head is insertion, tail is removal
The diagram is the state after step 8 of circular buffer operation trace. Reading the physical array from slot zero would give , which includes a removed label and puts first. Reading from for exactly items gives . The next insertion uses slot , not slot . This distinction also explains why the live sequence may need two contiguous pieces of the array: here slots through , followed by slot . No items need to move to make this wrap work.
Circular buffer operation costs 2026-10-18
With storage allocated in advance, each successful insertion, removal,
peek, size, and full/empty check uses worst-case time complexity in Big O notation. This assumes constant-cost indexed array access, bounded machine word index arithmetic and fixed-cost scalar item reads and writes. Each operation touches a bounded number of slots and fields regardless of and ; no shifting, scanning or resizing occurs. Rejected operations also take constant work. Producing a copy of all live items takes work under the same scalar-cost assumptions.The preallocated array occupies scalar slots plus constant bookkeeping. Allocation and any initial slot construction are separate setup costs. Copying a large item, running an expensive destructor or callback, allocating an item, or growing a dynamic array changes the operation cost and must be charged separately. The bound is worst-case structural work, with no need for amortized analysis, and says nothing about a measured wall-clock deadline.
These formulas describe sequential calls. Wrapping array indices implies neither atomic operations nor safe access from multiple software threads. Publication of an item's contents and reuse of its slot require a protocol justified under the relevant memory model, including any necessary acquire-release synchronization. The kernel documentation discusses barriers under explicit producer/consumer restrictions; Boost's documentation separately requires mutual exclusion for shared access involving writes. Neither property follows merely from choosing a circular buffer.[ref]
Circular Buffers by David Howells and Paul E. McKenney. David Howells and Paul E. McKenney, Circular Buffers, Using memory barriers with circular buffers; The producer; The Consumer: explicit synchronization and ordering requirements
[ref]Implementation. Jan Gaspar, Boost.Circular Buffer, Implementation, Thread-Safety: shared access involving a writer requires mutual exclusion
David Howells Created 2026-10-18 Updated 2026-11-05
David Howells is a software engineer who has contributed technical documentation for the Linux kernel. With Paul E. McKenney, he authored a guide to circular buffers, including the interaction between index arithmetic and synchronization.[ref]
Circular Buffers by David Howells and Paul E. McKenney. Author byline and opening summary
Their example separates the producer's insertion index from the consumer's removal index. With one producer and one consumer, memory barriers publish initialized data before an index advertises it as available. This illustrates why a correct data structure needs both an indexing convention and an account of what concurrent operations can observe.[ref]
Circular Buffers by David Howells and Paul E. McKenney. Using memory barriers with circular buffers, The producer and The Consumer
Paul E. McKenney Created 2026-10-18 Updated 2026-11-05
Paul E. McKenney is a computer scientist known for work on concurrent computing and operating-system kernels. His contributions include read-copy-update, developed for systems in which reads are frequent and updates must coexist with them.[ref]
The key read-copy-update distinction is between removing an object from a shared data structure and reclaiming its storage. An earlier reader may still hold a reference after removal; reclamation waits until the relevant readers have finished. That separation permits lighter synchronization on the read path. It is an example of how the lifetime of a shared object becomes part of a concurrent algorithm.[ref]
What is RCU? in the Linux Kernel documentation. Section 1, RCU Overview
With David Howells, he also authored the kernel guide to circular buffers and their memory barriers.[ref]
