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

⚙️ Code shared throughout the library More...

Files

file  common.c
 🔧 Core utilities: memory management, safe macros, and cross-platform helpers
 
file  common.h
 ⚙️ Common definitions, error codes, macros, and types shared throughout the application
 
file  fps.c
 ⏱️ FPS tracking utility implementation
 
file  fps.h
 ⏱️ FPS tracking utility for monitoring frame throughput across all threads
 

Data Structures

struct  fps_t
 FPS tracking state. More...
 

Macros

#define ASCII_CHAT_APP_NAME   "ascii-chat"
 Application name for key comments ("ascii-chat")
 
#define PLATFORM_MAX_PATH_LENGTH   4096
 
#define FATAL(code, ...)   asciichat_fatal_with_context(code, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
 Exit with error code and custom message, with stack trace in debug builds.
 
#define ALLOC_MALLOC(size)   malloc(size)
 Return the minimum of two values.
 
#define ALLOC_CALLOC(count, size)   calloc((count), (size))
 
#define ALLOC_REALLOC(ptr, size)   realloc((ptr), (size))
 
#define ALLOC_FREE(ptr)   free(ptr)
 
#define ASCIICHAT_CHECK_AND_LOG(expr, ok_value, msg, ...)
 Check result and log error if operation failed.
 

Typedefs

typedef unsigned char uint8_t
 
typedef unsigned short uint16_t
 
typedef unsigned int uint32_t
 
typedef unsigned long long uint64_t
 

Functions

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)
 
asciichat_error_t asciichat_shared_init (const char *default_log_filename, bool is_client)
 Initialize common subsystems shared by client and server.
 
void fps_init (fps_t *tracker, int expected_fps, const char *name)
 Initialize FPS tracker.
 
void fps_init_with_interval (fps_t *tracker, int expected_fps, const char *name, uint64_t report_interval_us)
 Initialize FPS tracker with custom report interval.
 
void fps_frame (fps_t *tracker, const struct timespec *current_time, const char *context)
 Track a frame and detect lag conditions.
 

Memory Debugging Macros

#define SAFE_MALLOC(size, cast)
 
#define SAFE_CALLOC(count, size, cast)
 
#define SAFE_REALLOC(ptr, size, cast)
 
#define TRACK_ALIGNED_ALLOC(ptr, size, file, line)   ((void)0)
 
#define SAFE_MALLOC_ALIGNED(size, alignment, cast)
 
#define SAFE_MALLOC_SIMD(size, cast)   SAFE_MALLOC_ALIGNED(size, 16, cast)
 
#define SAFE_CALLOC_SIMD(count, size, cast)
 
#define SAFE_FREE(ptr)
 
#define SAFE_FCLOSE(fp)
 
#define UNTRACKED_MALLOC(size, cast)
 
#define UNTRACKED_FREE(ptr)
 
#define SAFE_STRNCPY(dst, src, size)   platform_strlcpy((dst), (src), (size))
 
#define SAFE_STRDUP(dst, src)
 
#define SAFE_GETENV(name)   platform_getenv(name)
 
#define SAFE_SSCANF(str, format, ...)   sscanf(str, format, __VA_ARGS__)
 
#define SAFE_STRERROR(errnum)   platform_strerror(errnum)
 
#define SAFE_MEMCPY(dest, dest_size, src, count)   platform_memcpy((dest), (dest_size), (src), (count))
 
#define SAFE_MEMSET(dest, dest_size, ch, count)   platform_memset((dest), (dest_size), (ch), (count))
 
#define SAFE_MEMMOVE(dest, dest_size, src, count)   platform_memmove((dest), (dest_size), (src), (count))
 
#define SAFE_STRCPY(dest, dest_size, src)   platform_strcpy((dest), (dest_size), (src))
 
#define read_u16_unaligned   bytes_read_u16_unaligned
 
#define read_u32_unaligned   bytes_read_u32_unaligned
 
#define write_u16_unaligned   bytes_write_u16_unaligned
 
#define write_u32_unaligned   bytes_write_u32_unaligned
 
#define safe_size_mul   bytes_safe_size_mul
 
#define SAFE_SNPRINTF(buffer, buffer_size, ...)   (size_t)safe_snprintf((buffer), (buffer_size), __VA_ARGS__)
 
#define SAFE_BUFFER_SIZE(buffer_size, offset)    ((offset) < 0 || (size_t)(offset) >= (buffer_size) ? 0 : (buffer_size) - (size_t)(offset))
 Safe buffer size calculation for snprintf.
 
#define THREAD_CREATE_OR_RETURN(thread, func, arg)
 Create a thread or log error and return.
 
#define MUTEX_INIT_OR_RETURN(m)
 Initialize a mutex or log error and return.
 

Detailed Description

⚙️ Code shared throughout the library

This header provides core functionality used throughout ascii-chat:

Note
This header should be included early in other headers as it provides fundamental types and macros that many other modules depend on.

Provides a reusable FPS tracking mechanism for monitoring real-time frame delivery rates across all client threads (data reception, webcam capture, audio capture, keepalive). Tracks frame intervals, detects lag conditions, and generates periodic FPS reports every 7-15 seconds.

USAGE:

fps_t tracker;
fps_init(&tracker, 60, "WEBCAM"); // 60 FPS expected
// In your frame processing loop
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
fps_frame(&tracker, &now, "frame received");
void fps_frame(fps_t *tracker, const struct timespec *current_time, const char *context)
Track a frame and detect lag conditions.
Definition fps.c:57
void fps_init(fps_t *tracker, int expected_fps, const char *name)
Initialize FPS tracker.
Definition fps.c:36
FPS tracking state.
Definition fps.h:51

FEATURES:

Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
December 2025
Version
2.0 (Refactored with unified thread tracking)

Common Definitions

Core error handling, memory management, logging, and type definitions shared throughout the ascii-chat application.

Overview

The common module (lib/common.h, lib/common.c) provides fundamental infrastructure:

  • Comprehensive error handling with context tracking
  • Memory allocation with leak detection in debug builds
  • Rate-limited logging for high-frequency code paths
  • Platform-independent type definitions
  • Shared constants and macros

All application code should use these common definitions to ensure consistency, proper error handling, and debuggability.

Error Handling System

ascii-chat uses a comprehensive error system (lib/asciichat_errno.h) that provides:

  • Specific error codes for different failure modes
  • Contextual error messages with file/line/function information
  • System error integration (errno on POSIX, GetLastError on Windows)
  • Thread-local error state (thread-safe)
  • Stack traces in debug builds

Error Code Enumeration

