Skip to content

Question: Why is cursor.setVolatile(nextValue) (StoreLoad fence) needed in SingleProducerSequencer? #505

@neoremind

Description

@neoremind

Context

In SingleProducerSequencer, there's a specific pattern where cursor.setVolatile(nextValue) is called with a comment indicating it provides a StoreLoad fence:

if (wrapPoint > cachedGatingSequence || cachedGatingSequence > nextValue)
{
    cursor.setVolatile(nextValue);  // StoreLoad fence

    long minSequence = Util.getMinimumSequence(gatingSequences, nextValue);
    this.cachedValue = minSequence;

    if (wrapPoint > minSequence)
    {
        return false;
    }
}

This occurs in both next() and hasAvailableCapacity() methods.

Question

  1. Why is the StoreLoad fence specifically needed here? What memory ordering problem does it solve?

Because getMinimumSequence() already ensures fresh reads.

// In Util.getMinimumSequence():
public static long getMinimumSequence(final Sequence[] sequences, final long minimum) {
    for (int i = 0, n = sequences.length; i < n; i++) {
        long value = sequences[i].get();  // ← Each get() has acquire fence!
        minimumSequence = Math.min(minimumSequence, value);
    }
    return minimumSequence;
}

The operations in the if clause are logically independent. The producer's cursor update and the subsequent consumer sequence reads serve different purposes and don't have obvious ordering dependency.

  1. Is the operation necessary? The setVolatile with StoreLoad fence provides a full memory barrier to ensure ordering and visibility of the producer's cursor sequence in the next method. However, this early cursor update occurs before the producer has published the sequence - consumers don't need to see this intermediate cursor value yet. I understand this logic got triggered rarely only when ring buffer goes one full iteration because of "snapshot"ed gating consumer sequence, it needs to update the tracking of the slowest consumer's sequence in order to unblock advancing producer's cursor, but the nuance here is what caught me attention and led me think deeper.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions