ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches

⚠️ Thread-Local Error Number System with Context More...

Files

file  asciichat_errno.c
 🚨 Custom error code system with formatted messages, thread-local storage, and errno mapping
 
file  asciichat_errno.h
 ⚠️‼️ Error and/or exit() when things go bad.
 

Data Structures

struct  asciichat_error_context_t
 Error context structure. More...
 
struct  asciichat_error_stats_t
 Error statistics structure. More...
 

Macros

#define SET_ERRNO(code, context_msg, ...)
 Set error code with custom context message and log it.
 
#define SET_ERRNO_SYS(code, context_msg, ...)
 Set error code with custom message and system error context.
 
#define HAS_ERRNO(var)   asciichat_has_errno(var)
 Check if an error occurred and get full context.
 
#define CLEAR_ERRNO()   asciichat_clear_errno()
 Clear the current error state.
 
#define GET_ERRNO()   asciichat_get_errno()
 Get current error code (0 if no error)
 

Functions

void asciichat_set_errno (asciichat_error_t code, const char *file, int line, const char *function, const char *context_message)
 Set error code with basic context.
 
void asciichat_set_errno_with_message (asciichat_error_t code, const char *file, int line, const char *function, const char *format,...)
 Set error code with formatted message.
 
void asciichat_set_errno_with_system_error (asciichat_error_t code, const char *file, int line, const char *function, int sys_errno)
 Set error code with system error (errno)
 
void asciichat_set_errno_with_system_error_and_message (asciichat_error_t code, const char *file, int line, const char *function, int sys_errno, const char *format,...)
 Set error code with system error and formatted message.
 
void asciichat_set_errno_with_wsa_error (asciichat_error_t code, const char *file, int line, const char *function, int wsa_error)
 Set error code with Windows socket error (WSA error)
 
bool asciichat_has_errno (asciichat_error_context_t *context)
 Check if error occurred and get full context.
 
bool asciichat_has_wsa_error (void)
 Check if current error has WSA error code.
 
void asciichat_clear_errno (void)
 Clear the current error state.
 
asciichat_error_t asciichat_get_errno (void)
 Get current error code.
 
void asciichat_fatal_with_context (asciichat_error_t code, const char *file, int line, const char *function, const char *format,...)
 Exit with error code and context (used by FATAL macro)
 
void asciichat_print_error_context (const asciichat_error_context_t *context)
 Print full error context to stderr.
 
void asciichat_error_stats_init (void)
 Initialize error statistics system.
 
void asciichat_error_stats_record (asciichat_error_t code)
 Record an error in statistics.
 
void asciichat_error_stats_print (void)
 Print error statistics to stderr.
 
void asciichat_error_stats_reset (void)
 Reset all error statistics to zero.
 
asciichat_error_stats_t asciichat_error_stats_get (void)
 Get current error statistics.
 
asciichat_error_t asciichat_get_thread_error (int thread_id)
 Get error code for a specific thread.
 
void asciichat_set_thread_error (int thread_id, asciichat_error_t code)
 Set error code for a specific thread.
 
void asciichat_clear_thread_error (int thread_id)
 Clear error code for a specific thread.
 
void asciichat_errno_suppress (bool suppress)
 Suppress error logging and reporting.
 
void asciichat_errno_cleanup (void)
 Cleanup error system resources.
 

Variables

__thread asciichat_error_context_t asciichat_errno_context
 Thread-local error context storage.
 
__thread asciichat_error_t asciichat_errno
 Thread-local current error code.
 

Detailed Description

⚠️ Thread-Local Error Number System with Context

This header provides a comprehensive thread-local error number system that captures full error context including location, stack traces, and system errors. The system integrates with ascii-chat's error handling to provide detailed debugging information.

CORE FEATURES:

ERROR CONTEXT:

The system captures:

LIBRARY MACROS:

Use SET_ERRNO and SET_ERRNO_SYS in lib/ code:

APPLICATION MACROS:

Use HAS_ERRNO, GET_ERRNO, CLEAR_ERRNO in src/ code:

THREAD SAFETY:

DEBUG FEATURES:

In debug builds, the system:

Note
Use SET_ERRNO() in library code to set errors.
Use HAS_ERRNO() in application code to check errors.
Stack traces are only captured in debug builds.
Thread-local storage ensures thread safety automatically.
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
October 2025

asciichat_errno

Overview

The Error Handling System provides comprehensive error tracking and reporting for ascii-chat. It uses typed error codes, error context capture, and thread-local storage for safe multi-threaded error handling.

Implementation: lib/asciichat_errno.h

Key Features:

  • Typed error codes via asciichat_error_t enum
  • Thread-local error context storage
  • Automatic file/line/function capture
  • System error integration (errno capture)
  • Error context stack traces (debug builds)
  • Comprehensive error messages with context

Architecture

Error Code System:

  • All functions return asciichat_error_t instead of int
  • ASCIICHAT_OK (0) indicates success
  • Negative values indicate specific error types
  • Error codes are categorized by subsystem

Thread-Local Storage:

  • Each thread has its own error context
  • No cross-thread error contamination
  • Safe for concurrent error handling

Error Context Capture:

  • File name and line number where error occurred
  • Function name where error was set
  • Custom error message with printf-style formatting
  • System errno value (when applicable)
  • Stack trace (debug builds only)

Usage Patterns

Setting Errors

SET_ERRNO Macro (preferred):