typedef enum {
// Standard codes (0-2) - Unix conventions
ASCIICHAT_OK = 0, // Success
ERROR_GENERAL = 1, // Unspecified error
ERROR_USAGE = 2, // Invalid command line arguments
// Initialization failures (3-19)
ERROR_MEMORY = 3, // Memory allocation failed (OOM)
ERROR_CONFIG = 4, // Configuration file or settings error
ERROR_CRYPTO_INIT = 5, // Cryptographic initialization failed
ERROR_LOGGING_INIT = 6, // Logging system initialization failed
ERROR_PLATFORM_INIT = 7, // Platform-specific initialization failed
// Hardware/Device errors (20-39)
ERROR_WEBCAM = 20, // Webcam initialization or capture failed
ERROR_WEBCAM_IN_USE = 21, // Webcam is in use by another application
ERROR_WEBCAM_PERMISSION = 22, // Webcam permission denied
ERROR_AUDIO = 23, // Audio device initialization or I/O failed
ERROR_AUDIO_IN_USE = 24, // Audio device is in use
ERROR_TERMINAL = 25, // Terminal initialization failed
// Network errors (40-59)
ERROR_NETWORK = 40, // General network error
ERROR_NETWORK_BIND = 41, // Cannot bind to port (server)
ERROR_NETWORK_CONNECT = 42, // Cannot connect to server (client)
ERROR_NETWORK_TIMEOUT = 43, // Network operation timed out
ERROR_NETWORK_PROTOCOL = 44, // Protocol violation or incompatible version
ERROR_NETWORK_SIZE = 45, // Network packet size error
// Security/Crypto errors (60-79)
ERROR_CRYPTO = 60, // Cryptographic operation failed
ERROR_CRYPTO_KEY = 61, // Key loading, parsing, or generation failed
ERROR_CRYPTO_AUTH = 62, // Authentication failed
ERROR_CRYPTO_HANDSHAKE = 63, // Cryptographic handshake failed
ERROR_CRYPTO_VERIFICATION = 64, // Signature or key verification failed
// Runtime errors (80-99)
ERROR_THREAD = 80, // Thread creation or management failed
ERROR_BUFFER = 81, // Buffer allocation or overflow
ERROR_BUFFER_FULL = 82, // Buffer full
ERROR_BUFFER_OVERFLOW = 83, // Buffer overflow
ERROR_DISPLAY = 84, // Display rendering or output error
ERROR_INVALID_STATE = 85, // Invalid program state
ERROR_INVALID_PARAM = 86, // Invalid parameter
ERROR_INVALID_FRAME = 87, // Invalid frame data
ERROR_RESOURCE_EXHAUSTED = 88, // System resources exhausted
ERROR_FORMAT = 89, // String formatting operation failed
ERROR_STRING = 90, // String manipulation operation failed
// Signal/Crash handlers (100-127)
ERROR_SIGNAL_INTERRUPT = 100, // Interrupted by signal (SIGINT, SIGTERM)
ERROR_SIGNAL_CRASH = 101, // Fatal signal (SIGSEGV, SIGABRT, etc.)
ERROR_ASSERTION_FAILED = 102, // Assertion or invariant violation
// Compression errors (103-104)
ERROR_COMPRESSION = 103, // Compression operation failed
ERROR_DECOMPRESSION = 104, // Decompression operation failed
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:46
@ ERROR_LOGGING_INIT
Definition error_codes.h:56
@ ERROR_INVALID_STATE
@ ERROR_CRYPTO_HANDSHAKE
Definition error_codes.h:91
@ ERROR_NETWORK_CONNECT
Definition error_codes.h:71
@ ERROR_CRYPTO_KEY
Definition error_codes.h:89
@ ERROR_SIGNAL_INTERRUPT
@ ERROR_COMPRESSION
@ ERROR_NETWORK_BIND
Definition error_codes.h:70
@ ERROR_NETWORK
Definition error_codes.h:69
@ ERROR_PLATFORM_INIT
Definition error_codes.h:57
@ ERROR_WEBCAM_IN_USE
Definition error_codes.h:62
@ ERROR_CRYPTO_VERIFICATION
Definition error_codes.h:92
@ ERROR_RESOURCE_EXHAUSTED
@ ERROR_BUFFER_FULL
Definition error_codes.h:97
@ ERROR_AUDIO
Definition error_codes.h:64
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:73
@ ERROR_DECOMPRESSION
@ ERROR_MEMORY
Definition error_codes.h:53
@ ASCIICHAT_OK
Definition error_codes.h:48
@ ERROR_GENERAL
Definition error_codes.h:49
@ ERROR_STRING
@ ERROR_NETWORK_SIZE
Definition error_codes.h:74
@ ERROR_CRYPTO_AUTH
Definition error_codes.h:90
@ ERROR_WEBCAM_PERMISSION
Definition error_codes.h:63
@ ERROR_DISPLAY
Definition error_codes.h:99
@ ERROR_CRYPTO_INIT
Definition error_codes.h:55
@ ERROR_INVALID_FRAME
@ ERROR_CONFIG
Definition error_codes.h:54
@ ERROR_CRYPTO
Definition error_codes.h:88
@ ERROR_SIGNAL_CRASH
@ ERROR_USAGE
Definition error_codes.h:50
@ ERROR_INVALID_PARAM
@ ERROR_BUFFER_OVERFLOW
Definition error_codes.h:98
@ ERROR_AUDIO_IN_USE
Definition error_codes.h:65
@ ERROR_TERMINAL
Definition error_codes.h:66
@ ERROR_ASSERTION_FAILED
@ ERROR_NETWORK_TIMEOUT
Definition error_codes.h:72
@ ERROR_WEBCAM
Definition error_codes.h:61
@ ERROR_BUFFER
Definition error_codes.h:96
@ ERROR_THREAD
Definition error_codes.h:95
@ ERROR_FORMAT

Error Context

Each error includes rich contextual information:

typedef struct {
asciichat_error_t code; // Error code (asciichat_error_t enum value)
const char *file; // Source file where error occurred (NULL in release)
int line; // Line number where error occurred (0 in release)
const char *function; // Function name where error occurred (NULL in release)
char *context_message; // Optional custom message (dynamically allocated)
uint64_t timestamp; // Timestamp when error occurred (microseconds)
int system_errno; // System errno value (if applicable, 0 otherwise)
int wsa_error; // Windows socket error code (if applicable)
void *backtrace[32]; // Stack trace addresses (debug builds only)
char **backtrace_symbols; // Stack trace symbol strings (debug builds only)
int stack_depth; // Number of stack frames captured
bool has_system_error; // True if system_errno is valid
bool has_wsa_error; // True if wsa_error is valid
unsigned long long uint64_t
Definition common.h:59
Error context structure.

Error Handling Macros

SET_ERRNO: Set error with custom message

asciichat_error_t open_file(const char *path) {
if (!path) {
return SET_ERRNO(ERROR_INVALID_PARAM, "File path is NULL");
}
int fd = open(path, O_RDONLY);
if (fd < 0) {
return SET_ERRNO_SYS(ERROR_NOT_FOUND, "Failed to open file: %s", path);
}
return ASCIICHAT_OK;
}
#define SET_ERRNO_SYS(code, context_msg,...)
Set error code with custom message and system error context.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
@ ERROR_NOT_FOUND

SET_ERRNO_SYS: Set error with system errno

asciichat_error_t create_socket(void) {
socket_t sockfd = socket(AF_INET6, SOCK_STREAM, 0);
if (sockfd == INVALID_SOCKET_VALUE) {
return SET_ERRNO_SYS(ERROR_NETWORK_SOCKET,
"socket() failed (errno=%d)", errno);
}
return ASCIICHAT_OK;
}
int socket_t
Socket handle type (POSIX: int)
Definition socket.h:50
#define INVALID_SOCKET_VALUE
Invalid socket value (POSIX: -1)
Definition socket.h:52
int errno

HAS_ERRNO: Check if error occurred

asciichat_error_t result = risky_operation();
if (HAS_ERRNO(&err_ctx)) {
log_error("Operation failed: %s (code=%d, file=%s:%d)",
err_ctx.context_message, err_ctx.error_code,
err_ctx.file, err_ctx.line);
return result;
}
#define HAS_ERRNO(var)
Check if an error occurred and get full context.
#define log_error(...)
Log an ERROR message.
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)

PRINT_ERRNO_CONTEXT: Debug print error details

if (HAS_ERRNO(&err_ctx)) {
PRINT_ERRNO_CONTEXT(&err_ctx); // Only in debug builds
// Output:
// Error: ERROR_NETWORK_CONNECT (code=13)
// Message: Failed to connect to server (Connection refused)
// Location: src/client.c:245 in connect_to_server()
// System errno: 111 (Connection refused)
// Timestamp: 1234567890123456 µs
}
#define PRINT_ERRNO_CONTEXT(context)

CLEAR_ERRNO: Clear error state

CLEAR_ERRNO(); // Resets thread-local error state
#define CLEAR_ERRNO()
Clear the current error state.

GET_ERRNO: 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)

