18#define _POSIX_C_SOURCE 200809L
30#if defined(__wasm__) || defined(CWIST_BAREMETAL)
51#define LOG_ASYNC_DEFAULT_BYTES ((size_t)256 * 1024)
52#define LOG_ASYNC_STREAMS 2
66static bool g_stopping;
67static bool g_writer_alive;
69static bool g_hooks_installed;
72static log_ring_t *ring_for_fd(
int fd) {
79static bool rings_empty(
void) {
81 if (g_rings[
i].
head != g_rings[
i].tail)
return false;
88static void write_all(
int fd,
const char *
p,
size_t len) {
103static void *log_writer_main(
void *arg) {
108 if (rings_empty())
break;
111 log_ring_t *r = &g_rings[
i];
112 while (r->head != r->tail) {
113 size_t off = r->tail % r->cap;
114 size_t len = r->head - r->tail;
115 if (len > r->cap - off) len = r->cap - off;
116 const char *
p = r->buf + off;
131static bool spawn_writer(
void) {
132 if (g_writer_alive)
return true;
134 g_writer_alive =
true;
139static void atfork_prepare(
void) {
144static void atfork_parent(
void) {
151static void atfork_child(
void) {
153 g_writer_alive =
false;
162static void atexit_stop(
void) {
184 if (!g_hooks_installed) {
185 if (
pthread_atfork(atfork_prepare, atfork_parent, atfork_child) != 0 ||
186 atexit(atexit_stop) != 0) {
190 g_hooks_installed =
true;
193 log_ring_t *r = &g_rings[
i];
196 for (
int j = 0;
j <
i;
j++) {
198 g_rings[
j].buf =
NULL;
204 r->head = r->tail = 0;
207 if (!spawn_writer()) {
210 g_rings[
i].buf =
NULL;
223 while (g_active && g_writer_alive && !rings_empty())
pthread_cond_wait(&g_space, &g_lock);
237 bool join = g_writer_alive;
245 g_writer_alive =
false;
248 g_rings[
i].buf =
NULL;
249 g_rings[
i].cap = g_rings[
i].head = g_rings[
i].tail = 0;
258 bool active = g_active;
264 if (len == 0)
return true;
266 log_ring_t *r = ring_for_fd(fd);
267 if (!g_active || !r || len > r->cap) {
271 if (!g_writer_alive && !spawn_writer()) {
275 while (g_active && r->cap - (r->head - r->tail) < len)
pthread_cond_wait(&g_space, &g_lock);
281 size_t off = r->head % r->cap;
282 size_t first = r->cap - off;
Memory allocation wrappers backed by libttak.
void cwist_free(void *ptr)
Free memory obtained via cwist_alloc/cwist_strdup/etc.
void * cwist_alloc(size_t size)
Allocate zeroed memory tracked by libttak.
#define LOG_ASYNC_STREAMS
bool cwist_log_async_submit(int fd, const char *line, size_t len)
Queue one complete line for fd (STDOUT_FILENO or STDERR_FILENO).
void cwist_log_async_flush(void)
Block until every line queued so far has been written. No-op when inactive.
bool cwist_log_async_start(size_t buffer_bytes)
Start writing log lines from a background thread.
void cwist_log_async_stop(void)
Write out every queued line, stop the writer and go back to synchronous logging. Safe to call when as...
#define LOG_ASYNC_DEFAULT_BYTES
bool cwist_log_async_active(void)
Report whether async logging is active.
Internal hook from the log call sites into the async log writer.
Application-level structured logging.