// Set error with custom message
if (socket_fd < 0) {
return SET_ERRNO(ERROR_NETWORK_SOCKET, "Failed to create socket");
}
// Set error with formatted message
if (bind(sockfd, &addr, sizeof(addr)) < 0) {
return SET_ERRNO(ERROR_NETWORK_BIND, "Cannot bind to port %d", port);
}
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
@ ERROR_NETWORK_BIND
Definition error_codes.h:70

SET_ERRNO_SYS Macro (captures system errno):

// Capture both error code and system errno
int fd = open(path, O_RDONLY);
if (fd < 0) {
return SET_ERRNO_SYS(ERROR_CONFIG, "Failed to open config: %s", path);
}
#define SET_ERRNO_SYS(code, context_msg,...)
Set error code with custom message and system error context.
@ ERROR_CONFIG
Definition error_codes.h:54

Checking Errors

Check for Error:

if (HAS_ERRNO(&err_ctx)) {
log_error("Error: %s", err_ctx.context_message);
log_error(" at %s:%d in %s()", err_ctx.file, err_ctx.line, err_ctx.function);
}
#define HAS_ERRNO(var)
Check if an error occurred and get full context.
#define log_error(...)
Log an ERROR message.
Error context structure.
const char * function
Function name where error occurred (NULL in release builds)
char * context_message
Optional custom message (dynamically allocated, owned by system)
const char * file
Source file where error occurred (NULL in release builds)
int line
Line number where error occurred (0 in release builds)

Get Current Error Code:

asciichat_error_t current_error = GET_ERRNO();
if (current_error != ASCIICHAT_OK) {
// Handle error
}
#define GET_ERRNO()
Get current error code (0 if no error)
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:46
@ ASCIICHAT_OK
Definition error_codes.h:48

Clear Error State:

CLEAR_ERRNO(); // Reset error state for this thread
#define CLEAR_ERRNO()
Clear the current error state.

Debug Output

Print Error Context (debug builds only):

if (HAS_ERRNO(&err_ctx)) {
PRINT_ERRNO_CONTEXT(&err_ctx); // Prints full context including stack trace
}
#define PRINT_ERRNO_CONTEXT(context)

Error Code Categories

General Errors:

  • ASCIICHAT_OK (0) - Success
  • ERROR_UNKNOWN (-1) - Unknown error
  • ERROR_INVALID_PARAM (-2) - Invalid parameter
  • ERROR_OUT_OF_MEMORY (-3) - Memory allocation failed

Network Errors:

  • ERROR_NETWORK_SOCKET - Socket creation failed
  • ERROR_NETWORK_BIND - Socket bind failed
  • ERROR_NETWORK_LISTEN - Socket listen failed
  • ERROR_NETWORK_CONNECT - Connection failed
  • ERROR_NETWORK_SEND - Send operation failed
  • ERROR_NETWORK_RECV - Receive operation failed

Crypto Errors:

  • ERROR_CRYPTO_INIT - Crypto initialization failed
  • ERROR_CRYPTO_KEY - Key loading/parsing failed
  • ERROR_CRYPTO_HANDSHAKE - Handshake failed
  • ERROR_CRYPTO_ENCRYPT - Encryption failed
  • ERROR_CRYPTO_DECRYPT - Decryption failed

Platform Errors:

  • ERROR_PLATFORM_THREAD - Thread operation failed
  • ERROR_PLATFORM_MUTEX - Mutex operation failed
  • ERROR_PLATFORM_SOCKET - Platform-specific socket error

Configuration Errors:

  • ERROR_CONFIG - Configuration loading failed
  • ERROR_CONFIG_PARSE - Configuration parsing failed
  • ERROR_CONFIG_INVALID - Invalid configuration value

Integration with Logging

Automatic Logging:

  • SET_ERRNO() automatically logs errors at ERROR level
  • SET_ERRNO_SYS() logs with system error details
  • Error messages include context automatically

Example Integration:

asciichat_error_t process_client(client_t *client) {
if (!client) {
return SET_ERRNO(ERROR_INVALID_PARAM, "Client is NULL");
// Automatically logged: "ERROR: Client is NULL"
}
int result = some_operation();
if (result < 0) {
return SET_ERRNO_SYS(ERROR_OPERATION_FAILED, "Operation failed");
// Automatically logged with errno details
}
return ASCIICHAT_OK;
}
@ ERROR_INVALID_PARAM

Best Practices

DO:

  • Always use asciichat_error_t for return types
  • Use SET_ERRNO() for all error conditions
  • Use SET_ERRNO_SYS() when capturing system errors
  • Provide descriptive error messages
  • Check return values and propagate errors

DON'T:

  • Don't use int return values with -1 for errors
  • Don't ignore return values
  • Don't use generic error messages
  • Don't access errno directly (use SET_ERRNO_SYS)
  • Don't forget to check errors from called functions

Thread Safety

Thread-Local Storage:

  • Each thread maintains its own error context
  • No locks needed for error operations
  • Safe for concurrent error handling
  • Thread ID captured in error context

Example Multi-threaded Usage:

void* worker_thread(void *arg) {
// This thread's error context is independent
asciichat_error_t err = do_work();
if (err != ASCIICHAT_OK) {
if (HAS_ERRNO(&ctx)) {
log_error("Worker failed: %s", ctx.context_message);
}
}
return NULL;
}
See also
asciichat_errno.h
logging.h

Macro Definition Documentation

◆ CLEAR_ERRNO

#define CLEAR_ERRNO ( )    asciichat_clear_errno()

#include <asciichat_errno.h>

Clear the current error state.

Definition at line 259 of file asciichat_errno.h.

