triangular_arbitrage_bot/src/queue.c

91 lines
2.5 KiB
C

/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/eventfd.h>
#include <errno.h>
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;
}