Memory Management

Custom memory allocation macros with automatic leak detection in debug builds:

Memory Allocation Macros

SAFE_MALLOC: Allocate memory with leak tracking

// Syntax: SAFE_MALLOC(size, cast_type)
uint8_t *buffer = SAFE_MALLOC(1024, uint8_t*);
if (!buffer) {
return SET_ERRNO(ERROR_MEMORY, "Failed to allocate 1024 bytes");
}
// In debug builds, records:
// - Allocation address
// - Size (1024 bytes)
// - File (src/client.c)
// - Line (123)
// - Thread ID
#define SAFE_MALLOC(size, cast)
Definition common.h:208
unsigned char uint8_t
Definition common.h:56

SAFE_CALLOC: Allocate zeroed memory

// Allocate array of 100 integers, initialized to 0
int *array = SAFE_CALLOC(100, sizeof(int), int*);
if (!array) {
return ERROR_MEMORY;
}
#define SAFE_CALLOC(count, size, cast)
Definition common.h:218

SAFE_REALLOC: Reallocate memory

// Grow buffer from 1024 to 2048 bytes
uint8_t *new_buffer = SAFE_REALLOC(buffer, 2048, uint8_t*);
if (!new_buffer) {
// Original buffer still valid
SAFE_FREE(buffer);
return ERROR_MEMORY;
}
buffer = new_buffer;
#define SAFE_REALLOC(ptr, size, cast)
Definition common.h:228
#define SAFE_FREE(ptr)
Definition common.h:320

SAFE_FREE: Free memory and set to NULL

SAFE_FREE(buffer); // Frees and sets buffer = NULL
// In debug builds, removes from leak tracker

Debug Memory Tracking

When CMAKE_BUILD_TYPE=Debug and DEBUG_MEMORY is defined:

Leak Detection:

  • All SAFE_MALLOC/CALLOC/REALLOC calls are recorded
  • Each allocation stores: address, size, file, line, timestamp
  • On program exit, reports any allocations not freed
  • Example output:
    Memory leak detected:
    Address: 0x7f1234567890
    Size: 1024 bytes
    Allocated at: src/client.c:123 in process_frame()
    Thread: 0x7f123456
    Total leaked: 1024 bytes in 1 allocation

Allocation Logging:

#define DEBUG_MEMORY 1
// Every allocation is logged:
// [MEMORY] malloc(1024) = 0x7f1234567890 at src/client.c:123
// [MEMORY] free(0x7f1234567890) at src/client.c:456

Double-Free Detection:

  • Freeing already-freed memory triggers assertion
  • Prevents use-after-free bugs

Memory Reports:

void asciichat_common_print_memory_stats(void);
// Prints:
// Current allocations: 5 (total 4096 bytes)
// Peak allocations: 10 (total 8192 bytes)
// Total malloc calls: 100
// Total free calls: 95

Logging System

Multi-level logging with rate limiting for high-frequency code:

Log Levels