◆ GET_ERRNO

#define GET_ERRNO ( )    asciichat_get_errno()

#include <asciichat_errno.h>

Get current error code (0 if no error)

Definition at line 264 of file asciichat_errno.h.

◆ HAS_ERRNO

#define HAS_ERRNO (   var)    asciichat_has_errno(var)

#include <asciichat_errno.h>

Check if an error occurred and get full context.

Parameters
varVariable to store error context

Usage in src/ code: asciichat_error_context_t err_ctx; if (HAS_ERRNO(&err_ctx)) { FATAL(err_ctx.code, "Library error occurred"); }

Definition at line 254 of file asciichat_errno.h.

◆ SET_ERRNO

#define SET_ERRNO (   code,
  context_msg,
  ... 
)

#include <asciichat_errno.h>

Value:
({ \
asciichat_set_errno_with_message(code, __FILE__, __LINE__, __func__, context_msg, ##__VA_ARGS__); \
log_error("SET_ERRNO: " context_msg " (code: %d, meaning: %s)", ##__VA_ARGS__, code, \
asciichat_error_string(code)); \
(code); \
})

Set error code with custom context message and log it.

Parameters
codeError code to set
context_msgCustom message explaining the error

Usage in lib/ code: if (bind(sockfd, ...) < 0) { SET_ERRNO(ERROR_NETWORK_BIND, "Cannot bind to port %d", port); return ERROR_NETWORK_BIND; }

Definition at line 190 of file asciichat_errno.h.

191 { \
192 asciichat_set_errno_with_message(code, __FILE__, __LINE__, __func__, context_msg, ##__VA_ARGS__); \
193 log_error("SET_ERRNO: " context_msg " (code: %d, meaning: %s)", ##__VA_ARGS__, code, \
194 asciichat_error_string(code)); \
195 (code); \
196 })

◆ SET_ERRNO_SYS

#define SET_ERRNO_SYS (   code,
  context_msg,
  ... 
)

#include <asciichat_errno.h>

Value:
({ \
int captured_errno = platform_get_last_error(); \
asciichat_set_errno_with_system_error_and_message(code, __FILE__, __LINE__, __func__, captured_errno, context_msg, \
##__VA_ARGS__); \
log_error("SETERRNO_SYS: " context_msg " (code: %d - %s, system error: %d - %s)", ##__VA_ARGS__, code, \
asciichat_error_string(code), captured_errno, platform_strerror(captured_errno)); \
(code); \
})
int platform_get_last_error(void)
Get last platform error code.
const char * platform_strerror(int errnum)
Get thread-safe error string.

Set error code with custom message and system error context.

Parameters
codeError code to set
context_msgCustom message explaining the error

Usage in lib/ code: if (open(file, O_RDONLY) < 0) { SET_ERRNO_SYS(ERROR_CONFIG, "Failed to open config file: %s", path); return ERROR_CONFIG; }

Definition at line 221 of file asciichat_errno.h.

222 { \
223 int captured_errno = platform_get_last_error(); \
224 asciichat_set_errno_with_system_error_and_message(code, __FILE__, __LINE__, __func__, captured_errno, context_msg, \
225 ##__VA_ARGS__); \
226 log_error("SETERRNO_SYS: " context_msg " (code: %d - %s, system error: %d - %s)", ##__VA_ARGS__, code, \
227 asciichat_error_string(code), captured_errno, platform_strerror(captured_errno)); \
228 (code); \
229 })

Function Documentation

◆ asciichat_clear_errno()

void asciichat_clear_errno ( void  )

#include <asciichat_errno.h>

Clear the current error state.

Clears the thread-local error state, resetting error code to ASCIICHAT_OK and freeing any associated context message. This is the low-level function used by CLEAR_ERRNO() macro.

Note
Error context structure is reset to default state.
Any context message is freed automatically.
Thread-safe: Uses thread-local storage.
Warning
Use CLEAR_ERRNO() macro instead of calling this directly.

Definition at line 241 of file asciichat_errno.c.

241 {
245 }
246
250 }
251
257
258#ifdef _WIN32
259 WSASetLastError(0);
261#endif
262
263 errno = 0;
264}
#define SAFE_FREE(ptr)
Definition common.h:320
__thread asciichat_error_context_t asciichat_errno_context
Thread-local error context storage.
void platform_backtrace_symbols_free(char **strings)
Free symbol array returned by platform_backtrace_symbols()
int errno
int wsa_error
Windows socket error code (if applicable, 0 otherwise)
int system_errno
System errno value (if applicable, 0 otherwise)
char ** backtrace_symbols
Stack trace symbol strings (debug builds only)
bool has_system_error
True if system_errno is valid.
bool has_wsa_error
True if wsa_error is valid.
asciichat_error_t code
Error code (asciichat_error_t enum value)

References asciichat_errno_context, ASCIICHAT_OK, asciichat_error_context_t::backtrace_symbols, asciichat_error_context_t::code, asciichat_error_context_t::context_message, errno, asciichat_error_context_t::has_system_error, asciichat_error_context_t::has_wsa_error, platform_backtrace_symbols_free(), SAFE_FREE, asciichat_error_context_t::system_errno, and asciichat_error_context_t::wsa_error.

◆ asciichat_clear_thread_error()

void asciichat_clear_thread_error ( int  thread_id)

#include <asciichat_errno.h>

Clear error code for a specific thread.

Parameters
thread_idThread ID to clear error for

Clears the error code for a specific thread, resetting it to ASCIICHAT_OK.

