A single producer single consumer ring buffer keeps a head index written only by the consumer and a tail index written only by the producer. Explain why it needs no lock and no compare and swap, state which loads and stores need acquire or release, and name the two false sharing hazards.
A single producer single consumer ring buffer keeps a head index written only by the consumer and a tail index written only by the producer. Explain why it needs no lock and no compare and swap, state which loads and stores need acquire or release, and name the two false sharing hazards.
Approach: Identify who owns each index, then work out which index each side must read to know how much room or data exists, and what has to be visible before that read is acted upon.
Each index has exactly one writer, so there is no read modify write race and a plain store is enough, which is why no lock and no compare and swap appear; correctness needs only that the data writes are visible before the index that publishes them, giving a release store on tail with an acquire load of tail on the consumer side and the mirror pair on head. The producer writes the slot then does a release store to tail, and a consumer whose acquire load of tail sees the new value is guaranteed to see the slot contents. The consumer reads the slot then does a release store to head, and the producer acquire loads head before overwriting, which stops it clobbering a slot still being read. The two false sharing hazards are head and tail on one cache line, which puts both cores on the same line, and either index sharing a line with the data array. Fix both with alignas(64) and by caching the other side index locally so it is reloaded only when the local view says full or empty.
Follow-up: What has to change the moment a second consumer is added?
Key concepts: single producer single consumer, release store, acquire load, false sharing, cache line.