typedef enum {
LOG_DEBUG = 0, // Verbose debug information
LOG_INFO = 1, // Informational messages
LOG_WARN = 2, // Warnings (non-fatal issues)
LOG_ERROR = 3, // Errors (operation failed)
LOG_FATAL = 4, // Fatal errors (program will exit)
log_level_t
Logging levels enumeration.
Definition log/logging.h:59
@ LOG_ERROR
Definition log/logging.h:64
@ LOG_INFO
Definition log/logging.h:62
@ LOG_DEBUG
Definition log/logging.h:61
@ LOG_FATAL
Definition log/logging.h:65
@ LOG_WARN
Definition log/logging.h:63

Control via environment:

export LOG_LEVEL=0 # DEBUG (all messages)
export LOG_LEVEL=1 # INFO and above
export LOG_LEVEL=2 # WARN and above
export LOG_LEVEL=3 # ERROR and above
export LOG_LEVEL=4 # FATAL only

Standard Logging Macros

log_debug("Frame captured: %dx%d", width, height);
log_info("Client connected from %s:%d", ip, port);
log_warn("Audio buffer underrun detected");
log_error("Failed to send packet: %s", strerror(errno));
log_fatal("Critical error, terminating"); // Exits program
#define log_warn(...)
Log a WARN message.
#define log_fatal(...)
Log a FATAL message.
#define log_info(...)
Log an INFO message.
#define log_debug(...)
Log a DEBUG message.

Output format:

[2025-01-15 14:32:45.123] [DEBUG] Frame captured: 1920x1080
[2025-01-15 14:32:45.456] [INFO] Client connected from 192.168.1.100:12345
[2025-01-15 14:32:45.789] [WARN] Audio buffer underrun detected
[2025-01-15 14:32:46.012] [ERROR] Failed to send packet: Broken pipe
[2025-01-15 14:32:46.345] [FATAL] Critical error, terminating
#define FATAL(code,...)
Exit with error code and custom message, with stack trace in debug builds.
Definition common.h:151

Rate-Limited Logging

For high-frequency code paths (e.g., video/audio processing at 60+ FPS):

// Log at most once per second (1000000 µs)
log_debug_every(1000000, "Processing frame %d", frame_count);
// Log at most once per 5 seconds
log_info_every(5000000, "Client status: %d connected", num_clients);
// Log at most once per 10 seconds
log_warn_every(10000000, "High CPU usage: %.1f%%", cpu_usage);
#define log_info_every(interval_us, fmt,...)
Rate-limited INFO logging.
#define log_debug_every(interval_us, fmt,...)
Rate-limited DEBUG logging.
#define log_warn_every(interval_us, fmt,...)
Rate-limited WARN logging.

Implementation:

  • Uses thread-local timestamp tracking
  • First call always logs
  • Subsequent calls within interval are suppressed
  • Different log sites have independent rate limiting

Example (video processing loop at 60 FPS):

while (running) {
capture_frame();
// Without rate limiting: 60 log messages per second
// log_debug("Frame captured");
// With rate limiting: 1 log message per second
log_debug_every(1000000, "Frame captured (count=%d)", frame_count);
frame_count++;
}

Logging Configuration

// Initialize logging system
// Change log level at runtime
// Enable/disable color output
void log_set_color(bool enable);
// Cleanup logging
void log_destroy(void);
void log_destroy(void)
Destroy the logging system and close log file.
void log_set_level(log_level_t level)
Set the minimum log level.
void log_init(const char *filename, log_level_t level, bool force_stderr, bool use_mmap)
Initialize the logging system.
#define log_file(...)
File-only logging - writes to log file only, no stderr output.

Usage:

// Log to file and stderr
log_init("/tmp/ascii-chat.log", LOG_INFO);
// Later: increase verbosity
// Cleanup

Common Type Definitions

Fixed-width integers (platform-independent):

#include <stdint.h>
int8_t, uint8_t // 8-bit signed/unsigned
int16_t, uint16_t // 16-bit signed/unsigned
int32_t, uint32_t // 32-bit signed/unsigned
int64_t, uint64_t // 64-bit signed/unsigned
size_t // Unsigned size type (32 or 64-bit)
ssize_t // Signed size type
unsigned short uint16_t
Definition common.h:57
unsigned int uint32_t
Definition common.h:58

Boolean (C99 style):

#include <stdbool.h>
bool, true, false
#define bool
Definition stdbool.h:22

NULL pointer:

#define NULL ((void*)0)

Common Constants

// Buffer sizes
#define MAX_PACKET_SIZE 65536 // Maximum network packet payload
#define MAX_PATH_LEN 4096 // Maximum path length
#define MAX_CLIENTS 9 // Maximum simultaneous clients
// Network
#define DEFAULT_PORT 27224 // Default server port
#define PACKET_MAGIC 0xDEADBEEF // Packet header magic number
// Video
#define DEFAULT_WIDTH 80 // Default terminal width
#define DEFAULT_HEIGHT 24 // Default terminal height
#define DEFAULT_FPS 30 // Default framerate
// Audio
#define SAMPLE_RATE 48000 // Audio sample rate (Hz)
#define AUDIO_CHANNELS 1 // Mono audio
#define FRAMES_PER_BUFFER 512 // PortAudio buffer size

Usage Examples

Comprehensive Error Handling

asciichat_error_t initialize_system(const char *config_file) {
// Load configuration
options_t opts;
err = config_load(&opts, config_file);
if (err != ASCIICHAT_OK) {
if (HAS_ERRNO(&ctx)) {
log_error("Config load failed: %s", ctx.context_message);
}
return err;
}
// Initialize crypto
crypto_context_t crypto_ctx;
err = crypto_init(&crypto_ctx);
if (err != ASCIICHAT_OK) {
options_destroy(&opts);
return err;
}
// Initialize audio
audio_context_t audio_ctx;
err = audio_init(&audio_ctx);
if (err != ASCIICHAT_OK) {
crypto_destroy(&crypto_ctx);
options_destroy(&opts);
return err;
}
return ASCIICHAT_OK;
}
asciichat_error_t audio_init(audio_context_t *ctx)
Initialize audio context and PortAudio.
crypto_result_t crypto_init(crypto_context_t *ctx)
Initialize libsodium and crypto context.
Audio context for full-duplex capture and playback.
Cryptographic context structure.
Consolidated options structure.
Definition options.h:439

Memory Management with Leak Detection

void process_data(void) {
// Allocate buffers
uint8_t *buffer1 = SAFE_MALLOC(1024, uint8_t*);
uint8_t *buffer2 = SAFE_CALLOC(512, sizeof(uint8_t), uint8_t*);
if (!buffer1 || !buffer2) {
log_error("Memory allocation failed");
SAFE_FREE(buffer1);
SAFE_FREE(buffer2);
return;
}
// Process data...
// Grow buffer if needed
if (need_more_space) {
uint8_t *new_buffer = SAFE_REALLOC(buffer1, 2048, uint8_t*);
if (!new_buffer) {
log_error("Reallocation failed");
SAFE_FREE(buffer1);
SAFE_FREE(buffer2);
return;
}
buffer1 = new_buffer;
}
// Cleanup (SAFE_FREE sets pointers to NULL)
SAFE_FREE(buffer1);
SAFE_FREE(buffer2);
}
// At program exit (debug builds):
// If any SAFE_MALLOC'd memory wasn't freed, it's reported:
// Memory leak detected:
// Address: 0x7f1234567890
// Size: 1024 bytes
// Allocated at: src/client.c:123

Rate-Limited Logging in Video Loop

void video_processing_loop(void) {
uint64_t frame_count = 0;
uint64_t dropped_frames = 0;
while (running) {
// Capture frame (60 FPS)
uint8_t *frame_data;
asciichat_error_t err = webcam_capture_frame(webcam, &frame_data, &frame_len);
if (err != ASCIICHAT_OK) {
dropped_frames++;
// Log at most once per second to avoid spam
log_warn_every(1000000, "Frame capture failed (dropped %lu frames)",
dropped_frames);
continue;
}
// Process frame...
frame_count++;
// Log progress every 5 seconds
log_info_every(5000000, "Processed %lu frames (%.1f FPS)",
frame_count, calculate_fps());
}
}
See also
common.h
asciichat_errno.h
logging.h

Macro Definition Documentation

◆ ALLOC_CALLOC

#define ALLOC_CALLOC (   count,
  size 
)    calloc((count), (size))

#include <common.h>

Definition at line 196 of file common.h.

◆ ALLOC_FREE

#define ALLOC_FREE (   ptr)    free(ptr)

#include <common.h>

Definition at line 198 of file common.h.

◆ ALLOC_MALLOC

#define ALLOC_MALLOC (   size)    malloc(size)

#include <common.h>

Return the minimum of two values.

Return the maximum of two values

Calculate the number of elements in an array

Definition at line 195 of file common.h.

◆ ALLOC_REALLOC

#define ALLOC_REALLOC (   ptr,
  size 
)    realloc((ptr), (size))

#include <common.h>

Definition at line 197 of file common.h.