Note
Thread ID must be valid (positive integer).

Definition at line 468 of file asciichat_errno.c.

468 {
469 for (int i = 0; i < MAX_THREAD_ERRORS; i++) {
470 if (thread_errors[i].valid && thread_errors[i].thread_id == thread_id) {
471 thread_errors[i].valid = false;
472 break;
473 }
474 }
475}
#define MAX_THREAD_ERRORS
bool valid
int thread_id

References MAX_THREAD_ERRORS, thread_id, and valid.

◆ asciichat_errno_cleanup()

void asciichat_errno_cleanup ( void  )

#include <asciichat_errno.h>

Cleanup error system resources.

Cleans up error system resources including statistics and thread error storage. Should be called at application shutdown after all error handling is complete.

Note
Safe to call multiple times (no-op after first call).
All error statistics and context messages are freed.

Definition at line 486 of file asciichat_errno.c.

486 {
490 }
491
494 }
495
496 // Reset the context to a clean state
499
500 // Suppress any further error context allocation to prevent cleanup-phase leaks
501 // This prevents other atexit() functions from allocating new contexts after we've cleaned up
502 g_suppress_error_context = true;
503}

References asciichat_errno_context, ASCIICHAT_OK, asciichat_error_context_t::backtrace_symbols, asciichat_error_context_t::code, asciichat_error_context_t::context_message, platform_backtrace_symbols_free(), and SAFE_FREE.

Referenced by __attribute__(), asciichat_shared_init(), client_audio_render_thread(), client_receive_thread(), client_send_thread_func(), client_video_render_thread(), server_main(), and stats_logger_thread().

◆ asciichat_errno_suppress()

void asciichat_errno_suppress ( bool  suppress)

#include <asciichat_errno.h>

Suppress error logging and reporting.

Parameters
suppressIf true, suppress error logging; if false, enable logging

Controls whether errors are automatically logged when set. When suppressed, errors are still set in thread-local storage but logging is disabled. Useful for testing or when error logging would be too verbose.

Note
Suppression only affects automatic logging, not manual error checking.
Errors can still be checked using HAS_ERRNO() and GET_ERRNO().

Definition at line 482 of file asciichat_errno.c.

482 {
483 g_suppress_error_context = suppress;
484}

◆ asciichat_error_stats_get()

asciichat_error_stats_t asciichat_error_stats_get ( void  )

#include <asciichat_errno.h>

Get current error statistics.

Returns
Current error statistics structure (copy)

Retrieves a copy of the current error statistics. Returns all statistics including per-error-code counts, total errors, and last error information.

Note
Returns a copy of statistics (caller can modify without affecting internal state).
Thread-safe: Can be called from any thread.

Definition at line 427 of file asciichat_errno.c.

427 {
428 if (!stats_initialized) {
430 }
431 return error_stats;
432}
void asciichat_error_stats_init(void)
Initialize error statistics system.

References asciichat_error_stats_init().

◆ asciichat_error_stats_init()

void asciichat_error_stats_init ( void  )

#include <asciichat_errno.h>

Initialize error statistics system.

Initializes the error statistics tracking system. Must be called before recording any error statistics. Statistics are initialized to zero.

Note
Idempotent: Safe to call multiple times (no-op after first call).
Thread-safe: Can be called from any thread.

Definition at line 374 of file asciichat_errno.c.

374 {
375 if (!stats_initialized) {
376 memset(&error_stats, 0, sizeof(error_stats));
377 stats_initialized = true;
378 }
379}

Referenced by asciichat_error_stats_get(), and asciichat_error_stats_record().

◆ asciichat_error_stats_print()

void asciichat_error_stats_print ( void  )

#include <asciichat_errno.h>

Print error statistics to stderr.

Prints comprehensive error statistics including per-error-code counts, total errors, and last error information. Useful for periodic monitoring and debugging.

Note
Statistics are formatted and printed at INFO level.
Requires statistics system to be initialized.

Definition at line 394 of file asciichat_errno.c.

394 {
395 if (!stats_initialized || error_stats.total_errors == 0) {
396 log_plain("No errors recorded.\n");
397 return;
398 }
399
400 log_plain("\n=== ascii-chat Error Statistics ===\n");
401 log_plain("Total errors: %llu\n", (unsigned long long)error_stats.total_errors);
402
403 if (error_stats.last_error_time > 0) {
404 time_t sec = (time_t)(error_stats.last_error_time / 1000000);
405 struct tm tm_info;
406 if (platform_localtime(&sec, &tm_info) == ASCIICHAT_OK) {
407 char time_str[64];
408 strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", &tm_info);
409 log_plain("Last error: %s (code %d)\n", time_str, (int)error_stats.last_error_code);
410 }
411 }
412
413 log_plain("\nError breakdown:\n");
414 for (int i = 0; i < 256; i++) {
415 if (error_stats.error_counts[i] > 0) {
416 log_plain(" %3d (%s): %llu\n", i, asciichat_error_string((asciichat_error_t)i),
417 (unsigned long long)error_stats.error_counts[i]);
418 }
419 }
420 log_plain("\n");
421}
#define log_plain(...)
Plain logging - writes to both log file and stderr without timestamps or log levels.
asciichat_error_t platform_localtime(const time_t *timer, struct tm *result)
Platform-safe localtime wrapper.
uint64_t last_error_time
Timestamp of last error (microseconds since epoch)
uint64_t error_counts[256]
Count for each error code (256 possible error codes)
asciichat_error_t last_error_code
Error code of last error recorded.
uint64_t total_errors
Total number of errors recorded (sum of all error_counts)

