Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 38 additions & 4 deletions include/glatter/glatter_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,34 @@ static glatter_atomic(glatter_log_handler_fn) glatter_log_handler_state =
/* Log handler is frozen after the first log to avoid races with late setters. */
static glatter_atomic_int glatter_log_handler_frozen = GLATTER_ATOMIC_INT_INIT(0);

#ifndef GLATTER_LOG_RING_SLOTS
#define GLATTER_LOG_RING_SLOTS 8
#endif

typedef struct {
char* data;
size_t capacity;
} glatter_log_slot_t;

static GLATTER_THREAD_LOCAL glatter_log_slot_t glatter_log_ring_[GLATTER_LOG_RING_SLOTS];
static GLATTER_THREAD_LOCAL unsigned glatter_log_ring_cursor_ = 0;

static const char* glatter_log_copy_for_handler_(const char* message)
{
glatter_log_slot_t* slot = &glatter_log_ring_[glatter_log_ring_cursor_++ % GLATTER_LOG_RING_SLOTS];
size_t needed = strlen(message) + 1;
if (slot->capacity < needed) {
char* new_data = (char*)realloc(slot->data, needed);
if (!new_data) {
return NULL;
}
slot->data = new_data;
slot->capacity = needed;
}
memcpy(slot->data, message, needed);
return slot->data;
}

GLATTER_INLINE_OR_NOT
void glatter_log_handler_store(glatter_log_handler_fn handler_ptr)
{
Expand All @@ -185,17 +213,23 @@ void (*glatter_log_handler())(const char*)
GLATTER_INLINE_OR_NOT
const char* glatter_log(const char* str)
{
static const char fallback[] = "GLATTER: message formatting failed.\n";
const char* message = str;
const char* delivered = NULL;

if (message == NULL) {
static const char fallback[] = "GLATTER: message formatting failed.\n";
message = fallback;
delivered = fallback;
}
else {
const char* copied = glatter_log_copy_for_handler_(message);
delivered = copied ? copied : fallback;
}
/* Freeze the handler on first log, race-free. */
int expected = 0;
(void)GLATTER_ATOMIC_INT_CAS(glatter_log_handler_frozen, expected, 1);
glatter_log_handler_fn handler = glatter_log_handler_load();
handler(message);
return str;
handler(delivered);
return str ? delivered : NULL;
}


Expand Down
29 changes: 29 additions & 0 deletions tests/test_glatter_log_null.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,35 @@ int main(void)
return 1;
}

static const char dynamic_message[] = "GLATTER: dynamic log copy test.\n";
char* dynamic = (char*)malloc(sizeof(dynamic_message));
if (!dynamic) {
fprintf(stderr, "failed to allocate dynamic log message buffer\n");
return 1;
}
memcpy(dynamic, dynamic_message, sizeof(dynamic_message));

const char* delivered_dynamic = glatter_log(dynamic);
if (delivered_dynamic == NULL) {
fprintf(stderr, "glatter_log returned NULL for dynamic message\n");
free(dynamic);
return 1;
}

if (delivered_dynamic == dynamic) {
fprintf(stderr, "glatter_log reused caller-owned buffer\n");
free(dynamic);
return 1;
}

if (strcmp(g_last_log_message, dynamic_message) != 0) {
fprintf(stderr, "dynamic log message was not preserved: %s\n", g_last_log_message);
free(dynamic);
return 1;
}

free(dynamic);

glatter_set_log_handler(NULL);

if (glatter_log_handler() != glatter_default_log_handler) {
Expand Down