◆ ASCII_CHAT_APP_NAME

#define ASCII_CHAT_APP_NAME   "ascii-chat"

#include <common.h>

Application name for key comments ("ascii-chat")

Definition at line 43 of file common.h.

◆ ASCIICHAT_CHECK_AND_LOG

#define ASCIICHAT_CHECK_AND_LOG (   expr,
  ok_value,
  msg,
  ... 
)

#include <common.h>

Value:
do { \
if ((expr) != (ok_value)) { \
log_error(msg, ##__VA_ARGS__); \
return (expr); \
} \
} while (0)

Check result and log error if operation failed.

Consolidates the pattern:

operation_result_t result = operation();
if (result != OK) {
log_error("Operation failed: %d", result);
return result;
}

Usage:

ASCIICHAT_CHECK_AND_LOG(crypto_result = crypto_operation(args),
ASCIICHAT_OK, "Crypto operation failed: %d",
crypto_result);
#define ASCIICHAT_CHECK_AND_LOG(expr, ok_value, msg,...)
Check result and log error if operation failed.
Definition common.h:529

Definition at line 529 of file common.h.

530 { \
531 if ((expr) != (ok_value)) { \
532 log_error(msg, ##__VA_ARGS__); \
533 return (expr); \
534 } \
535 } while (0)

◆ FATAL

#define FATAL (   code,
  ... 
)    asciichat_fatal_with_context(code, __FILE__, __LINE__, __func__, ##__VA_ARGS__)

#include <common.h>

Exit with error code and custom message, with stack trace in debug builds.

Parameters
codeError code (asciichat_error_t)
...Custom message format string and arguments (printf-style)
Note
In debug builds, includes file, line, and function information. In release builds, file/line/function are omitted to reduce binary size.
Warning
This macro terminates the program. Use only for unrecoverable errors.
Example:
FATAL(ERROR_NETWORK_BIND, "Cannot bind to port %d", port_number);

Definition at line 151 of file common.h.

◆ MUTEX_INIT_OR_RETURN

#define MUTEX_INIT_OR_RETURN (   m)

#include <common.h>

Value:
do { \
if (mutex_init(&(m)) != 0) { \
log_error("Failed to initialize mutex: %s", #m); \
return -1; \
} \
} while (0)
int mutex_init(mutex_t *mutex)
Initialize a mutex.

Initialize a mutex or log error and return.

Parameters
mPointer to mutex_t variable

Handles common pattern: init mutex, log error if failed, then return -1. Example usage:

MUTEX_INIT_OR_RETURN(mutex); // Mutex initialized successfully, continue

Definition at line 462 of file common.h.

463 { \
464 if (mutex_init(&(m)) != 0) { \
465 log_error("Failed to initialize mutex: %s", #m); \
466 return -1; \
467 } \
468 } while (0)

◆ PLATFORM_MAX_PATH_LENGTH

#define PLATFORM_MAX_PATH_LENGTH   4096

#include <common.h>

Definition at line 91 of file common.h.

◆ read_u16_unaligned

#define read_u16_unaligned   bytes_read_u16_unaligned

#include <common.h>

Definition at line 404 of file common.h.

◆ read_u32_unaligned

#define read_u32_unaligned   bytes_read_u32_unaligned

#include <common.h>

Definition at line 405 of file common.h.

◆ SAFE_BUFFER_SIZE

#define SAFE_BUFFER_SIZE (   buffer_size,
  offset 
)     ((offset) < 0 || (size_t)(offset) >= (buffer_size) ? 0 : (buffer_size) - (size_t)(offset))

#include <common.h>

Safe buffer size calculation for snprintf.

Casts offset to size_t to avoid sign conversion warnings when subtracting from buffer_size. Returns 0 if offset is negative or >= buffer_size (prevents underflow).

Definition at line 421 of file common.h.

422 : (buffer_size) - (size_t)(offset))

◆ SAFE_CALLOC

#define SAFE_CALLOC (   count,
  size,
  cast 
)

#include <common.h>

Value:
({ \
cast _ptr = (cast)ALLOC_CALLOC((count), (size)); \
if (!_ptr) { \
FATAL(ERROR_MEMORY, "Memory allocation failed: %zu elements x %zu bytes", (size_t)(count), (size_t)(size)); \
} \
_ptr; \
})
#define ALLOC_CALLOC(count, size)
Definition common.h:196

Definition at line 218 of file common.h.

219 { \
220 cast _ptr = (cast)ALLOC_CALLOC((count), (size)); \
221 if (!_ptr) { \
222 FATAL(ERROR_MEMORY, "Memory allocation failed: %zu elements x %zu bytes", (size_t)(count), (size_t)(size)); \
223 } \
224 _ptr; \
225 })

◆ SAFE_CALLOC_SIMD

#define SAFE_CALLOC_SIMD (   count,
  size,
  cast 
)

#include <common.h>

Value:
({ \
size_t total_size = (count) * (size); \
cast _ptr = SAFE_MALLOC_SIMD(total_size, cast); \
memset(_ptr, 0, total_size); \
_ptr; \
})
#define SAFE_MALLOC_SIMD(size, cast)
Definition common.h:308

Definition at line 311 of file common.h.

312 { \
313 size_t total_size = (count) * (size); \
314 cast _ptr = SAFE_MALLOC_SIMD(total_size, cast); \
315 memset(_ptr, 0, total_size); \
316 _ptr; \
317 })

◆ SAFE_FCLOSE

#define SAFE_FCLOSE (   fp)

#include <common.h>

Value:
do { \
if ((fp) != NULL) { \
fclose(fp); \
(fp) = NULL; \
} \
} while (0)

Definition at line 330 of file common.h.

331 { \
332 if ((fp) != NULL) { \
333 fclose(fp); \
334 (fp) = NULL; \
335 } \
336 } while (0)

◆ SAFE_FREE

#define SAFE_FREE (   ptr)

#include <common.h>

Value:
do { \
if ((ptr) != NULL) { \
ALLOC_FREE((void *)(ptr)); \
(ptr) = NULL; \
} \
} while (0)

Definition at line 320 of file common.h.

321 { \
322 if ((ptr) != NULL) { \
323 ALLOC_FREE((void *)(ptr)); \
324 (ptr) = NULL; \
325 } \
326 } while (0)

◆ SAFE_GETENV

#define SAFE_GETENV (   name)    platform_getenv(name)

#include <common.h>

Definition at line 378 of file common.h.

◆ SAFE_MALLOC

#define SAFE_MALLOC (   size,
  cast 
)

#include <common.h>

Value:
({ \
cast _ptr = (cast)ALLOC_MALLOC(size); \
if (!_ptr) { \
FATAL(ERROR_MEMORY, "Memory allocation failed: %zu bytes", (size_t)(size)); \
} \
_ptr; \
})
#define ALLOC_MALLOC(size)
Return the minimum of two values.
Definition common.h:195