References ASCIICHAT_OK, asciichat_error_stats_t::error_counts, asciichat_error_stats_t::last_error_code, asciichat_error_stats_t::last_error_time, log_plain, platform_localtime(), and asciichat_error_stats_t::total_errors.

Referenced by server_main(), and stats_logger_thread().

◆ asciichat_error_stats_record()

void asciichat_error_stats_record ( asciichat_error_t  code)

#include <asciichat_errno.h>

Record an error in statistics.

Parameters
codeError code to record (asciichat_error_t)

Records an error occurrence in the statistics system. Updates per-error-code count, total error count, and last error information.

Note
Thread-safe: Can be called from multiple threads simultaneously.
Statistics counters are updated atomically.

Definition at line 381 of file asciichat_errno.c.

381 {
382 if (!stats_initialized) {
384 }
385
386 if (code >= 0 && code < 256) {
387 error_stats.error_counts[code]++;
388 }
389 error_stats.total_errors++;
390 error_stats.last_error_time = get_timestamp_microseconds();
391 error_stats.last_error_code = code;
392}

References asciichat_error_stats_init(), asciichat_error_stats_t::error_counts, asciichat_error_stats_t::last_error_code, asciichat_error_stats_t::last_error_time, and asciichat_error_stats_t::total_errors.

Referenced by asciichat_set_errno().

◆ asciichat_error_stats_reset()

void asciichat_error_stats_reset ( void  )

#include <asciichat_errno.h>

Reset all error statistics to zero.

Resets all error statistics counters to zero. Useful for resetting statistics between test runs or monitoring periods.

Note
Thread-safe: Can be called from any thread.
All counters are reset to zero (total_errors, error_counts, etc.).

Definition at line 423 of file asciichat_errno.c.

423 {
424 memset(&error_stats, 0, sizeof(error_stats));
425}

◆ asciichat_fatal_with_context()

void asciichat_fatal_with_context ( asciichat_error_t  code,
const char *  file,
int  line,
const char *  function,
const char *  format,
  ... 
)

#include <asciichat_errno.h>

Exit with error code and context (used by FATAL macro)

Parameters
codeError code (asciichat_error_t)
fileSource file name (can be NULL)
lineLine number (0 if not provided)
functionFunction name (can be NULL)
formatFormat string (printf-style)
...Format arguments

Low-level function used by FATAL() macro. Sets error context and exits the program with the specified error code. In debug builds, prints stack trace before exiting.

Note
This function never returns (terminates program).
In debug builds, prints full error context and stack trace.
Thread-safe: Uses thread-local storage.
Warning
This function terminates the program. Use FATAL() macro instead of calling this directly.

Definition at line 275 of file asciichat_errno.c.

276 {
277 (void)file;
278 (void)line;
279 (void)function;
280
281 // Print library error context if available
283 if (HAS_ERRNO(&err_ctx)) {
284 log_labeled("\nasciichat_errno: libary code error context", LOG_COLOR_ERROR, "");
286 } else {
287 log_plain("WARNING: No error context found (asciichat_errno_context.code=%d)", asciichat_errno_context.code);
288 }
289
290 safe_fprintf(stderr, "\n");
291 log_labeled("FATAL ERROR", LOG_COLOR_FATAL, "exit code %d (%s)", (int)code, asciichat_error_string(code));
292#ifndef NDEBUG
293 const char *relative_file = extract_project_relative_path(file);
294 log_plain(" Location: %s:%d in %s()", relative_file, line, function);
295#endif
296
297 if (format) {
298 va_list args;
299 va_start(args, format);
300 char *formatted_message = format_message(format, args);
301 log_plain(" Error message: %s", formatted_message);
302 SAFE_FREE(formatted_message);
303 va_end(args);
304 }
305
306#ifndef NDEBUG
307 // Always print platform backtrace in debug/dev builds
308 void *buffer[32];
309 int size = platform_backtrace(buffer, 32);
310 if (size > 0) {
311 char **symbols = platform_backtrace_symbols(buffer, size);
312 if (symbols) {
313 platform_print_backtrace_symbols("\nFATAL BACKTRACE", symbols, size, 0, 0, skip_backtrace_frame);
315 }
316 }
317#endif
318
319 exit(code);
320}
void asciichat_print_error_context(const asciichat_error_context_t *context)
Print full error context to stderr.
char * format_message(const char *format, va_list args)
Format a message using va_list.
void log_labeled(const char *label, log_color_t color, const char *message,...)
Print a labeled message with color.
@ LOG_COLOR_FATAL
@ LOG_COLOR_ERROR
int safe_fprintf(FILE *stream, const char *format,...)
Safe version of fprintf.
int platform_backtrace(void **buffer, int size)
Get a backtrace of the current call stack.
void platform_print_backtrace_symbols(const char *label, char **symbols, int count, int skip_frames, int max_frames, backtrace_frame_filter_t filter)
Print pre-resolved backtrace symbols with consistent formatting.
char ** platform_backtrace_symbols(void *const *buffer, int size)
Convert backtrace addresses to symbol names.
const char * extract_project_relative_path(const char *file)
Extract relative path from an absolute path.
Definition path.c:127

◆ asciichat_get_errno()

asciichat_error_t asciichat_get_errno ( void  )

#include <asciichat_errno.h>

Get current error code.

Returns
Current error code (ASCIICHAT_OK if no error)

Returns the current thread-local error code. Returns ASCIICHAT_OK if no error has occurred. This is the low-level function used by GET_ERRNO().

