Add struct mg_queue and API, change examples/multi-threaded, add tests

This commit is contained in:
cpq committed 2023-02-11 13:24:26 +00:00
1 parent c0f4bde5fc
commit bf9d69ab56
20 files changed
+427 -94

No files matched your search

+1
View File
@@ -19,5 +19,6 @@
#define MG_PATH_MAX 100
#define MG_ENABLE_SOCKET 0
#define MG_ENABLE_DIRLIST 0
#define MG_ENABLE_ATOMIC 1
#endif
+1
View File
@@ -12,4 +12,5 @@
#include <pico/stdlib.h>
int mkdir(const char *, mode_t);
#define MG_ENABLE_ATOMIC 1
#endif
+4
View File
@@ -8,6 +8,10 @@
#define MG_ENABLE_TCPIP 0 // Mongoose built-in network stack
#endif
#ifndef MG_ENABLE_ATOMIC
#define MG_ENABLE_ATOMIC 0 // Required by mg_queue
#endif
#ifndef MG_ENABLE_LWIP
#define MG_ENABLE_LWIP 0 // lWIP network stack
#endif
+3 -1
View File
@@ -2,11 +2,11 @@
#include "arch.h"
#include "iobuf.h"
#include "queue.h"
typedef void (*mg_pfn_t)(char, void *); // Output function
typedef size_t (*mg_pm_t)(mg_pfn_t, void *, va_list *); // %M printer
// The lowest level
size_t mg_vxprintf(void (*)(char, void *), void *, const char *fmt, va_list *);
size_t mg_xprintf(void (*fn)(char, void *), void *, const char *fmt, ...);
@@ -15,6 +15,8 @@ size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap);
size_t mg_snprintf(char *, size_t, const char *fmt, ...);
char *mg_vmprintf(const char *fmt, va_list *ap);
char *mg_mprintf(const char *fmt, ...);
size_t mg_queue_vprintf(struct mg_queue *, const char *fmt, va_list *);
size_t mg_queue_printf(struct mg_queue *, const char *fmt, ...);
// %M print helper functions
size_t mg_print_ip(void (*out)(char, void *), void *arg, va_list *ap);
+22
View File
@@ -1,7 +1,29 @@
#include "fmt.h"
#include "iobuf.h"
#include "queue.h"
#include "util.h"
size_t mg_queue_vprintf(struct mg_queue *q, const char *fmt, va_list *ap) {
size_t len = mg_snprintf(NULL, 0, fmt, ap);
char *buf;
if (mg_queue_space(q, &buf) < len + 1) {
len = 0; // Nah. Not enough space
} else {
len = mg_vsnprintf((char *)buf, len + 1, fmt, ap);
mg_queue_add(q, len);
}
return len;
}
size_t mg_queue_printf(struct mg_queue *q, const char *fmt, ...) {
va_list ap;
size_t len;
va_start(ap, fmt);
len = mg_queue_vprintf(q, fmt, &ap);
va_end(ap);
return len;
}
static void mg_pfn_iobuf_private(char ch, void *param, bool expand) {
struct mg_iobuf *io = (struct mg_iobuf *) param;
if (expand && io->len + 2 > io->size) mg_iobuf_resize(io, io->len + 2);
+40
View File
@@ -0,0 +1,40 @@
#include "queue.h"
// Every message in the queue is prepended by the message length (ML)
// ML is sizeof(size_t) in size
// Tail points to the message data
//
// |------| ML | message1 | ML | message2 |--- free space ---|
// ^ ^ ^ ^
// buf tail head len
size_t mg_queue_space(struct mg_queue *q, char **buf) {
size_t ofs;
if (q->head > 0 && q->tail >= q->head) { // All messages read?
q->head = 0; // Yes. Reset head first
q->tail = 0; // Now reset the tail
}
ofs = q->head + sizeof(size_t);
if (buf != NULL) *buf = q->buf + ofs;
return ofs > q->len ? 0 : q->len - ofs;
}
size_t mg_queue_next(struct mg_queue *q, char **buf) {
size_t len = MG_QUEUE_EMPTY;
if (q->tail < q->head) memcpy(&len, &q->buf[q->tail], sizeof(len));
if (buf != NULL) *buf = &q->buf[q->tail + sizeof(len)];
return len;
}
void mg_queue_add(struct mg_queue *q, size_t len) {
size_t head = q->head + len + (size_t) sizeof(head); // New head
if (head <= q->len) { // Have space ?
memcpy(q->buf + q->head, &len, sizeof(len)); // Yes. Store ML
q->head = head; // Advance head
}
}
void mg_queue_del(struct mg_queue *q) {
size_t len = mg_queue_next(q, NULL), tail = q->tail + len + sizeof(size_t);
if (len != MG_QUEUE_EMPTY) q->tail = tail;
}
+38
View File
@@ -0,0 +1,38 @@
#pragma once
#include "arch.h" // For size_t
#include "config.h" // For MG_ENABLE_ATOMIC
#if MG_ENABLE_ATOMIC
#include <stdatomic.h>
#elif !defined(_Atomic)
#define _Atomic
#endif
// Single producer, single consumer non-blocking queue
//
// Producer:
// void *buf;
// while (mg_queue_space(q, &buf) < len) WAIT(); // Wait for free space
// memcpy(buf, data, len); // Copy data to the queue
// mg_queue_add(q, len); // Advance q->head
//
// Consumer:
// void *buf;
// while ((len = mg_queue_next(q, &buf)) == MG_QUEUE_EMPTY) WAIT();
// mg_hexdump(buf, len); // Handle message
// mg_queue_del(q); // Delete message
//
struct mg_queue {
char *buf;
size_t len;
volatile _Atomic size_t tail;
volatile _Atomic size_t head;
};
#define MG_QUEUE_EMPTY ((size_t) ~0ul) // Next message size when queue is empty
void mg_queue_add(struct mg_queue *, size_t len); // Advance head
void mg_queue_del(struct mg_queue *); // Advance tail
size_t mg_queue_space(struct mg_queue *, char **); // Get free space
size_t mg_queue_next(struct mg_queue *, char **); // Get next message size