ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
common.c
Go to the documentation of this file.
1
9// Platform abstraction includes memory sizing functions
10#include <ascii-chat/common.h>
14#include <ascii-chat/log/log.h>
17#include <ascii-chat/video/ascii/common.h> // For simd_caches_destroy_all()
18#include <ascii-chat/options/colorscheme.h> // For colorscheme_destroy()
19#include <ascii-chat/util/time.h> // For timer_system_destroy()
20#include <ascii-chat/util/pcre2.h> // For asciichat_pcre2_cleanup_all()
24#include <ascii-chat/options/rcu.h> // For RCU-based options access
25#include <ascii-chat/discovery/strings.h> // For RCU-based options access
26#include <ascii-chat/debug/sync.h> // For debug_sync_final_cleanup, debug_sync_cleanup_thread, debug_sync_destroy
27#include <ascii-chat/debug/mutex.h> // For mutex_stack_cleanup
28#include <ascii-chat/debug/named.h> // For named_destroy()
29#include <ascii-chat/debug/atomic.h> // For debug_atomic_shutdown()
30#include <ascii-chat/debug/memory.h> // For debug_memory_thread_cleanup in debug builds
31#include <ascii-chat/platform/symbols.h> // For symbols cache init
32#include <ascii-chat/platform/terminal.h> // For terminal_screen_cleanup
33#include <ascii-chat/platform/keyboard.h> // For keyboard_destroy()
34#include <ascii-chat/ui/terminal_screen.h> // For terminal_screen_cleanup
35#include <string.h>
36#include <ascii-chat/atomic.h>
37#include <limits.h>
38#include <stdlib.h>
39
40#ifndef _WIN32
41#include <pthread.h> // For PTHREAD_MUTEX_INITIALIZER on POSIX
42#endif
43
44/* ============================================================================
45 * Global Variables for Early Argv Inspection
46 * ============================================================================
47 */
48
49// These globals are used by lib/platform/terminal.c to detect --color flag
50// during early execution phases (e.g., when --help is processed before
51// options are fully parsed and published to RCU)
52// ASCIICHAT_API is required for proper DLL export on Windows
54ASCIICHAT_API char **g_argv = NULL;
55
56// Global flags for --color detection (set during options_init before RCU is ready)
57ASCIICHAT_API bool g_color_flag_passed = false; // Was --color explicitly passed in argv?
58ASCIICHAT_API bool g_color_flag_value = false; // What was the value of --color?
59
60/* ============================================================================
61 * Shutdown Check System Implementation
62 * ============================================================================
63 */
64
65// Use atomic for thread-safe access to shutdown callback
66#include <ascii-chat/atomic.h>
67static atomic_ptr_t g_shutdown_callback = {0};
68
70 atomic_ptr_store(&g_shutdown_callback, (void *)callback);
71}
72
74 shutdown_check_fn callback = (shutdown_check_fn)atomic_ptr_load(&g_shutdown_callback);
75 if (callback == NULL) {
76 return false; // No callback registered, assume not shutting down
77 }
78 return callback();
79}
80
81/* ============================================================================
82 * Shared Initialization Implementation
83 * ============================================================================
84 */
85
86#if defined(USE_MIMALLOC_DEBUG) && !defined(NDEBUG)
87static void print_mimalloc_stats(void);
88#endif
89
90// Guard to prevent multiple initialization of shared subsystems
91// (can happen if asciichat_shared_init() is called multiple times during startup)
92// But allow subsystem reinitialization for tests and other use cases
93static bool g_shared_initialized = false;
94
95#ifndef _WIN32
102static void asciichat_common_atfork_child(void) {
103 // Reset the shared initialization flag so asciichat_shared_init() will reinitialize subsystems
104 g_shared_initialized = false;
105}
106
110__attribute__((constructor)) static void register_common_fork_handlers(void) {
111 pthread_atfork(NULL, NULL, asciichat_common_atfork_child);
112}
113#endif
114
115asciichat_error_t asciichat_shared_init(const char *log_file, bool is_client, bool quiet_startup) {
116 // Initialize shared subsystems BEFORE options_init()
117 // This allows options_init() to use properly configured logging with colors
118 //
119 // NOTE: This function is called BEFORE options_init(), so we can't use GET_OPTION()
120 // Palette config and quiet mode will be applied after options_init() is called
121
122 // Only register initialization tracking once
123 if (!g_shared_initialized) {
124 // 1. TIMER SYSTEM - Initialize first so timing is available for everything else
125 if (!timer_system_init()) {
126 FATAL(ERROR_PLATFORM_INIT, "Failed to initialize timer system");
127 }
128
129 // KEYBOARD SYSTEM must be initialized BEFORE logging since keyboard_init() calls log_info()
130 // But we need to initialize logging AFTER timer_system_init() and BEFORE keyboard_init()
131 // So we move keyboard_init() to AFTER log_init()
132
133#ifndef NDEBUG
134 if (named_init() != ASCIICHAT_OK) {
135 return SET_ERRNO(ERROR_PLATFORM_INIT, "Failed to initialize named.");
136 }
139#endif
140
141 // 2. LOGGING - Initialize system state first, then user-level logging
142 // Initialize logging system internal state (terminal caps, colors)
144
145 // Initialize with provided log file
146 // Force stderr for client-like modes when stdout is piped to keep stdout clean
147 bool force_stderr = is_client && terminal_is_piped_output();
148
149 if (quiet_startup) {
151 }
152
153 // Use LOG_DEBUG by default; will be reconfigured after options_init()
154 log_init(log_file, LOG_DEBUG, force_stderr, false /* don't use_mmap */);
155
156 // 2. KEYBOARD SYSTEM - Initialize keyboard input AFTER logging (may fail if stdin/tty not available)
157 // Failure is not fatal - keyboard_read_nonblocking() safely handles uninitialized state
159
160 // 3. PLATFORM - Initialize platform-specific functionality (Winsock, etc)
161 if (platform_init() != ASCIICHAT_OK) {
162 FATAL(ERROR_PLATFORM_INIT, "Failed to initialize platform");
163 }
164
165 // 4. BUFFER POOL - Initialize global shared buffer pool
167
168 // Mark that we've initialized (prevents re-registration of subsystems)
169 g_shared_initialized = true;
170 }
171
172 // NOTE: This library function does NOT register atexit() handlers.
173 // Library code should not own the process lifecycle. The application
174 // is responsible for calling atexit(asciichat_shared_destroy) if it
175 // wants automatic cleanup on normal exit.
176
177 return ASCIICHAT_OK;
178}
179
196 // Guard against double cleanup (can be called explicitly + via atexit)
197 static bool shutdown_done = false;
198 if (shutdown_done) {
199 return;
200 }
201 shutdown_done = true;
202
204
205 // Cleanup in reverse order of initialization (LIFO)
206 // This ensures dependencies are properly handled
207
208 // 0. Stop background threads first (before any cleanup that might use them)
209 // Terminal resize detection thread (Windows only, checks if active)
211
212#ifndef NDEBUG
213 // Lock debug thread - must join before any lock cleanup
215
216 // Clean up all remaining mutex stacks before memory report
218
219 // Clean up current thread's allocations
221
222 // Memory debug thread - prints memory report (must be last)
223#if defined(DEBUG_MEMORY)
225#endif
226
227 // Lock debug system - set initialized=false so mutex_lock uses mutex_lock_impl directly
228 // This must happen after thread cleanup but before any subsystem that uses mutex_lock
230
231 // Atomic debug cleanup
233#endif
234
235 // 1. Terminal screen - cleanup frame buffer
237
238 // 2. SIMD caches - cleanup CPU-specific caches
240
241 // 3. Discovery service strings cache - cleanup session string cache
243
244 // 4. Logging - in correct order (end first, then begin)
247
248 // 5. Known hosts - cleanup authentication state
250
251 // 6. Options state - cleanup RCU-based options
253
254 // 7. Buffer pool - cleanup global buffer pool
256
257 // 8. Platform cleanup - restores terminal, cleans up platform resources
258 // (includes symbol cache cleanup on Windows)
260
261 // 9. Keyboard - restore terminal settings (redundant with platform_destroy but safe)
263
264 // 10. Timer system - cleanup timers (may still log!)
266
267 // 11. Error context cleanup
269
270 // 12. Logging cleanup - free log format buffers and system state
271 log_destroy();
273
274 // 13. Mutex stack cleanup - must be before memory report so stacks are freed
275#ifndef NDEBUG
277 // Final cleanup: free the main thread's debug allocations
279#endif
280
281 // 14. Memory stats (debug builds only) - runs with colors still available
282 // Note: PCRE2 singletons are cleaned up later via atexit handler (runs after this function)
283 // This ensures code can still use PCRE2 during normal program shutdown
284 // Note: debug_memory_report() is also called manually during shutdown in server_main()
285 // The function is safe to call multiple times with polling-based locking
286#if defined(USE_MIMALLOC_DEBUG) && !defined(NDEBUG)
287 print_mimalloc_stats();
288#endif
289
290#if defined(DEBUG_MEMORY) && !defined(NDEBUG)
292#endif
293
294 // 15. Color cleanup - free compiled ANSI strings (AFTER memory report)
297
298 // 16. Clean up binary path cache explicitly
299 // Note: This is also called by platform_destroy() via atexit(), but it's idempotent
301
302 // 17. Clean up RCU-based options state
304
305 // 18. Clean up errno context (allocated strings, backtrace symbols)
307
308#ifndef NDEBUG
309 // 19. Named registry - cleanup all registered thread names and debug entries
310 // Must come BEFORE PCRE2 cleanup since named_destroy() uses path normalization which needs PCRE2
312#endif
313
314 // 20. PCRE2 - cleanup all regex singletons together (after named_destroy)
316
317 // 21. timer resolution restore
318 platform_restore_timer_resolution(); // Restore timer resolution (no-op on POSIX)
319
320 // 22. Clean up SIMD caches
322
323 // 23. Clean up symbol cache
324 // This must be called BEFORE log_destroy() as symbol_cache_destroy() uses log_debug()
325 // Safe to call even if atexit() runs - it's idempotent (checks g_symbol_cache_initialized)
326 // Also called via platform_destroy() atexit handler, but explicit call ensures proper ordering
328
329 // 24. Clean up global buffer pool (explicitly, as atexit may not run on Ctrl-C)
330 // Note: This is also registered with atexit(), but calling it explicitly is safe (idempotent)
331 // Safe to call even if atexit() runs - it checks g_global_buffer_pool and sets it to NULL
333
334#ifndef NDEBUG
335 // 25. Symbol cache cleanup
337#endif
338}
339
340#if defined(USE_MIMALLOC_DEBUG) && !defined(NDEBUG)
341// Wrapper function for mi_stats_print to use with atexit()
342// mi_stats_print takes a parameter, but atexit requires void(void)
343static void print_mimalloc_stats(void) {
344 // Skip memory report if an action flag was passed (for clean action output)
345 // unless explicitly forced via ASCII_CHAT_MEMORY_DEBUG environment variable
346 if (has_action_flag() && !SAFE_GETENV("ASCII_CHAT_MEMORY_DEBUG")) {
347 return;
348 }
349 mi_stats_print(NULL); // NULL = print to stderr
350}
351#endif
⚠️‼️ Comprehensive thread-local error context system for ascii-chat
void atomic_ptr_store(atomic_ptr_t *a, void *value)
Atomically store a pointer.
Definition atomic.c:280
void * atomic_ptr_load(atomic_ptr_t *a)
Atomically load a pointer.
Definition atomic.c:272
⚛️ Atomic operations abstraction layer with debug tracking
🗃️ Lock-Free Unified Memory Buffer Pool with Lazy Allocation
void colorscheme_destroy(void)
Shutdown the color system.
⚙️ Common definitions, error codes, macros, and types shared throughout the application
void debug_atomic_shutdown(void)
Lifecycle shutdown for debug atomic tracking.
Debug tracking for atomic operations.
void debug_memory_thread_cleanup(void)
void debug_memory_report(void)
🔍 Memory debugging helpers for tracking allocations in debug builds
void mutex_stack_cleanup(void)
Cleanup mutex stack system.
Per-thread mutex lock stack for deadlock detection.
Named object registry for debugging — log identifiable resource names.
void acds_strings_destroy(void)
Cleanup function for session string cache Called by asciichat_shared_destroy() during library cleanup...
void buffer_pool_cleanup_global(void)
void buffer_pool_init_global(void)
bool g_color_flag_value
Value of –color flag (true if –color was in argv) Set by options_init() before RCU is initialized,...
Definition common.c:58
char ** g_argv
Global argv for early argument inspection (e.g., –color flag detection) Set by main() for access from...
Definition common.c:54
int g_argc
Global argc for early argument inspection (e.g., –color flag detection) Set by main() for access from...
Definition common.c:53
bool g_color_flag_passed
Was –color explicitly passed in command-line arguments? Set by options_init() before RCU is initializ...
Definition common.c:57
#define SAFE_GETENV(name)
Definition common.h:434
asciichat_error_t asciichat_shared_init(const char *log_file, bool is_client, bool quiet_startup)
Initialize common subsystems shared by client and server.
Definition common.c:115
#define FATAL(code,...)
Exit with error code and custom message, with stack trace in debug builds.
Definition common.h:158
void asciichat_shared_destroy(void)
Clean up all shared library subsystems.
Definition common.c:195
void known_hosts_destroy(void)
Cleanup function to free cached known_hosts path.
void named_destroy(void)
Destroy the named object registry.
asciichat_error_t named_init(void)
Initialize the named object registry.
Definition debug/named.c:89
void debug_sync_destroy(void)
Destroy debug synchronization system.
Definition sync.c:670
void debug_sync_final_cleanup(void)
Final cleanup of all debug allocations at shutdown.
Definition sync.c:595
void debug_sync_cleanup_thread(void)
Stop and clean up background debug thread.
Definition sync.c:674
void debug_memory_ensure_init(void)
Init the lifecycle of the debug memory module.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
void asciichat_errno_destroy(void)
Cleanup error system resources.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ERROR_PLATFORM_INIT
Definition error_codes.h:60
@ ASCIICHAT_OK
Definition error_codes.h:51
void log_system_init(void)
Initialize the logging system internal state (system-level init)
Definition log/log.c:669
void log_shutdown_end(void)
End shutdown phase - restore previous logging settings.
Definition log/log.c:1806
void log_destroy(void)
Destroy the logging system and close log file.
Definition log/log.c:624
void log_cleanup_colors(void)
Clean up compiled color scheme.
Definition log/log.c:1822
void log_shutdown_begin(void)
Begin shutdown phase - disable console logging but keep file logging.
Definition log/log.c:1795
void log_system_destroy(void)
Shutdown the logging system internal state (system-level cleanup)
Definition log/log.c:686
void log_init(const char *filename, log_level_t level, bool force_stderr, bool use_mmap)
Initialize the logging system.
Definition log/log.c:533
#define log_file(...)
File-only logging - writes to log file only, no stderr output.
Definition log/log.h:646
void log_set_terminal_output(bool enabled)
Control stderr output to terminal.
Definition log/log.c:700
void timer_system_destroy(void)
Cleanup the timing system.
Definition util/time.c:213
bool timer_system_init(void)
Initialize the timing system.
Definition util/time.c:191
bool has_action_flag(void)
Check if an action flag was detected.
asciichat_error_t keyboard_init(void)
Initialize keyboard input system.
void platform_destroy(void)
Cleanup platform-specific subsystems.
#define ASCIICHAT_API
Export symbols on Unix platforms (Linux, macOS)
Definition api.h:82
asciichat_error_t symbol_cache_init(void)
Initialize the symbol cache.
Definition symbols.c:291
void keyboard_destroy(void)
Cleanup keyboard input system and restore terminal.
void symbol_cache_destroy(void)
Clean up the symbol cache and free all resources.
Definition symbols.c:316
asciichat_error_t platform_restore_timer_resolution(void)
Restore default timer precision.
Definition misc.c:99
void platform_cleanup_binary_path_cache(void)
Cleanup the binary PATH cache.
Definition filesystem.c:113
asciichat_error_t platform_init(void)
Initialize platform-specific subsystems.
bool(* shutdown_check_fn)(void)
Shutdown check callback function type.
Definition shutdown.h:32
bool shutdown_is_requested(void)
Check if shutdown has been requested.
Definition common.c:73
void shutdown_register_callback(shutdown_check_fn callback)
Register application's shutdown check function.
Definition common.c:69
void simd_caches_destroy_all(void)
Destroy all SIMD caches (palette and other caches)
⚙️ Unified options parsing system for ascii-chat with builder pattern and lock-free access
Platform initialization and static synchronization helpers.
🎮 Cross-platform keyboard input interface for ascii-chat
Known hosts management for MITM attack prevention.
Application limits and constraints.
📝 Logging API with multiple log levels and terminal output control
void terminal_stop_resize_detection(void)
No-op stub on POSIX platforms (no resize detection thread)
Definition misc.c:91
ASCII Palette Management for Video-to-ASCII Conversion.
void asciichat_pcre2_cleanup_all(void)
Free all PCRE2 singletons in the global registry.
Definition pcre2.c:214
🔍 Centralized PCRE2 singleton pattern for efficient regex compilation
Cross-platform memory allocation utilities.
bool terminal_is_piped_output(void)
Check if stdout is piped or redirected.
void options_state_destroy(void)
Shutdown RCU options system.
Definition rcu.c:461
Network quality metrics for a single participant.
Definition metrics.h:33
Atomic pointer wrapper.
Definition atomic.h:106
Symbol Resolution Cache for Backtrace Addresses.
🔒 Synchronization primitive debugging (mutexes, rwlocks, condition variables)
Cross-platform system functions interface for ascii-chat.
🖥️ Cross-platform terminal interface for ascii-chat
void terminal_screen_cleanup(void)
Clean up terminal screen resources.
Reusable "fixed header + scrolling logs" terminal screen abstraction.
⏱️ High-precision timing utilities using sokol_time.h and uthash
#define LOG_DEBUG
Definition types.h:39
SIMD-optimized ASCII conversion interface.