Note
Thread-safe: Uses thread-local storage.
For full error context, use HAS_ERRNO() instead.
Warning
Use GET_ERRNO() macro instead of calling this directly.

Definition at line 266 of file asciichat_errno.c.

266 {
268}

References asciichat_errno_context, and asciichat_error_context_t::code.

◆ asciichat_get_thread_error()

asciichat_error_t asciichat_get_thread_error ( int  thread_id)

#include <asciichat_errno.h>

Get error code for a specific thread.

Parameters
thread_idThread ID to query
Returns
Error code for thread, or ASCIICHAT_OK if no error

Retrieves the error code for a specific thread. Useful for checking errors from other threads or propagating errors across thread boundaries.

Note
Thread ID must be valid (positive integer).
Returns ASCIICHAT_OK if thread has no error or thread ID is invalid.

Definition at line 439 of file asciichat_errno.c.

439 {
440 for (int i = 0; i < MAX_THREAD_ERRORS; i++) {
441 if (thread_errors[i].valid && thread_errors[i].thread_id == thread_id) {
442 return thread_errors[i].error_code;
443 }
444 }
445 return ASCIICHAT_OK;
446}

References ASCIICHAT_OK, MAX_THREAD_ERRORS, thread_id, and valid.

◆ asciichat_has_errno()

bool asciichat_has_errno ( asciichat_error_context_t context)

#include <asciichat_errno.h>

Check if error occurred and get full context.

Parameters
contextOutput structure for error context (must not be NULL)
Returns
true if error occurred, false if no error

Checks if an error has occurred and copies the full error context into the provided structure. This is the low-level function used by HAS_ERRNO().

Note
Returns false if no error has occurred (asciichat_errno == ASCIICHAT_OK).
If error occurred, context structure is filled with all error information.
Thread-safe: Uses thread-local storage.
Warning
Use HAS_ERRNO() macro instead of calling this directly.

Definition at line 229 of file asciichat_errno.c.

229 {
231 return false;
232 }
233
234 if (context) {
235 *context = asciichat_errno_context;
236 }
237
238 return true;
239}

References asciichat_errno_context, ASCIICHAT_OK, and asciichat_error_context_t::code.

◆ asciichat_has_wsa_error()

bool asciichat_has_wsa_error ( void  )

#include <asciichat_errno.h>

Check if current error has WSA error code.

Returns
true if error has WSA error code, false otherwise

Checks whether the current thread-local error context includes a Windows socket error (WSA error) code. Useful for platform-specific error handling.

Note
Only meaningful on Windows platforms.
Returns false if no error occurred or error doesn't have WSA code.
Thread-safe: Uses thread-local storage.

Definition at line 220 of file asciichat_errno.c.

220 {
222}

References asciichat_errno_context, and asciichat_error_context_t::has_wsa_error.

◆ asciichat_print_error_context()

void asciichat_print_error_context ( const asciichat_error_context_t context)

#include <asciichat_errno.h>

Print full error context to stderr.

Parameters
contextError context to print (must not be NULL)

Prints comprehensive error information including error code, location, context message, system errors, and stack trace (if available) to stderr. Useful for debugging and error reporting.

Note
Stack trace is only printed in debug builds.
System errors (errno, WSA) are included if present.
Timestamp and location information are included.

Definition at line 327 of file asciichat_errno.c.

327 {
328 if (!context || context->code == ASCIICHAT_OK) {
329 return;
330 }
331
332 if (context->file && context->line && context->function) {
333 log_plain(" Location: %s:%d in %s()", extract_project_relative_path(context->file), context->line,
334 context->function);
335 } else {
336 log_plain(" Location: unknown (set by system code)");
337 }
338
339 if (context->context_message) {
341 context->context_message);
342 log_file(" Context: %s", context->context_message);
343 }
344
345 if (context->has_system_error) {
346 log_plain(" System error: %s (code: %d, meaning: %s)", SAFE_STRERROR(context->system_errno), context->system_errno,
347 SAFE_STRERROR(context->system_errno));
348 }
349
350 // Print timestamp
351 if (context->timestamp > 0) {
352 time_t sec = (time_t)(context->timestamp / 1000000);
353 long usec = (long)(context->timestamp % 1000000);
354 struct tm tm_info;
355 if (platform_localtime(&sec, &tm_info) == ASCIICHAT_OK) {
356 char time_str[64];
357 (void)strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", &tm_info);
358 log_plain(" Timestamp: %s.%06ld", time_str, usec);
359 }
360 }
361
362 // Print stack trace from library error
363 if (context->stack_depth > 0 && context->backtrace_symbols) {
364 platform_print_backtrace_symbols("\nBacktrace from library error", context->backtrace_symbols, context->stack_depth,
365 0, 0, skip_backtrace_frame);
366 }
367}
#define SAFE_STRERROR(errnum)
Definition common.h:385
const char * log_level_color(log_color_t color)
Get color string for a given color enum.
#define log_file(...)
File-only logging - writes to log file only, no stderr output.
@ LOG_COLOR_RESET
@ LOG_COLOR_WARN
uint64_t timestamp
Timestamp when error occurred (microseconds since epoch)
int stack_depth
Number of stack frames captured (0 if not captured)