Definition at line 208 of file common.h.

209 { \
210 cast _ptr = (cast)ALLOC_MALLOC(size); \
211 if (!_ptr) { \
212 FATAL(ERROR_MEMORY, "Memory allocation failed: %zu bytes", (size_t)(size)); \
213 } \
214 _ptr; \
215 })

◆ SAFE_MALLOC_ALIGNED

#define SAFE_MALLOC_ALIGNED (   size,
  alignment,
  cast 
)

#include <common.h>

Value:
({ \
size_t aligned_size = (((size) + (alignment) - 1) / (alignment)) * (alignment); \
cast _ptr = (cast)aligned_alloc((alignment), aligned_size); \
if (!_ptr) { \
FATAL(ERROR_MEMORY, "Aligned memory allocation failed: %zu bytes, %zu alignment", aligned_size, \
(size_t)(alignment)); \
} \
TRACK_ALIGNED_ALLOC(_ptr, aligned_size, __FILE__, __LINE__); \
_ptr; \
})

Definition at line 293 of file common.h.

294 { \
295 size_t aligned_size = (((size) + (alignment) - 1) / (alignment)) * (alignment); \
296 cast _ptr = (cast)aligned_alloc((alignment), aligned_size); \
297 if (!_ptr) { \
298 FATAL(ERROR_MEMORY, "Aligned memory allocation failed: %zu bytes, %zu alignment", aligned_size, \
299 (size_t)(alignment)); \
300 } \
301 TRACK_ALIGNED_ALLOC(_ptr, aligned_size, __FILE__, __LINE__); \
302 _ptr; \
303 })

◆ SAFE_MALLOC_SIMD

#define SAFE_MALLOC_SIMD (   size,
  cast 
)    SAFE_MALLOC_ALIGNED(size, 16, cast)

#include <common.h>

Definition at line 308 of file common.h.

◆ SAFE_MEMCPY

#define SAFE_MEMCPY (   dest,
  dest_size,
  src,
  count 
)    platform_memcpy((dest), (dest_size), (src), (count))

#include <common.h>

Definition at line 388 of file common.h.

◆ SAFE_MEMMOVE

#define SAFE_MEMMOVE (   dest,
  dest_size,
  src,
  count 
)    platform_memmove((dest), (dest_size), (src), (count))

#include <common.h>

Definition at line 390 of file common.h.

◆ SAFE_MEMSET

#define SAFE_MEMSET (   dest,
  dest_size,
  ch,
  count 
)    platform_memset((dest), (dest_size), (ch), (count))

#include <common.h>

Definition at line 389 of file common.h.

◆ SAFE_REALLOC

#define SAFE_REALLOC (   ptr,
  size,
  cast 
)

#include <common.h>

Value:
({ \
void *tmp_ptr = ALLOC_REALLOC((ptr), (size)); \
if (!tmp_ptr) { \
FATAL(ERROR_MEMORY, "Memory reallocation failed: %zu bytes", (size_t)(size)); \
} \
(cast)(tmp_ptr); \
})
#define ALLOC_REALLOC(ptr, size)
Definition common.h:197

Definition at line 228 of file common.h.

229 { \
230 void *tmp_ptr = ALLOC_REALLOC((ptr), (size)); \
231 if (!tmp_ptr) { \
232 FATAL(ERROR_MEMORY, "Memory reallocation failed: %zu bytes", (size_t)(size)); \
233 } \
234 (cast)(tmp_ptr); \
235 })

◆ safe_size_mul

#define safe_size_mul   bytes_safe_size_mul

#include <common.h>

Definition at line 408 of file common.h.

◆ SAFE_SNPRINTF

#define SAFE_SNPRINTF (   buffer,
  buffer_size,
  ... 
)    (size_t)safe_snprintf((buffer), (buffer_size), __VA_ARGS__)

#include <common.h>

Definition at line 412 of file common.h.

◆ SAFE_SSCANF

#define SAFE_SSCANF (   str,
  format,
  ... 
)    sscanf(str, format, __VA_ARGS__)

#include <common.h>

Definition at line 381 of file common.h.

◆ SAFE_STRCPY

#define SAFE_STRCPY (   dest,
  dest_size,
  src 
)    platform_strcpy((dest), (dest_size), (src))

#include <common.h>

Definition at line 391 of file common.h.

◆ SAFE_STRDUP

#define SAFE_STRDUP (   dst,
  src 
)

#include <common.h>

Value:
do { \
if (src) { \
size_t _len = strlen(src) + 1; \
(dst) = SAFE_MALLOC(_len, char *); \
if (dst) { \
SAFE_MEMCPY((dst), _len, (src), _len); \
} else { \
SET_ERRNO(ERROR_MEMORY, "String duplication failed for: %s", (src)); \
} \
} else { \
(dst) = NULL; \
} \
} while (0)

Definition at line 362 of file common.h.

363 { \
364 if (src) { \
365 size_t _len = strlen(src) + 1; \
366 (dst) = SAFE_MALLOC(_len, char *); \
367 if (dst) { \
368 SAFE_MEMCPY((dst), _len, (src), _len); \
369 } else { \
370 SET_ERRNO(ERROR_MEMORY, "String duplication failed for: %s", (src)); \
371 } \
372 } else { \
373 (dst) = NULL; \
374 } \
375 } while (0)

◆ SAFE_STRERROR

#define SAFE_STRERROR (   errnum)    platform_strerror(errnum)

#include <common.h>

Definition at line 385 of file common.h.

◆ SAFE_STRNCPY

#define SAFE_STRNCPY (   dst,
  src,
  size 
)    platform_strlcpy((dst), (src), (size))

#include <common.h>

Definition at line 358 of file common.h.

◆ THREAD_CREATE_OR_RETURN

#define THREAD_CREATE_OR_RETURN (   thread,
  func,
  arg 
)

#include <common.h>

Value:
do { \
if (asciichat_thread_create(&(thread), (func), (arg)) != 0) { \
log_error("Failed to create thread: %s", #func); \
return -1; \
} \
} while (0)
int asciichat_thread_create(asciichat_thread_t *thread, void *(*func)(void *), void *arg)
Create a new thread.

Create a thread or log error and return.

Parameters
threadPointer to thread_t variable
funcThread function (should match void* (*)(void*) signature)
argThread argument

Handles common pattern: create thread, log error if failed, then return -1. Example usage:

THREAD_CREATE_OR_RETURN(thread, thread_func, arg); // Thread created successfully, continue

Definition at line 442 of file common.h.

443 { \
444 if (asciichat_thread_create(&(thread), (func), (arg)) != 0) { \
445 log_error("Failed to create thread: %s", #func); \
446 return -1; \
447 } \
448 } while (0)

◆ TRACK_ALIGNED_ALLOC

#define TRACK_ALIGNED_ALLOC (   ptr,
  size,
  file,
  line 
)    ((void)0)

#include <common.h>

Definition at line 247 of file common.h.

◆ UNTRACKED_FREE

#define UNTRACKED_FREE (   ptr)

#include <common.h>

Value:
do { \
if ((ptr) != NULL) { \
free((void *)(ptr)); \
(ptr) = NULL; \
} \
} while (0)

Definition at line 349 of file common.h.

350 { \
351 if ((ptr) != NULL) { \
352 free((void *)(ptr)); \
353 (ptr) = NULL; \
354 } \
355 } while (0)

◆ UNTRACKED_MALLOC

#define UNTRACKED_MALLOC (   size,
  cast 
)

