/* * queue.c - Lock-free single-producer single-consumer (SPSC) bounded queue * * Uses C11 atomics with acquire/release ordering for correct head/tail * synchronization without locks. The eventfd notify on push wakes the * consumer's epoll loop (cold thread) for immediate dispatch. */ #include "queue.h" #include #include #include #include #include #include int spsc_init(spsc_queue_t *q, int wakeup_fd) { memset(q, 0, sizeof(*q)); q->buffer = calloc(Spsc_QUEUE_DEPTH, sizeof(signal_entry_t)); if (!q->buffer) return -1; q->head = 0; q->tail = 0; q->depth = Spsc_QUEUE_DEPTH; q->dropped = 0; q->eventfd = wakeup_fd; return 0; } void spsc_destroy(spsc_queue_t *q) { /* eventfd is owned by caller (event_loops), don't close */ q->eventfd = -1; free(q->buffer); q->buffer = NULL; } static inline void eventfd_notify(int fd) { uint64_t val = 1; ssize_t ret; do { ret = write(fd, &val, sizeof(val)); } while (ret < 0 && errno == EINTR); (void)ret; } bool spsc_push(spsc_queue_t *q, const signal_entry_t *entry) { uint32_t head = atomic_load_explicit(&q->head, memory_order_relaxed); uint32_t tail = atomic_load_explicit(&q->tail, memory_order_acquire); uint32_t next_head = head + 1; if (next_head >= q->depth) next_head = 0; if (next_head == tail) { q->dropped++; return false; } q->buffer[head] = *entry; atomic_store_explicit(&q->head, next_head, memory_order_release); eventfd_notify(q->eventfd); return true; } bool spsc_pop(spsc_queue_t *q, signal_entry_t *entry) { uint32_t tail = atomic_load_explicit(&q->tail, memory_order_relaxed); uint32_t head = atomic_load_explicit(&q->head, memory_order_acquire); if (tail == head) return false; *entry = q->buffer[tail]; uint32_t next_tail = tail + 1; if (next_tail >= q->depth) next_tail = 0; atomic_store_explicit(&q->tail, next_tail, memory_order_release); return true; } bool spsc_empty(const spsc_queue_t *q) { uint32_t head = atomic_load_explicit(&q->head, memory_order_acquire); uint32_t tail = atomic_load_explicit(&q->tail, memory_order_acquire); return head == tail; } uint32_t spsc_count(const spsc_queue_t *q) { uint32_t head = atomic_load_explicit(&q->head, memory_order_acquire); uint32_t tail = atomic_load_explicit(&q->tail, memory_order_acquire); if (head >= tail) return head - tail; return q->depth - tail + head; }