References ASCIICHAT_OK, asciichat_error_context_t::backtrace_symbols, asciichat_error_context_t::code, asciichat_error_context_t::context_message, extract_project_relative_path(), asciichat_error_context_t::file, asciichat_error_context_t::function, asciichat_error_context_t::has_system_error, asciichat_error_context_t::line, LOG_COLOR_RESET, LOG_COLOR_WARN, log_file, log_level_color(), log_plain, platform_localtime(), platform_print_backtrace_symbols(), safe_fprintf(), SAFE_STRERROR, asciichat_error_context_t::stack_depth, asciichat_error_context_t::system_errno, and asciichat_error_context_t::timestamp.

Referenced by asciichat_fatal_with_context().

◆ asciichat_set_errno()

void asciichat_set_errno ( asciichat_error_t  code,
const char *  file,
int  line,
const char *  function,
const char *  context_message 
)

#include <asciichat_errno.h>

Set error code with basic context.

Parameters
codeError code to set (asciichat_error_t)
fileSource file name (can be NULL)
lineLine number (0 if not provided)
functionFunction name (can be NULL)
context_messageCustom context message (can be NULL, will be copied)

Sets the thread-local error code with basic context information. This is the low-level function used by SET_ERRNO() macros.

Note
In debug builds, automatically captures stack trace if enabled.
Context message is copied if provided (caller can free original).
Thread-safe: Uses thread-local storage (no synchronization needed).
Warning
Use SET_ERRNO() macros instead of calling this directly.

Definition at line 117 of file asciichat_errno.c.

118 {
119 // Suppress error context allocation during cleanup to prevent leaks
120 if (g_suppress_error_context) {
121 return;
122 }
123
124 // Clear any existing context message
128 }
129
130 // Set the error context
135 asciichat_errno_context.timestamp = get_timestamp_microseconds();
137
138 // Set the simple error code variable
139 asciichat_errno = code;
140
141 // Copy context message if provided
142 if (context_message == NULL) {
143 log_error("context_message is NULL");
144 const char *fallback = "No context message (this is invalid - set a context message)";
145 size_t len = strlen(fallback) + 1;
149 } else {
150 log_error("SAFE_MALLOC failed for fallback context_message");
151 }
152 } else {
153 size_t len = strlen(context_message) + 1;
157 } else {
158 log_error("SAFE_MALLOC failed for context_message");
159 }
160 }
161
162 // Capture stack trace in debug builds
166 }
169
170 // Record in statistics
172}
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:358
#define SAFE_MALLOC(size, cast)
Definition common.h:208
__thread asciichat_error_t asciichat_errno
Thread-local current error code.
void asciichat_error_stats_record(asciichat_error_t code)
Record an error in statistics.
void * backtrace[32]
Stack trace addresses (debug builds only)

References asciichat_errno, asciichat_errno_context, asciichat_error_stats_record(), asciichat_error_context_t::backtrace, asciichat_error_context_t::backtrace_symbols, asciichat_error_context_t::code, asciichat_error_context_t::context_message, asciichat_error_context_t::file, asciichat_error_context_t::function, asciichat_error_context_t::has_system_error, asciichat_error_context_t::line, log_error, platform_backtrace_symbols_free(), SAFE_FREE, SAFE_MALLOC, SAFE_STRNCPY, asciichat_error_context_t::stack_depth, and asciichat_error_context_t::timestamp.

Referenced by asciichat_set_errno_with_message(), asciichat_set_errno_with_system_error(), asciichat_set_errno_with_system_error_and_message(), and asciichat_set_errno_with_wsa_error().

◆ asciichat_set_errno_with_message()

void asciichat_set_errno_with_message ( asciichat_error_t  code,
const char *  file,
int  line,
const char *  function,
const char *  format,
  ... 
)

#include <asciichat_errno.h>

Set error code with formatted message.

Parameters
codeError code to set (asciichat_error_t)
fileSource file name (can be NULL)
lineLine number (0 if not provided)
functionFunction name (can be NULL)
formatFormat string (printf-style)
...Format arguments

Sets the thread-local error code with a formatted context message. This is the low-level function used by SET_ERRNO() macros.

Note
Message is formatted using vsnprintf and stored in error context.
In debug builds, automatically captures stack trace if enabled.
Thread-safe: Uses thread-local storage.
Warning
Use SET_ERRNO() macros instead of calling this directly.

Definition at line 174 of file asciichat_errno.c.

175 {
176 va_list args;
177 va_start(args, format);
178
179 char *context_message = format_message(format, args);
180 asciichat_set_errno(code, file, line, function, context_message);
181
182 if (context_message) {
183 SAFE_FREE(context_message);
184 }
185
186 va_end(args);
187}
void asciichat_set_errno(asciichat_error_t code, const char *file, int line, const char *function, const char *context_message)
Set error code with basic context.

References asciichat_set_errno(), format_message(), and SAFE_FREE.

◆ asciichat_set_errno_with_system_error()

void asciichat_set_errno_with_system_error ( asciichat_error_t  code,
const char *  file,
int  line,
const char *  function,
int  sys_errno 
)

#include <asciichat_errno.h>

Set error code with system error (errno)

Parameters
codeError code to set (asciichat_error_t)
fileSource file name (can be NULL)
lineLine number (0 if not provided)
functionFunction name (can be NULL)
sys_errnoSystem errno value

Sets the thread-local error code with system error context. Captures the system errno value for detailed error reporting.

Note
System errno is stored in error context for later retrieval.
In debug builds, automatically captures stack trace if enabled.
Thread-safe: Uses thread-local storage.
Warning
Use SET_ERRNO_SYS() macro instead of calling this directly.

Definition at line 189 of file asciichat_errno.c.

190 {
191 asciichat_set_errno(code, file, line, function, NULL);
194}

