October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk3 min

The Empty Check Passed on a Full Ring: A C++ Ring-Buffer Bug Explained

When a ring buffer compares cursor positions only modulo capacity, an exact-capacity fill can look empty. Here’s the state-aliasing bug and a practical sequential test.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A ring buffer can report “empty” when it is actually full if its empty check compares only the read and write cursors modulo the buffer capacity. In Morgan Ma’s example, four pushes into a four-slot ring make both cursor residues zero, even though all four slots are occupied. The underlying problem is that modulo arithmetic discards how many times a cursor has gone around the ring.

How a full ring can look empty

Ma’s example uses monotonically increasing read and write cursors, named r and w, for a four-slot buffer. The physical slot for a cursor is selected with its value modulo the capacity. If the empty check compares those residues, then it treats equal positions as proof that no data is stored.

After four pushes and no pops, the raw cursors are conceptually r = 0 and w = 4. Their positions within the ring are both zero: r % 4 == w % 4. The comparison says “empty,” but the difference between the cursors says four items have been written and none read. In the article’s illustrative program, the output after the four pushes is empty=true and popped=0.

This is state aliasing: mapping cursors to ring positions loses the lap count. Equal modulo positions can represent either zero items or a whole number of capacities of items. A residue-only comparison cannot distinguish those states.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What state distinguishes empty from full?

For the sequential design described by Ma, the useful occupancy invariant is w - r. If the read cursor never advances beyond the write cursor, that difference represents the number of occupied elements. The empty and full checks can then be expressed separately:

  • empty() when w - r == 0.
  • full() when w - r == capacity.

The article’s second sketch uses std::size_t cursors and a vector, calculates occupied() as w - r, and refuses a push when the ring is full. It is an illustrative approach for the example, not a universal production fix: cursor wrap and concurrency still require deliberate design.

How to reproduce and diagnose the boundary error

Ma recommends first testing the ring sequentially with a deliberately small capacity. A four- or eight-slot ring makes the collision easy to reach and inspect.

  1. Set the capacity to four or eight slots.
  2. Test pushes at capacity - 1, capacity, and capacity + 1. Define what should happen at each boundary, especially whether a push at capacity must be rejected.
  3. Record the raw read and write cursors after each operation, along with their residues modulo capacity.
  4. Compare w - r with the number of elements visibly stored. Check that the occupancy remains between zero and capacity under the intended push and pop behavior.
  5. Only after the sequential behavior has a reliable oracle, add threads and investigate concurrency separately.

The useful diagnostic is not just “the ring says empty.” Inspect the raw cursor values and their residues at the failure point: equal residues show the matching physical position, while the raw cursor difference reveals whether the ring has made a complete lap.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why memory-safety tools may not catch this

The article characterizes this failure as an invariant or logic error, not necessarily an invalid memory access. A program can access valid slots and still make the wrong decision about whether data exists. Passing a sanitizer run therefore does not, by itself, prove that the ring’s full/empty protocol is correct.

Ma recommends treating race detection as a later, separate step: a thread race is a different failure mode from the sequential boundary collision. The article names AddressSanitizer, UndefinedBehaviorSanitizer, ThreadSanitizer, GDB, and compiler commands in its workflow, but the central check for this bug is first to compare the implementation’s empty/full decisions with a sequential occupancy oracle.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Limits of the example

Ma presents proposed test examples rather than a production incident dump, and the example does not establish a concurrency solution or prove wait-free behavior. The article also notes that finite-width cursors can wrap during long runs; its occupancy subtraction assumes the read cursor does not outrun the write cursor. Those properties need to be addressed for the specific implementation rather than inferred from the four-slot demonstration.

The article discloses that it was prepared as part of MonkeyCode product outreach. Ma says free model access and a free server option were used to draft boundary tests and compile throwaway variants, while candidate outputs were compiled locally. The article cautions that a remote compile is not a sanitizer run. That disclosure is context for the example, not independent validation of a product or a recommendation to use one.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value

Source

Morgan Ma, “The Empty Check Passed on a Full Ring”, DEV Community, posted September 20 (the year is not established in the available article text).

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.