#include <common.h>

Value:
({ \
cast _ptr = (cast)malloc(size); \
if (!_ptr) { \
FATAL(ERROR_MEMORY, "Memory allocation failed: %zu bytes", (size_t)(size)); \
} \
_ptr; \
})

Definition at line 340 of file common.h.

341 { \
342 cast _ptr = (cast)malloc(size); \
343 if (!_ptr) { \
344 FATAL(ERROR_MEMORY, "Memory allocation failed: %zu bytes", (size_t)(size)); \
345 } \
346 _ptr; \
347 })

◆ write_u16_unaligned

#define write_u16_unaligned   bytes_write_u16_unaligned

#include <common.h>

Definition at line 406 of file common.h.

◆ write_u32_unaligned

#define write_u32_unaligned   bytes_write_u32_unaligned

#include <common.h>

Definition at line 407 of file common.h.

Typedef Documentation

◆ uint16_t

typedef unsigned short uint16_t

#include <common.h>

Definition at line 57 of file common.h.

◆ uint32_t

typedef unsigned int uint32_t

#include <common.h>

Definition at line 58 of file common.h.

◆ uint64_t

typedef unsigned long long uint64_t

#include <common.h>

Definition at line 59 of file common.h.

◆ uint8_t

typedef unsigned char uint8_t

#include <common.h>

Definition at line 56 of file common.h.

Function Documentation

◆ 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 <common.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}
__thread asciichat_error_context_t asciichat_errno_context
Thread-local error context storage.
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.
#define log_plain(...)
Plain logging - writes to both log file and stderr without timestamps or log levels.
@ 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.
void platform_backtrace_symbols_free(char **strings)
Free symbol array returned by platform_backtrace_symbols()
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_error_t code
Error code (asciichat_error_t enum value)

References asciichat_errno_context, asciichat_print_error_context(), asciichat_error_context_t::code, extract_project_relative_path(), format_message(), HAS_ERRNO, LOG_COLOR_ERROR, LOG_COLOR_FATAL, log_labeled(), log_plain, platform_backtrace(), platform_backtrace_symbols(), platform_backtrace_symbols_free(), platform_print_backtrace_symbols(), safe_fprintf(), and SAFE_FREE.

◆ asciichat_shared_init()

asciichat_error_t asciichat_shared_init ( const char *  default_log_filename,
bool  is_client 
)

#include <common.h>

Initialize common subsystems shared by client and server.

This function performs initialization that is common to both client and server modes:

  • Platform initialization (Winsock, etc.)
  • Logging setup with default filename
  • Palette configuration
  • Buffer pool initialization
  • Cleanup registration (errno, known_hosts, platform, buffer pool)
  • Mimalloc debug registration (if enabled)

