Skip to content

Instantly share code, notes, and snippets.

@skeeto
Created April 12, 2025 14:21
Show Gist options
  • Save skeeto/20756eb4c177744cb98354f5a6173097 to your computer and use it in GitHub Desktop.
Save skeeto/20756eb4c177744cb98354f5a6173097 to your computer and use it in GitHub Desktop.
Single consumer, single producer circular buffer using Win32 SRW
// Single consumer, single producer circular buffer using Win32 SRW
// Ref: https://github.com/vibhav950/cbuf
// Ref: https://old.reddit.com/r/C_Programming/comments/1jwnlrf
// Ref: https://nullprogram.com/blog/2024/10/03/
#include <stddef.h>
#include <stdint.h>
#include <string.h>
typedef uint8_t u8;
typedef int32_t b32;
typedef int32_t i32;
typedef ptrdiff_t iz;
typedef size_t uz;
typedef struct { uz _; } Lock;
typedef struct { uz _; } Cond;
#define W32(r) __declspec(dllimport) r __stdcall
W32(i32) SleepConditionVariableSRW(Cond *, Lock *, i32, i32);
W32(void) AcquireSRWLockExclusive(Lock *);
W32(void) ReleaseSRWLockExclusive(Lock *);
W32(void) WakeConditionVariable(Cond *);
typedef struct {
u8 *buf;
iz cap;
Lock m;
Cond cv;
u8 *writep;
u8 *readp;
} Queue;
static Queue queue_init(u8 *buf, iz cap)
{
Queue q = {0};
q.writep = q.readp = q.buf = buf;
q.cap = cap;
return q;
}
// Use -1 for timeout_ms to wait indefinitely.
static b32 queue_read(Queue *q, u8 *buf, iz len, i32 timeout_ms)
{
AcquireSRWLockExclusive(&q->m);
iz avail = -1;
for (;;) {
avail = q->writep - q->readp;
avail += avail<0 ? q->cap : 0;
if (avail >= len) {
break;
}
if (!SleepConditionVariableSRW(&q->cv, &q->m, timeout_ms, 0)) {
ReleaseSRWLockExclusive(&q->m);
return 0;
}
// FIXME: get sleep time and reduce timeout_ms
}
iz trunc = q->cap - (q->readp - q->buf);
if (trunc < len) {
memcpy(buf, q->readp, (uz)trunc);
q->readp = q->buf;
buf += trunc;
len -= trunc;
}
memcpy(buf, q->readp, (uz)len);
q->readp += len;
ReleaseSRWLockExclusive(&q->m);
WakeConditionVariable(&q->cv);
return 1;
}
// Use -1 for timeout_ms to wait indefinitely.
static b32 queue_write(Queue *q, u8 *buf, iz len, i32 timeout_ms)
{
AcquireSRWLockExclusive(&q->m);
iz avail = -1;
for (;;) {
avail = q->readp - q->writep;
avail += avail<=0 ? q->cap : 0;
if (avail >= len) {
break;
}
if (!SleepConditionVariableSRW(&q->cv, &q->m, timeout_ms, 0)) {
ReleaseSRWLockExclusive(&q->m);
return 0;
}
// FIXME: get sleep time and reduce timeout_ms
}
iz trunc = q->cap - (q->writep - q->buf);
if (trunc < len) {
memcpy(q->writep, buf, (uz)trunc);
q->writep = q->buf;
buf += trunc;
len -= trunc;
}
memcpy(q->writep, buf, (uz)len);
q->writep += len;
ReleaseSRWLockExclusive(&q->m);
WakeConditionVariable(&q->cv);
return 1;
}
// test / demo
#include <stdio.h>
#define lenof(a) ((i32)(sizeof(a) / sizeof(*(a))))
int main(void)
{
enum { cap = 8 };
Queue q = queue_init((u8[cap]){0}, cap);
b32 ok = 0;
{
u8 msg[] = "hello";
ok = queue_write(&q, msg, lenof(msg)-1, 100);
printf("ok = %d\n", ok);
ok = queue_write(&q, msg, lenof(msg)-1, 100);
printf("ok = %d\n", ok);
}
{
u8 buf[2] = {0};
ok = queue_read(&q, buf, lenof(buf), 100);
printf("ok = %d [%.*s]\n", ok, lenof(buf), buf);
ok = queue_read(&q, buf, 2, 100);
printf("ok = %d [%.*s]\n", ok, lenof(buf), buf);
ok = queue_read(&q, buf, 2, 100);
printf("ok = %d [%.*s]\n", ok, lenof(buf), buf);
}
{
u8 msg[] = "world";
ok = queue_write(&q, msg, lenof(msg)-1, 100);
printf("ok = %d\n", ok);
}
{
u8 buf[6] = {0};
ok = queue_read(&q, buf, lenof(buf), 100);
printf("ok = %d [%.*s]\n", ok, lenof(buf), buf);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment