Files
ppsspp/libretro/libretro-common/include/retro_spsc.h
T
libretroadmin cf9d102864 libretro: vendor libretro-common from RetroArch in-tree
The libretro-common submodule pinned the standalone repository at
76a3d54. It is replaced by an in-tree copy of RetroArch master
(b655a20) holding only what the core uses: the VFS, file, string,
encoding, compat and time sources it already built, the headers they and
the core include, the threading set - rthreads, retro_atomic, retro_spsc
and retro_waitable_spsc, mpsc_stack, retro_triple_buffer,
retro_eventcount, retro_asym_eventcount, retro_procbarrier and tpool,
with features_cpu and rstrtod, which they need - and the codecs that
replace the bundled zlib, zstd, libchdr, libzip and libpng: rinflate and
rdeflate, rzstd, rchd with its LZMA and FLAC decoders, rzip_archive,
rpng and crc32.

stdstring.c, which nothing calls, is no longer built. The threading sources are always built, by both the static Makefile and
CMake; the VFS set stays behind HAVE_LIBRETRO_VFS as before. The codecs
are built by the change that switches the core over to them.
2026-10-08 14:14:37 +02:00

395 lines
15 KiB
C

/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (retro_spsc.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_SPSC_H
#define __LIBRETRO_SDK_SPSC_H
/*
* retro_spsc.h - portable single-producer / single-consumer byte queue
*
* Lock-free byte-stream queue with one writer thread and one reader
* thread. Uses release-store / acquire-load on two cursors (the
* Lamport / Disruptor design) rather than a single shared count, so
* neither thread issues atomic RMW operations on the hot path.
*
* Constraints:
* - Exactly ONE producer and ONE consumer. No locking is performed;
* two producers or two consumers will corrupt the queue.
* - Capacity must be power-of-2. Init rounds up.
* - Maximum capacity is SIZE_MAX/2 (so head - tail never overflows
* the well-defined unsigned modular range).
* - The buffer is byte-addressed. Callers pushing structured records
* are responsible for framing.
*
* Memory model:
* - Producer writes data into the buffer (non-atomic stores), then
* publishes the new head via release-store.
* - Consumer acquire-loads head (sees data writes that preceded the
* release), reads data (non-atomic loads), then publishes the new
* tail via release-store.
* - Producer acquire-loads tail to know how much space is free.
* - This pairing gives the SPSC invariant on every backend
* retro_atomic.h supports lock-freely. On the volatile fallback
* (no real backend), correctness reduces to single-core or x86 TSO,
* same as every other retro_atomic.h caller.
*
* Cache behaviour:
* - head and tail are placed on separate cache lines via explicit
* padding to RETRO_SPSC_CACHE_LINE. Without this, the producer's
* publish would invalidate the consumer's tail line and vice versa,
* halving throughput on contended SMP. The padding is a
* performance hint; correctness does not depend on it.
* - The padding runs past tail as well, so the isolation survives
* embedding retro_spsc_t by value in a larger struct. Without a
* trailing pad, whatever the embedder declares next lands within
* tail's line and the consumer's publish starts invalidating it -
* which defeats the point for exactly the callers this primitive
* exists for.
*
* Lifetime:
* - retro_spsc_init allocates an internal buffer; retro_spsc_free
* releases it. The caller owns the retro_spsc_t struct itself.
* - The struct is NOT itself thread-safe to construct or destroy
* while in use. Build it on one thread, hand the producer pointer
* to one thread and the consumer pointer to another, then tear
* down on one thread after both producer and consumer have stopped.
*
* Example:
*
* retro_spsc_t q;
* retro_spsc_init(&q, 4096);
*
* // Producer thread:
* uint8_t msg[64] = ...;
* if (retro_spsc_write_avail(&q) >= sizeof(msg))
* retro_spsc_write(&q, msg, sizeof(msg));
*
* // Consumer thread:
* uint8_t msg[64];
* if (retro_spsc_read_avail(&q) >= sizeof(msg))
* retro_spsc_read(&q, msg, sizeof(msg));
*
* retro_spsc_free(&q);
*
* Comparison with libretro-common's existing fifo_queue_t:
* - fifo_queue uses a slock_t internally; safe with multiple producers
* and multiple consumers, but every push/pop takes a mutex.
* - retro_spsc is lock-free but limited to one producer and one
* consumer. Use this when (a) the producer/consumer count is fixed
* at one each (audio driver -> backend, video -> task system, etc.)
* and (b) the lock contention in fifo_queue measurably matters.
* - For most code paths, fifo_queue is the better default. This
* primitive is for hot paths where lock-free is a measured win.
*/
#include <stddef.h>
#include <stdint.h>
#include <boolean.h>
#include <retro_common_api.h>
#include <retro_atomic.h>
RETRO_BEGIN_DECLS
/* Cache-line padding size. 64 covers x86-64, AArch64, most modern
* ARMs, and modern PowerPC. Apple Silicon's effective coherency line
* is 128 due to its M-series cluster topology; over-padding to 64
* still avoids false sharing, just slightly less efficiently. Older
* 32-bit ARMs (ARMv6, Cortex-A8) used 32-byte lines but tolerate the
* larger pad without correctness impact. */
#ifndef RETRO_SPSC_CACHE_LINE
#define RETRO_SPSC_CACHE_LINE 64
#endif
/* Padding helper. C89 has no _Static_assert; the array-size trick is
* portable. We pad to the next multiple of cache-line size after the
* pointer-and-size_t prefix and after each cursor. */
/* Effective cache line for padding. We pad each cursor to a full
* cache line. If the pre-cursor fields exceed RETRO_SPSC_CACHE_LINE
* on some hypothetical small-cache target, the underflow on the
* subtraction would produce a giant array; guard with a max() so
* the pad is always at least 1 byte and never underflows. */
#define RETRO_SPSC_PAD0_BYTES \
((RETRO_SPSC_CACHE_LINE > (sizeof(uint8_t*) + sizeof(size_t))) \
? (RETRO_SPSC_CACHE_LINE - (sizeof(uint8_t*) + sizeof(size_t))) \
: 1)
/* Each cursor shares its line with that side's private copy of the
* other cursor (see cached_tail / cached_head below), so the pad is
* a line less both. */
#define RETRO_SPSC_PAD1_BYTES \
((RETRO_SPSC_CACHE_LINE > sizeof(retro_atomic_size_t) + sizeof(size_t)) \
? (RETRO_SPSC_CACHE_LINE - sizeof(retro_atomic_size_t) - sizeof(size_t)) \
: 1)
typedef struct retro_spsc
{
uint8_t *buffer;
size_t capacity; /* power of 2; mask = capacity - 1 */
/* Pad so head sits on a fresh cache line, isolating it from
* the buffer/capacity fields that init may touch. */
uint8_t _pad0[RETRO_SPSC_PAD0_BYTES];
retro_atomic_size_t head; /* producer publishes; consumer reads */
/* Producer-private copy of tail. The producer computes room from
* this and only re-reads the real tail - the consumer's cache line
* - when the copy says there is not enough, i.e. once per fill
* rather than once per write. Always <= tail, so room computed
* from it is never overstated. Plain size_t: only the producer
* ever touches it. */
size_t cached_tail;
/* Pad so tail sits on its own cache line, isolating it from head. */
uint8_t _pad1[RETRO_SPSC_PAD1_BYTES];
retro_atomic_size_t tail; /* consumer publishes; producer reads */
/* Consumer-private copy of head, the mirror of cached_tail: always
* <= head, re-read only when it says the ring is short. */
size_t cached_head;
/* Pad past tail so an embedding struct's own fields cannot land on
* tail's line. Same width as _pad1. */
uint8_t _pad2[RETRO_SPSC_PAD1_BYTES];
} retro_spsc_t;
/**
* retro_spsc_init:
* @q : The queue.
* @min_capacity : Requested capacity in bytes. Rounded up to the
* next power of 2. Must be > 0 and <= SIZE_MAX/2.
*
* Allocates the internal buffer and zero-initialises both cursors.
* Both cursors begin at 0; the queue is empty.
*
* Returns: true on success, false on allocation failure or invalid
* @min_capacity.
*
* After this returns true, the producer thread can call
* retro_spsc_write / retro_spsc_write_avail and the consumer thread can
* call retro_spsc_read / retro_spsc_read_avail.
*/
bool retro_spsc_init(retro_spsc_t *q, size_t min_capacity);
/**
* retro_spsc_free:
* @q : The queue.
*
* Releases the internal buffer. Caller must ensure both producer
* and consumer have stopped using @q before calling. Safe to call
* on a queue that retro_spsc_init failed on (no-op).
*/
void retro_spsc_free(retro_spsc_t *q);
/**
* retro_spsc_clear:
* @q : The queue.
*
* Resets head and tail to 0, discarding any unread data. The
* underlying buffer is preserved (no reallocation).
*
* SAFETY: callable only when both the producer and consumer are
* quiesced -- e.g. before either has started, or after both have
* stopped. Concurrent calls with a live producer or consumer are
* a data race. This is the same lifetime constraint as
* retro_spsc_init / retro_spsc_free.
*
* Typical use is when a stream is stopped and restarted (e.g. an
* audio driver pausing and resuming, or switching device formats),
* and stale buffered data should be discarded before the new
* stream begins.
*/
void retro_spsc_clear(retro_spsc_t *q);
/**
* retro_spsc_write_avail:
* @q : The queue.
*
* Producer-side query: how many bytes can be written before the
* queue is full.
*
* Returns: byte count, in [0, capacity].
*
* SAFETY: callable only from the producer thread.
*/
size_t retro_spsc_write_avail(const retro_spsc_t *q);
/**
* retro_spsc_read_avail:
* @q : The queue.
*
* Consumer-side query: how many bytes are available to read.
*
* Returns: byte count, in [0, capacity].
*
* SAFETY: callable only from the consumer thread.
*/
size_t retro_spsc_read_avail(const retro_spsc_t *q);
/**
* retro_spsc_write:
* @q : The queue.
* @data : Source bytes.
* @bytes : Number of bytes to attempt to write.
*
* Writes up to @bytes from @data into the queue. If the queue has
* less than @bytes free, writes only what fits.
*
* Returns: number of bytes actually written.
*
* SAFETY: callable only from the producer thread. Concurrent calls
* with another producer corrupt the queue.
*/
size_t retro_spsc_write(retro_spsc_t *q, const void *data, size_t bytes);
/* Producer-only whole-frame write; returns frames, not bytes. Free bytes
* shorter than one frame remain unused. The byte queue capacity need not be
* divisible by frame_bytes, and frames may wrap across its physical end.
* Zero frames or frame_bytes writes nothing. As with write(), q must be
* initialized and data must cover the frames that can be accepted.
* Consumers must read/skip whole frames to preserve stream boundaries. */
size_t retro_spsc_write_frames(retro_spsc_t *q, const void *data,
size_t frames, size_t frame_bytes);
/**
* retro_spsc_read:
* @q : The queue.
* @data : Destination bytes.
* @bytes : Number of bytes to attempt to read.
*
* Reads up to @bytes from the queue into @data. If the queue has
* less than @bytes available, reads only what is present.
*
* Returns: number of bytes actually read.
*
* SAFETY: callable only from the consumer thread. Concurrent calls
* with another consumer corrupt the queue.
*/
size_t retro_spsc_read(retro_spsc_t *q, void *data, size_t bytes);
/**
* retro_spsc_peek:
* @q : The queue.
* @data : Destination bytes.
* @bytes : Number of bytes to peek.
*
* Like retro_spsc_read but does not advance the read cursor. The
* peeked bytes remain available for the next read.
*
* Returns: number of bytes peeked.
*
* SAFETY: callable only from the consumer thread.
*/
size_t retro_spsc_peek(const retro_spsc_t *q, void *data, size_t bytes);
/**
* retro_spsc_write_begin:
* @q : The queue.
* @ptr : Receives a pointer to the contiguous writable region at the
* current head.
*
* Producer-side zero-copy reservation. Returns the number of bytes
* that can be written CONTIGUOUSLY at *@ptr - this is min(total free
* space, distance to the physical end of the ring), so it can be
* smaller than retro_spsc_write_avail() when the head is near the
* wrap point. No cursor moves; call retro_spsc_write_end() with the
* number of bytes actually written (which may be 0, and must not
* exceed the returned span). A caller needing more than the span can
* fall back to retro_spsc_write(), which handles the split copy.
*
* The pointer stays valid until write_end; the consumer cannot enter
* this region because head has not moved. Alignment of *@ptr is
* whatever the caller's own write granularity guarantees: the ring
* is byte-addressed, so producers that always write N-byte records
* keep every span N-aligned.
*
* Returns: contiguous writable byte count (0 when full or when the
* head sits exactly at the wrap point of a full lap).
*
* SAFETY: producer thread only. At most one begin outstanding.
*/
size_t retro_spsc_write_begin(retro_spsc_t *q, void **ptr);
/**
* retro_spsc_write_end:
* @q : The queue.
* @bytes : Bytes actually written into the span from write_begin.
*
* Publishes @bytes with a release-store on head, making them visible
* to the consumer. Passing 0 abandons the reservation.
*
* SAFETY: producer thread only; @bytes must not exceed the span
* returned by the matching retro_spsc_write_begin.
*/
void retro_spsc_write_end(retro_spsc_t *q, size_t bytes);
/**
* retro_spsc_read_begin:
* @q : The queue.
* @ptr : Receives a pointer to the contiguous readable region at the
* current tail.
*
* Consumer-side zero-copy drain. Returns the number of bytes
* readable CONTIGUOUSLY at *@ptr - min(total buffered, distance to
* the physical end of the ring). Wrapped data needs a second
* begin/end round after consuming the first span. No cursor moves;
* call retro_spsc_read_end() with the bytes actually consumed.
*
* The region stays stable until read_end: the producer cannot write
* into it because tail has not moved.
*
* Returns: contiguous readable byte count (0 when empty).
*
* SAFETY: consumer thread only. At most one begin outstanding.
*/
size_t retro_spsc_read_begin(retro_spsc_t *q, const void **ptr);
/**
* retro_spsc_read_end:
* @q : The queue.
* @bytes : Bytes actually consumed from the span from read_begin.
*
* Frees @bytes back to the producer with a release-store on tail.
* Passing 0 abandons the read.
*
* SAFETY: consumer thread only; @bytes must not exceed the span
* returned by the matching retro_spsc_read_begin.
*/
void retro_spsc_read_end(retro_spsc_t *q, size_t bytes);
/**
* retro_spsc_skip:
* @q : The queue.
* @bytes : Number of bytes to discard.
*
* Consumer-side discard: frees up to @bytes back to the producer
* without copying them anywhere, clamped to what is actually
* buffered. This is what a consumer that has already inspected a
* record through retro_spsc_peek or retro_spsc_read_begin needs in
* order to commit the read later, without re-deriving a span it
* has already seen.
*
* Returns: number of bytes actually discarded.
*
* SAFETY: consumer thread only.
*/
size_t retro_spsc_skip(retro_spsc_t *q, size_t bytes);
RETRO_END_DECLS
#endif /* __LIBRETRO_SDK_SPSC_H */