Skip to content

File syn_spsc_queue.c

File List > src > syntropic > util > syn_spsc_queue.c

Go to the documentation of this file

#include "syn_spsc_queue.h"

#include "../common/syn_barrier.h"

#include <string.h>

/* ── Helpers ────────────────────────────────────────────────────────────── */

static inline size_t spsc_advance(size_t idx, size_t capacity)
{
    size_t next = idx + 1;
    return (next >= capacity) ? 0 : next;
}

/* ── API ────────────────────────────────────────────────────────────────── */

SYN_Status syn_spsc_queue_init(SYN_SPSC_Queue *q, void *elem_buf, size_t elem_size, size_t capacity)
{
    if (!q || !elem_buf || elem_size == 0 || capacity == 0)
        return SYN_INVALID_PARAM;

    q->buffer = (uint8_t *)elem_buf;
    q->elem_size = elem_size;
    q->capacity = capacity;
    q->head = 0;
    q->tail = 0;

    return SYN_OK;
}

bool syn_spsc_queue_is_empty(const SYN_SPSC_Queue *q)
{
    if (!q || !q->buffer || q->capacity == 0)
        return true;
    return (SYN_LOAD_ACQUIRE(&q->head) == q->tail);
}

bool syn_spsc_queue_is_full(const SYN_SPSC_Queue *q)
{
    if (!q || !q->buffer || q->capacity == 0)
        return false;
    size_t next_head = spsc_advance(q->head, q->capacity);
    return (next_head == SYN_LOAD_ACQUIRE(&q->tail));
}

size_t syn_spsc_queue_count(const SYN_SPSC_Queue *q)
{
    if (!q || !q->buffer || q->capacity == 0)
        return 0;
    size_t head = SYN_LOAD_ACQUIRE(&q->head);
    size_t tail = q->tail;
    if (head >= tail) {
        return head - tail;
    }
    return q->capacity - (tail - head);
}

SYN_Status syn_spsc_queue_push(SYN_SPSC_Queue *q, const void *item)
{
    if (!q || !q->buffer || !item || q->capacity == 0 || q->elem_size == 0)
        return SYN_INVALID_PARAM;

    size_t current_head = q->head;
    size_t next_head = spsc_advance(current_head, q->capacity);

    if (next_head == SYN_LOAD_ACQUIRE(&q->tail)) {
        return SYN_BUSY; /* Queue full */
    }

    memcpy(q->buffer + (current_head * q->elem_size), item, q->elem_size);
    SYN_STORE_RELEASE(&q->head, next_head);

    return SYN_OK;
}

SYN_Status syn_spsc_queue_pop(SYN_SPSC_Queue *q, void *out_item)
{
    if (!q || !q->buffer || !out_item || q->capacity == 0 || q->elem_size == 0)
        return SYN_INVALID_PARAM;

    size_t current_head = SYN_LOAD_ACQUIRE(&q->head);
    size_t current_tail = q->tail;

    if (current_head == current_tail) {
        return SYN_ERROR; /* Queue empty */
    }

    memcpy(out_item, q->buffer + (current_tail * q->elem_size), q->elem_size);
    size_t next_tail = spsc_advance(current_tail, q->capacity);
    SYN_STORE_RELEASE(&q->tail, next_tail);

    return SYN_OK;
}