Note: Memory debugging setup is handled separately by each mode due to different requirements (client has snapshot mode, server doesn't).

Parameters
default_log_filenameDefault log filename (e.g., "client.log" or "server.log")
is_clienttrue for client mode (routes all logs to stderr), false for server mode
Returns
ASCIICHAT_OK on success, error code on failure

Definition at line 64 of file common.c.

64 {
65 // Register memory debugging stats FIRST so it runs LAST at exit
66 // (atexit callbacks run in LIFO order - last registered runs first)
67 // This ensures all cleanup handlers run before the memory report is printed
68#if defined(DEBUG_MEMORY) && !defined(USE_MIMALLOC_DEBUG) && !defined(NDEBUG)
69 (void)atexit(debug_memory_report);
70#elif defined(USE_MIMALLOC_DEBUG) && !defined(NDEBUG)
71 (void)atexit(print_mimalloc_stats);
72 UNUSED(print_mimalloc_stats);
73#endif
74
75 // Initialize platform-specific functionality (Winsock, etc)
76 if (platform_init() != ASCIICHAT_OK) {
77 FATAL(ERROR_PLATFORM_INIT, "Failed to initialize platform");
78 }
79 (void)atexit(platform_cleanup);
80
81 // Apply quiet mode setting BEFORE log_init so initialization messages are suppressed
82 if (GET_OPTION(quiet)) {
84 }
85
86 // Initialize logging with default filename
87 // Client mode: route ALL logs to stderr to keep stdout clean for ASCII art output
88 const options_t *opts = options_get();
89 const char *log_file = opts && opts->log_file[0] != '\0' ? opts->log_file : default_log_filename;
90 // Use log_level from parsed options (set by options_init)
91 // Default levels (when no --log-level arg or LOG_LEVEL env var):
92 // Debug/Dev builds: LOG_DEBUG
93 // Release/RelWithDebInfo builds: LOG_INFO
94 // Precedence: LOG_LEVEL env var > --log-level CLI arg > build type default
95 // use_mmap=true: Lock-free mmap logging for performance and crash safety
96 log_init(log_file, GET_OPTION(log_level), is_client, true /* use_mmap */);
97
98 // Initialize palette based on command line options
99 const char *custom_chars = opts && opts->palette_custom_set ? opts->palette_custom : NULL;
100 if (apply_palette_config(GET_OPTION(palette_type), custom_chars) != 0) {
101 FATAL(ERROR_CONFIG, "Failed to apply palette configuration");
102 }
103
104 // Initialize global shared buffer pool
106 (void)atexit(buffer_pool_cleanup_global);
107
108 // Register errno cleanup
109 (void)atexit(asciichat_errno_cleanup);
110
111 // Register options state cleanup
112 (void)atexit(options_state_shutdown);
113
114 // Register known_hosts cleanup
115 (void)atexit(known_hosts_cleanup);
116
117 // Truncate log if it's already too large
119
120 // Set quiet mode for memory debugging (registration done at function start)
121#if defined(DEBUG_MEMORY) && !defined(USE_MIMALLOC_DEBUG) && !defined(NDEBUG)
122 debug_memory_set_quiet_mode(GET_OPTION(quiet));
123#endif
124
125 return ASCIICHAT_OK;
126}
void buffer_pool_cleanup_global(void)
void buffer_pool_init_global(void)
void known_hosts_cleanup(void)
Cleanup function to free cached known_hosts path.
void asciichat_errno_cleanup(void)
Cleanup error system resources.
void log_truncate_if_large(void)
Manually truncate large log files.
void log_set_terminal_output(bool enabled)
Control stderr output to terminal.
#define GET_OPTION(field)
Safely get a specific option field (lock-free read)
Definition options.h:644
const options_t * options_get(void)
Get current options (lock-free read)
Definition rcu.c:222
int apply_palette_config(palette_type_t type, const char *custom_chars)
Apply palette configuration (set global palette)
Definition palette.c:277
void platform_cleanup(void)
Cleanup platform-specific subsystems.
#define UNUSED(x)
Suppress unused parameter warnings.
asciichat_error_t platform_init(void)
Initialize platform-specific subsystems.
void options_state_shutdown(void)
Shutdown RCU options system.
Definition rcu.c:197
char palette_custom[256]
Custom palette characters.
Definition options.h:582
bool palette_custom_set
True if custom palette was set.
Definition options.h:583
char log_file[256]
Log file path.
Definition options.h:535

References apply_palette_config(), asciichat_errno_cleanup(), ASCIICHAT_OK, buffer_pool_cleanup_global(), buffer_pool_init_global(), ERROR_CONFIG, ERROR_PLATFORM_INIT, FATAL, GET_OPTION, known_hosts_cleanup(), log_file, options_state::log_file, log_init(), log_set_terminal_output(), log_truncate_if_large(), options_get(), options_state_shutdown(), options_state::palette_custom, options_state::palette_custom_set, platform_cleanup(), platform_init(), and UNUSED.

Referenced by main(), and main().

◆ fps_frame()

void fps_frame ( fps_t tracker,
const struct timespec *  current_time,
const char *  context 
)

#include <fps.h>

Track a frame and detect lag conditions.

Call this function when a frame is processed. Automatically detects when frames arrive late and generates periodic FPS reports every report_interval_us.

Parameters
trackerFPS tracker state
current_timeTimestamp of this frame (CLOCK_MONOTONIC)
contextOptional context string for lag logging (e.g., "ASCII frame received")

Definition at line 57 of file fps.c.

57 {
58 if (!tracker || !current_time) {
59 return;
60 }
61
62 // Initialize on first frame
63 if (tracker->last_fps_report.tv_sec == 0) {
64 tracker->last_fps_report = *current_time;
65 tracker->last_frame_time = *current_time;
66 }
67
68 tracker->frame_count++;
69
70 // Calculate time since last frame
71 uint64_t current_us = timespec_to_us(current_time);
72 uint64_t last_frame_us = timespec_to_us(&tracker->last_frame_time);
73 uint64_t frame_interval_us = current_us - last_frame_us;
74 tracker->last_frame_time = *current_time;
75
76 // Expected frame interval in microseconds
77 uint64_t expected_interval_us = 1000000ULL / (uint64_t)tracker->expected_fps;
78 uint64_t lag_threshold_us = expected_interval_us + (expected_interval_us / 2); // 50% over expected
79
80 // Log error if frame arrived too late
81 if (tracker->frame_count > 1 && frame_interval_us > lag_threshold_us) {
82 const char *context_str = context ? context : "Frame";
83 double late_ms = (double)(frame_interval_us - expected_interval_us) / 1000.0;
84 double expected_ms = (double)expected_interval_us / 1000.0;
85 double actual_ms = (double)frame_interval_us / 1000.0;
86 double actual_fps = 1000000.0 / (double)frame_interval_us;
87 log_lag_event(tracker->tracker_name, context_str, late_ms, expected_ms, actual_ms, actual_fps);
88 }
89
90 // Report FPS every report_interval_us (default 10 seconds)
91 uint64_t report_us = timespec_to_us(&tracker->last_fps_report);
92 uint64_t elapsed_us = current_us - report_us;
93
94 if (elapsed_us >= tracker->report_interval_us) {
95 double elapsed_seconds = (double)elapsed_us / 1000000.0;
96 double actual_fps = (double)tracker->frame_count / elapsed_seconds;
97 log_fps_report(tracker->tracker_name, actual_fps, tracker->frame_count, elapsed_seconds);
98
99 // Reset counters for next interval
100 tracker->frame_count = 0;
101 tracker->last_fps_report = *current_time;
102 }
103}
const char * tracker_name
Definition fps.h:57
struct timespec last_frame_time
Definition fps.h:54
uint64_t frame_count
Definition fps.h:52
uint64_t report_interval_us
Definition fps.h:56
struct timespec last_fps_report
Definition fps.h:53
int expected_fps
Definition fps.h:55

References fps_t::expected_fps, fps_t::frame_count, fps_t::last_fps_report, fps_t::last_frame_time, fps_t::report_interval_us, and fps_t::tracker_name.

Referenced by __attribute__(), client_audio_render_thread(), and client_video_render_thread().

◆ fps_init()

void fps_init ( fps_t tracker,
int  expected_fps,
const char *  name 
)

#include <fps.h>

Initialize FPS tracker.

Sets up the tracker with expected FPS and default reporting interval (10 seconds). Should be called once before using the tracker.

Parameters
trackerPointer to uninitialized fps_t (should be zero-initialized)
expected_fpsExpected frame rate in FPS (e.g., 60, 30, 20)
nameDisplay name for logging (e.g., "CLIENT", "WEBCAM", "AUDIO", "KEEPALIVE")

Definition at line 36 of file fps.c.

36 {
37 fps_init_with_interval(tracker, expected_fps, name, 10000000ULL); // 10 seconds default
38}
void fps_init_with_interval(fps_t *tracker, int expected_fps, const char *name, uint64_t report_interval_us)
Initialize FPS tracker with custom report interval.
Definition fps.c:40

References fps_init_with_interval().

Referenced by client_audio_render_thread(), and client_video_render_thread().

◆ fps_init_with_interval()

void fps_init_with_interval ( fps_t tracker,
int  expected_fps,
const char *  name,
uint64_t  report_interval_us 
)

#include <fps.h>

Initialize FPS tracker with custom report interval.

Sets up the tracker with expected FPS and custom reporting interval. Useful for different threads that may need different reporting frequencies.

Parameters
trackerPointer to uninitialized fps_t (should be zero-initialized)
expected_fpsExpected frame rate in FPS
nameDisplay name for logging
report_interval_usCustom report interval in microseconds

Definition at line 40 of file fps.c.

40 {
41 if (!tracker) {
42 return;
43 }
44
45 tracker->frame_count = 0;
46 tracker->expected_fps = expected_fps > 0 ? expected_fps : 60;
47 tracker->tracker_name = name ? name : "FPS";
48 tracker->report_interval_us = report_interval_us;
49
50 // Initialize timestamps to zero (will be set on first frame)
51 tracker->last_fps_report.tv_sec = 0;
52 tracker->last_fps_report.tv_nsec = 0;
53 tracker->last_frame_time.tv_sec = 0;
54 tracker->last_frame_time.tv_nsec = 0;
55}

References fps_t::expected_fps, fps_t::frame_count, fps_t::last_fps_report, fps_t::last_frame_time, fps_t::report_interval_us, and fps_t::tracker_name.

Referenced by __attribute__(), and fps_init().