References asciichat_errno_context, asciichat_set_errno(), asciichat_error_context_t::has_system_error, and asciichat_error_context_t::system_errno.

◆ asciichat_set_errno_with_system_error_and_message()

void asciichat_set_errno_with_system_error_and_message ( asciichat_error_t  code,
const char *  file,
int  line,
const char *  function,
int  sys_errno,
const char *  format,
  ... 
)

#include <asciichat_errno.h>

Set error code with system error and formatted message.

Parameters
codeError code to set (asciichat_error_t)
fileSource file name (can be NULL)
lineLine number (0 if not provided)
functionFunction name (can be NULL)
sys_errnoSystem errno value
formatFormat string (printf-style)
...Format arguments

Sets the thread-local error code with both system error context and a formatted message. This is the low-level function used by SET_ERRNO_SYS().

Note
Combines system error capture with formatted message.
In debug builds, automatically captures stack trace if enabled.
Thread-safe: Uses thread-local storage.
Warning
Use SET_ERRNO_SYS() macro instead of calling this directly.

Definition at line 196 of file asciichat_errno.c.

197 {
198 va_list args;
199 va_start(args, format);
200
201 char *context_message = format_message(format, args);
202 asciichat_set_errno(code, file, line, function, context_message);
205
206 if (context_message) {
207 SAFE_FREE(context_message);
208 }
209
210 va_end(args);
211}

References asciichat_errno_context, asciichat_set_errno(), format_message(), asciichat_error_context_t::has_system_error, SAFE_FREE, and asciichat_error_context_t::system_errno.

◆ asciichat_set_errno_with_wsa_error()

void asciichat_set_errno_with_wsa_error ( asciichat_error_t  code,
const char *  file,
int  line,
const char *  function,
int  wsa_error 
)

#include <asciichat_errno.h>

Set error code with Windows socket error (WSA error)

Parameters
codeError code to set (asciichat_error_t)
fileSource file name (can be NULL)
lineLine number (0 if not provided)
functionFunction name (can be NULL)
wsa_errorWindows socket error code

Sets the thread-local error code with Windows-specific socket error context. Used for Windows socket operations that fail with WSA error codes.

Note
WSA error is stored in error context for later retrieval.
This function is only useful on Windows platforms.
Thread-safe: Uses thread-local storage.

Definition at line 213 of file asciichat_errno.c.

214 {
215 asciichat_set_errno(code, file, line, function, NULL);
218}

References asciichat_errno_context, asciichat_set_errno(), asciichat_error_context_t::has_wsa_error, and asciichat_error_context_t::wsa_error.

Referenced by accept_with_timeout().

◆ asciichat_set_thread_error()

void asciichat_set_thread_error ( int  thread_id,
asciichat_error_t  code 
)

#include <asciichat_errno.h>

Set error code for a specific thread.

Parameters
thread_idThread ID to set error for
codeError code to set (asciichat_error_t)

Sets the error code for a specific thread. Useful for propagating errors from one thread to another or storing thread-specific error states.

Note
Thread ID must be valid (positive integer).
This is separate from thread-local error state (asciichat_errno).

Definition at line 448 of file asciichat_errno.c.

448 {
449 // Find existing entry or empty slot
450 int slot = -1;
451 for (int i = 0; i < MAX_THREAD_ERRORS; i++) {
452 if (thread_errors[i].valid && thread_errors[i].thread_id == thread_id) {
453 slot = i;
454 break;
455 }
456 if (!thread_errors[i].valid && slot == -1) {
457 slot = i;
458 }
459 }
460
461 if (slot >= 0) {
462 thread_errors[slot].thread_id = thread_id;
463 thread_errors[slot].error_code = code;
464 thread_errors[slot].valid = true;
465 }
466}

References MAX_THREAD_ERRORS, thread_id, and valid.

Variable Documentation

◆ asciichat_errno

__thread asciichat_error_t asciichat_errno
extern

#include <asciichat_errno.h>

Thread-local current error code.

Current error code for the calling thread. Set to ASCIICHAT_OK when no error. Updated automatically when errors are set via SET_ERRNO() macros.

Note
Use GET_ERRNO() macro to read the current error code.
Use CLEAR_ERRNO() macro to clear the error state.

Definition at line 39 of file asciichat_errno.c.

Referenced by accept_with_timeout(), and asciichat_set_errno().

◆ asciichat_errno_context

__thread asciichat_error_context_t asciichat_errno_context
extern

#include <asciichat_errno.h>

Thread-local error context storage.

Each thread has its own independent error context. This ensures thread-safe error handling without synchronization overhead.

Note
Use HAS_ERRNO() macro to check for errors and get context.
Context is automatically updated when errors are set via SET_ERRNO().

Definition at line 27 of file asciichat_errno.c.

27 {.code = ASCIICHAT_OK,
28 .file = NULL,
29 .line = 0,
30 .function = NULL,
31 .context_message = NULL,
32 .timestamp = 0,
33 .system_errno = 0,
34 .backtrace = {0},
35 .backtrace_symbols = NULL,
36 .stack_depth = 0,
37 .has_system_error = false};

Referenced by accept_with_timeout(), asciichat_clear_errno(), asciichat_errno_cleanup(), asciichat_fatal_with_context(), asciichat_get_errno(), asciichat_has_errno(), asciichat_has_wsa_error(), asciichat_set_errno(), asciichat_set_errno_with_system_error(), asciichat_set_errno_with_system_error_and_message(), and asciichat_set_errno_with_wsa_error().