ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
Debug_sync

Files

file  mutex.c
 Per-thread mutex lock stack for deadlock detection.
 
file  sync.c
 ðŸ”’ Synchronization primitive debugging with dynamic state inspection
 
file  mutex.h
 Per-thread mutex lock stack for deadlock detection.
 
file  sync.h
 ðŸ”’ Synchronization primitive debugging (mutexes, rwlocks, condition variables)
 

Functions

void debug_sync_check_cond_deadlocks (void)
 Check all condition variables for deadlocks.
 
void debug_sync_print_mutex_state (void)
 Print timing state for all named mutexes to stdout/log.
 
void debug_sync_print_rwlock_state (void)
 Print timing state for all named read-write locks to stdout/log.
 
void debug_sync_print_cond_state (void)
 Print timing state for all named condition variables to stdout/log.
 
void debug_sync_print_state (void)
 Print all synchronization primitive states at once.
 
void debug_sync_print_state_delayed (uint64_t delay_ns)
 Schedule delayed sync state printing on debug thread.
 
void debug_sync_print_backtrace_delayed (uint64_t delay_ns)
 Schedule delayed backtrace printing on debug thread.
 
void debug_sync_set_memory_report_interval (uint64_t interval_ns)
 Set periodic memory report interval.
 
void debug_sync_set_main_thread_id (void)
 Initialize debug synchronization system.
 
uint64_t debug_sync_get_main_thread_id (void)
 Get the main thread ID for memory reporting.
 
int debug_sync_start_thread (void)
 Start background debug thread for scheduled operations.
 
void debug_sync_destroy (void)
 Destroy debug synchronization system.
 
void debug_sync_cleanup_thread (void)
 Stop and clean up background debug thread.
 
void debug_sync_final_cleanup (void)
 Final cleanup of debug allocations at shutdown.
 
void debug_sync_trigger_print (void)
 Trigger sync state print immediately (synchronous)
 
void debug_sync_get_stats (uint64_t *total_acquired, uint64_t *total_released, uint32_t *currently_held)
 Get synchronization statistics.
 

Detailed Description

This module provides comprehensive synchronization state inspection and debugging:

No internal collection overhead - queries named.c directly for live state, making it safe to call even in tight loops without performance penalty.

Purpose

Synchronization debugging answers critical questions:

Useful for:

Key Features

Integration with Named Registry

Synchronization primitives are identified by their names in the named.c registry:

// In your code:
mutex_t recv_lock;
mutex_init(&recv_lock);
NAMED_REGISTER(&recv_lock, "recv", "mutex"); // See debug/named.h
// Later, in debug output:
debug_sync_print_state(); // Prints "recv.1 (mutex)" with timing
#define NAMED_REGISTER(ptr, name, type, fmt, parent_ptr)
Register any pointer with base name, type, format spec, and location (auto-suffix)
void debug_sync_print_state(void)
Print all synchronization primitive states at once.
Definition sync.c:387
int mutex_init(mutex_t *mutex, const char *name)
Initialize a mutex with a name.
Definition threading.c:16
Mutex type (POSIX: pthread_mutex_t with debug tracking)
See also
debug/named.h - The registry that backs this module

Usage Examples

Development: Print Current Lock State

// When you suspect a deadlock or lock contention:
debug_sync_print_state(); // Prints all mutexes, rwlocks, conds with timing

Production: Periodic State Dumps

// In a signal handler (e.g., SIGUSR2):
void handle_debug_signal(int sig) {
debug_sync_trigger_print(); // Schedules print on debug thread
}
// In main:
signal(SIGUSR2, handle_debug_signal); // SIGUSR2 is mapped to sync state printing
// Now: kill -USR2 <pid> triggers state dump in logs
void debug_sync_trigger_print(void)
Trigger sync state print immediately (synchronous)
Definition sync.c:710
int debug_sync_start_thread(void)
Start background debug thread for scheduled operations.
Definition sync.c:657
int debug_sync_init(void)
Definition sync.c:644

Testing: Capture State at Specific Moments

// Schedule a state dump after 100ms (during critical section):
debug_sync_print_state_delayed(100 * 1000000); // 100ms in nanoseconds
// State will print automatically on debug thread
void debug_sync_print_state_delayed(uint64_t delay_ns)
Schedule delayed debug state printing on debug thread.
Definition sync.c:604

Backtrace on Lock Contention

// Combined with backtrace:
debug_sync_print_state(); // See which locks are held
debug_sync_print_backtrace_delayed(50 * 1000000); // Get stacks after 50ms
void debug_sync_print_backtrace_delayed(uint64_t delay_ns)
Schedule delayed backtrace printing on debug thread.
Definition sync.c:617

Output Format

Typical output:

=== Mutex State ===
recv.1 (mutex) @ lib/network/socket.c:42:socket_create()
Last lock: 2026-02-23T17:45:23.123456789Z (123ms ago)
Last unlock: 2026-02-23T17:45:23.200000000Z (45ms ago)
Status: free
send.2 (mutex) @ lib/network/socket.c:89:socket_send()
Last lock: 2026-02-23T17:45:23.195000000Z (50ms ago)
Last unlock: (never)
Status: HELD (potential deadlock!)
mutex_t mutex
ssize_t socket_send(socket_t sock, const void *buf, size_t len, int flags)
Send data on a socket.
socket_t socket_create(const char *name, int domain, int type, int protocol)
Create a new named socket.
Author
Zachary Fogg me@zf.nosp@m.o.gg
Date
February 2026

Function Documentation

◆ debug_sync_check_cond_deadlocks()

void debug_sync_check_cond_deadlocks ( void  )

#include <mutex.c>

Check all condition variables for deadlocks.

Check all condition variables for potential deadlocks.

Scans all registered condition variables and logs warnings for any that have been waiting without signal for longer than COND_DEADLOCK_THRESHOLD_NS. Called periodically by the debug thread (every 100ms).

Skips checks during shutdown to avoid accessing freed memory.

Scans all registered condition variables and logs warnings for any that have threads waiting without being signaled for longer than 5 seconds.

For each stuck condition variable, logs:

  • Number of waiting threads and elapsed wait time
  • Callsite where wait was entered (file, line, function)
  • Associated mutex status (held by whom, or free)

Called periodically by the debug thread (every 100ms), so detection latency is at most 5 seconds + 100ms for stuck conditions.

Output Example

[WARN] Stuck cond 'audio_send_queue_cond.0': 1 thread(s) waiting 66s with no signal (most recent waiter:
audio_sender.0) [WARN] wait entered at src/client/audio.c:291 audio_sender_thread_func() [WARN] associated mutex
is FREE — producer is not calling cond_signal
int cond_signal(cond_t *cond)
Signal a condition variable (wake one waiting thread)
Note
Thread-safe: can be called from any thread
No blocking: reads only, doesn't acquire locks
Called automatically by debug thread; only call manually for immediate checks
See also
debug_sync_start_thread() to ensure periodic checking

Definition at line 737 of file debug/mutex.c.

737 {
739 return;
740 }
741 named_registry_for_each(cond_deadlock_check_callback, NULL);
742}
bool debug_sync_is_cleanup_in_progress(void)
Definition sync.c:653
void named_registry_for_each(named_iter_callback_t callback, void *user_data)
Iterate through all registered entries.

References debug_sync_is_cleanup_in_progress(), and named_registry_for_each().

◆ debug_sync_cleanup_thread()

void debug_sync_cleanup_thread ( void  )

#include <sync.h>

Stop and clean up background debug thread.

Gracefully shuts down the background debug thread that was started by debug_sync_start_thread(). Processes any remaining queued jobs before exit.

Typical Shutdown Sequence

// At program shutdown:
debug_sync_cleanup_thread(); // Stop background thread
debug_sync_destroy(); // Destroy system
void debug_sync_destroy(void)
Destroy debug synchronization system.
Definition sync.c:670
void debug_sync_cleanup_thread(void)
Stop and clean up background debug thread.
Definition sync.c:674
Note
Must be called before debug_sync_destroy()
Safe to call multiple times

Definition at line 674 of file sync.c.

674 {
675 log_debug("[DEBUG_SYNC_CLEANUP] Starting cleanup");
676
677 // Only join if thread was actually created
678 if (!g_debug_state_request.initialized) {
679 log_debug("[DEBUG_SYNC_CLEANUP] Thread not initialized, returning");
680 return;
681 }
682
683 // Set cleanup flag to prevent deadlock checks from accessing freed memory
684 // Do this after checking initialization so we don't set it unnecessarily
685 atomic_store_bool(&g_cleanup_in_progress, true);
686 log_debug("[DEBUG_SYNC_CLEANUP] Thread was initialized, proceeding with cleanup");
687
688 log_debug("[DEBUG_SYNC_CLEANUP] Setting initialized to false");
689 g_debug_state_request.initialized = false; // Prevent double-join
690
691 // Signal the thread to wake up immediately instead of waiting for 100ms timeout
692 log_debug("[DEBUG_SYNC_CLEANUP] Signaling thread to exit");
693 atomic_store_bool(&g_debug_state_request.should_exit, true);
694 cond_signal(&g_debug_state_request.cond);
695 log_debug("[DEBUG_SYNC_CLEANUP] Signal sent, about to join thread");
696
697 // Use a timeout join to ensure we don't deadlock, but still unregister the thread
698 // The debug thread should exit quickly after should_exit is set above
699 int join_result = asciichat_thread_join_timeout(&g_debug_thread, NULL, 1000000000ULL); // 1 second timeout
700 if (join_result == 0) {
701 log_debug("[DEBUG_SYNC_CLEANUP] Thread joined successfully");
702 } else if (join_result == -2) {
703 log_debug("[DEBUG_SYNC_CLEANUP] Thread join timed out (thread may still be running)");
704 // Don't unregister if timeout - thread is still alive
705 } else {
706 log_debug("[DEBUG_SYNC_CLEANUP] Thread join failed with error %d", join_result);
707 }
708}
void atomic_store_bool(atomic_t *a, bool value)
Atomically store a boolean value.
Definition atomic.c:177
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
int asciichat_thread_join_timeout(asciichat_thread_t *thread, void **retval, uint64_t timeout_ns)
Wait for a thread to complete with timeout.
atomic_t should_exit
Definition sync.c:445

References asciichat_thread_join_timeout(), atomic_store_bool(), debug_state_request_t::cond, cond_signal(), debug_state_request_t::initialized, log_debug, and debug_state_request_t::should_exit.

Referenced by asciichat_shared_destroy(), debug_sync_destroy(), and session_client_like_run().

◆ debug_sync_destroy()

void debug_sync_destroy ( void  )

#include <sync.h>

Destroy debug synchronization system.

Cleans up internal structures. Should be called during shutdown, after debug_sync_cleanup_thread().

See also
debug_sync_cleanup_thread() should be called first

Definition at line 670 of file sync.c.

670 {
672}

References debug_sync_cleanup_thread().

Referenced by asciichat_shared_destroy().

◆ debug_sync_final_cleanup()

void debug_sync_final_cleanup ( void  )

#include <sync.h>

Final cleanup of debug allocations at shutdown.

Cleans up any remaining thread-local allocations (mutex stacks, etc) from the current thread. Must be called before debug_sync_destroy().

Final cleanup of debug allocations at shutdown.

Definition at line 595 of file sync.c.

595 {
596 // Clean up current thread's mutex stack explicitly
598}
void mutex_stack_cleanup_current_thread(void)
Cleanup TLS stack for current thread Explicitly frees the thread-local mutex stack....

References mutex_stack_cleanup_current_thread().

Referenced by asciichat_shared_destroy().

◆ debug_sync_get_main_thread_id()

uint64_t debug_sync_get_main_thread_id ( void  )

#include <sync.h>

Get the main thread ID for memory reporting.

Returns
Main thread ID as uint64_t, or 0 if not initialized

Definition at line 649 of file sync.c.

649 {
650 return g_debug_main_thread_id;
651}

◆ debug_sync_get_stats()

void debug_sync_get_stats ( uint64_t *  total_acquired,
uint64_t *  total_released,
uint32_t *  currently_held 
)

#include <sync.h>

Get synchronization statistics.

Parameters
total_acquiredPointer to store total lock acquisitions (can be NULL)
total_releasedPointer to store total lock releases (can be NULL)
currently_heldPointer to store currently held locks (can be NULL)

Retrieves global statistics about synchronization primitive usage. Any parameter can be NULL if that statistic isn't needed.

Useful for:

  • Monitoring lock contention trends
  • Validating symmetric lock/unlock behavior
  • Detecting lock leaks (acquired != released)

Example Usage

uint64_t acquired, released;
uint32_t held;
debug_sync_get_stats(&acquired, &released, &held);
if (acquired == released && held == 0) {
log_info("No lock leaks: %lu acquisitions, all released", acquired);
} else if (held > 0) {
log_warn("Possible deadlock: %u locks currently held", held);
}
unsigned int uint32_t
Definition common.h:58
unsigned long long uint64_t
Definition common.h:59
void debug_sync_get_stats(uint64_t *total_acquired, uint64_t *total_released, uint32_t *currently_held)
Get synchronization statistics.
Definition sync.c:722
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
Note
Atomically reads global counters, no locking required
Statistics accumulate over program lifetime
Useful for periodic health checks or monitoring

Definition at line 722 of file sync.c.

722 {
723 if (total_acquired)
724 *total_acquired = 0;
725 if (total_released)
726 *total_released = 0;
727 if (currently_held)
728 *currently_held = 0;
729}

Referenced by stats_logger_thread().

◆ debug_sync_print_backtrace_delayed()

void debug_sync_print_backtrace_delayed ( uint64_t  delay_ns)

#include <sync.h>

Schedule delayed backtrace printing on debug thread.

Parameters
delay_nsDelay in nanoseconds before printing

Schedules a full backtrace capture and print on the debug thread after the specified delay. Complements debug_sync_print_state_delayed() to capture both lock state AND stack traces at a specific moment.

The debug thread must be running (started via debug_sync_start_thread()).

Combined Usage

void suspect_deadlock_region(void) {
// Capture both state and stacks after 100ms
// Execute potentially problematic code
// After 100ms, both state and stacks will be printed
}
Note
Non-blocking: returns immediately
Useful with delayed state printing for comprehensive debugging
Complements backtrace_capture_and_symbolize() for manual backtraces
See also
debug_sync_print_state_delayed()
backtrace.h for manual backtrace capture
Parameters
delay_nsNanoseconds to sleep before printing

Definition at line 617 of file sync.c.

617 {
618 mutex_lock(&g_debug_state_request.mutex);
619 g_debug_state_request.request_type = DEBUG_REQUEST_BACKTRACE;
620 g_debug_state_request.delay_ns = delay_ns;
621 atomic_store_bool(&g_debug_state_request.should_run, true);
622 cond_signal(&g_debug_state_request.cond);
623 mutex_unlock(&g_debug_state_request.mutex);
624}
#define mutex_lock(mutex)
Lock a mutex (with debug tracking in debug builds)
#define mutex_unlock(mutex)
Unlock a mutex (with debug tracking in debug builds)
atomic_t should_run
Definition sync.c:444
uint64_t delay_ns
Definition sync.c:443
debug_request_type_t request_type
Definition sync.c:442
@ DEBUG_REQUEST_BACKTRACE
Definition sync.c:437

References atomic_store_bool(), debug_state_request_t::cond, cond_signal(), DEBUG_REQUEST_BACKTRACE, debug_state_request_t::delay_ns, debug_state_request_t::mutex, mutex_lock, mutex_unlock, debug_state_request_t::request_type, and debug_state_request_t::should_run.

◆ debug_sync_print_cond_state()

void debug_sync_print_cond_state ( void  )

#include <sync.h>

Print timing state for all named condition variables to stdout/log.

Queries the named.c registry and prints a table of all registered condition variables with their timing information:

  • Name and registration location
  • Last wait time (with elapsed time)
  • Last signal time (with elapsed time)
  • Last broadcast time (with elapsed time)

Useful for detecting:

  • Condition variable deadlocks (waiters never signaled)
  • Signal storms (excessive signaling)
  • Lost wakeups (signals without corresponding waiters)
Note
Thread-safe: can be called from any thread or signal handler
No blocking: reads only, doesn't acquire locks
See also
NAMED_REGISTER() in debug/named.h to register condition variables

◆ debug_sync_print_mutex_state()

void debug_sync_print_mutex_state ( void  )

#include <sync.h>

Print timing state for all named mutexes to stdout/log.

Queries the named.c registry and prints a table of all registered mutexes with their timing information:

  • Name and registration location
  • Last lock time (with elapsed time)
  • Last unlock time (with elapsed time)
  • Current status (held/free)

Useful for detecting:

  • Mutex deadlocks (HELD locks that should be released)
  • Lock starvation (locks never acquired)
  • Excessive lock contention (very frequent lock/unlock cycles)
Note
Thread-safe: can be called from any thread or signal handler
No blocking: reads only, doesn't acquire locks
Zero overhead in release builds if no mutexes are named
See also
NAMED_REGISTER() in debug/named.h to register mutexes for tracking

◆ debug_sync_print_rwlock_state()

void debug_sync_print_rwlock_state ( void  )

#include <sync.h>

Print timing state for all named read-write locks to stdout/log.

Queries the named.c registry and prints a table of all registered rwlocks with their timing information:

  • Name and registration location
  • Last read-lock time (with elapsed time)
  • Last write-lock time (with elapsed time)
  • Last unlock time (with elapsed time)
  • Current status (read-held/write-held/free)

Useful for detecting:

  • RWlock writer starvation (reads blocking writers)
  • Asymmetric lock patterns (only readers or only writers)
  • Writer lock deadlocks (HELD write locks)
Note
Thread-safe: can be called from any thread or signal handler
No blocking: reads only, doesn't acquire locks
See also
NAMED_REGISTER() in debug/named.h to register rwlocks for tracking

◆ debug_sync_print_state()

void debug_sync_print_state ( void  )

#include <sync.h>

Print all synchronization primitive states at once.

Comprehensive debugging view that combines output from:

  1. debug_sync_print_mutex_state()
  2. debug_sync_print_rwlock_state()
  3. debug_sync_print_cond_state()

This is the starting point for understanding overall lock contention and state. Use individual print*_state() functions if you only need specific primitive types.

Typical Workflow

// In debugger or signal handler:
debug_sync_print_state(); // See all sync primitives at once
// Then examine the output for:
// - Any HELD locks that should be free
// - Asymmetric patterns (locks acquired but never released)
// - Timestamps that suggest contention or deadlock
Note
Thread-safe: can be called from any thread or signal handler
Useful in production with signal handlers for "give me the state now"
See also
debug_sync_print_mutex_state()
debug_sync_print_rwlock_state()
debug_sync_print_cond_state()

Definition at line 387 of file sync.c.

387 {
388// Use a single large buffer for all sync state output
389#define SYNC_BUFFER_SIZE 65536 // Increased from 8192 to handle many syncs
390 log_debug("[debug_sync_print_state] ENTRY");
391
392 char *buffer = SAFE_MALLOC(SYNC_BUFFER_SIZE, char *);
393 if (!buffer) {
394 log_debug("[debug_sync_print_state] Failed to allocate buffer");
395 return;
396 }
397
398 sync_buffer_t buf = {.buffer = buffer, .buffer_size = SYNC_BUFFER_SIZE, .offset = 0};
399
400 // Iterate through all registered syncs
401 log_debug("[debug_sync_print_state] Iterating mutexes");
402 named_registry_for_each(mutex_iter_callback, &buf);
403 log_debug("[debug_sync_print_state] Iterating rwlocks");
404 named_registry_for_each(rwlock_iter_callback, &buf);
405 log_debug("[debug_sync_print_state] Iterating conds");
406 named_registry_for_each(cond_iter_callback, &buf);
407
408 named_registry_for_each(atomic_t_iter_callback, &buf);
409 named_registry_for_each(atomic_ptr_iter_callback, &buf);
410
411 // Print lock stacks for deadlock analysis
412 log_debug("[debug_sync_print_state] Getting lock stacks");
413 debug_sync_print_lock_stacks(buf.buffer, buf.buffer_size, &buf.offset);
414
415 // Log everything in one call
416 log_debug("[debug_sync_print_state] Buffer size: %zu bytes", buf.offset);
417 if (buf.offset > 0) {
418 log_info("SYNC_STATE:\n%s", buf.buffer);
419 } else {
420 log_info("SYNC_STATE: (empty)");
421 }
422
423 SAFE_FREE(buffer);
424#undef SYNC_BUFFER_SIZE
425}
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
char * buffer
Definition sync.c:219
size_t buffer_size
Definition sync.c:220
size_t offset
Definition sync.c:221
#define SYNC_BUFFER_SIZE

References sync_buffer_t::buffer, sync_buffer_t::buffer_size, log_debug, log_info, named_registry_for_each(), sync_buffer_t::offset, SAFE_FREE, SAFE_MALLOC, and SYNC_BUFFER_SIZE.

Referenced by crypto_handshake_init().

◆ debug_sync_print_state_delayed()

void debug_sync_print_state_delayed ( uint64_t  delay_ns)

#include <sync.h>

Schedule delayed sync state printing on debug thread.

Parameters
delay_nsDelay in nanoseconds before printing (e.g., 100 * 1000000 for 100ms)

Schedules debug_sync_print_state() to execute on the debug thread after the specified delay. Useful for capturing state snapshots at specific moments in execution (e.g., "print state 50ms from now, during this critical section").

The debug thread must be running (started via debug_sync_start_thread()).

Use Cases

// Capture state during a suspected deadlock region
void critical_section(void) {
// Schedule state dump 50ms from now (during execution)
// Do work...
work_that_might_deadlock();
// By the time this returns, state was dumped if deadlock happened
}
Note
Non-blocking: returns immediately, print happens on debug thread
Useful for production debugging with minimal impact
If multiple calls are made, they queue and execute sequentially
See also
debug_sync_start_thread() to ensure Debug thread is running

Schedule delayed sync state printing on debug thread.

Parameters
delay_nsNanoseconds to sleep before printing

Definition at line 604 of file sync.c.

604 {
605 mutex_lock(&g_debug_state_request.mutex);
606 g_debug_state_request.request_type = DEBUG_REQUEST_STATE;
607 g_debug_state_request.delay_ns = delay_ns;
608 atomic_store_bool(&g_debug_state_request.should_run, true);
609 cond_signal(&g_debug_state_request.cond);
610 mutex_unlock(&g_debug_state_request.mutex);
611}
@ DEBUG_REQUEST_STATE
Definition sync.c:436

References atomic_store_bool(), debug_state_request_t::cond, cond_signal(), DEBUG_REQUEST_STATE, debug_state_request_t::delay_ns, debug_state_request_t::mutex, mutex_lock, mutex_unlock, debug_state_request_t::request_type, and debug_state_request_t::should_run.

◆ debug_sync_set_main_thread_id()

void debug_sync_set_main_thread_id ( void  )

#include <sync.h>

Initialize debug synchronization system.

Returns
0 on success, non-zero on error

Called at startup to initialize internal structures for sync debugging. Must be called before debug_sync_start_thread().

Safe to call multiple times (idempotent).

See also
debug_sync_start_thread() to start the background Debug thread

Definition at line 639 of file sync.c.

639 {
640 // Save main thread ID for memory reporting (call very early)
641 g_debug_main_thread_id = asciichat_thread_current_id();
642}
uint64_t asciichat_thread_current_id(void)
Get the current thread's unique numeric ID.
Definition threading.c:92

References asciichat_thread_current_id().

Referenced by main().

◆ debug_sync_set_memory_report_interval()

void debug_sync_set_memory_report_interval ( uint64_t  interval_ns)

#include <sync.h>

Set periodic memory report interval.

Parameters
interval_nsInterval in nanoseconds (0 to disable)

Configures the debug sync thread to print memory reports at the specified interval. The first report will be printed after interval_ns nanoseconds, subsequent reports will follow at that interval.

Parameters
interval_nsNanoseconds between reports (0 disables periodic reporting)

Example:

// Print memory report every 5 seconds
void debug_sync_set_memory_report_interval(uint64_t interval_ns)
Set periodic memory report interval.
Definition sync.c:630
See also
debug_memory_report() for the memory report function being called
Parameters
interval_nsInterval in nanoseconds (0 to disable)

Definition at line 630 of file sync.c.

630 {
631 g_debug_state_request.memory_report_interval_ns = interval_ns;
632 g_debug_state_request.last_memory_report_time_ns = 0; // Reset timer
633}
uint64_t last_memory_report_time_ns
Definition sync.c:448
uint64_t memory_report_interval_ns
Definition sync.c:447

References debug_state_request_t::last_memory_report_time_ns, and debug_state_request_t::memory_report_interval_ns.

◆ debug_sync_start_thread()

int debug_sync_start_thread ( void  )

#include <sync.h>

Start background debug thread for scheduled operations.

Returns
0 on success, non-zero on error

Spawns a background thread that handles scheduled delayed printing operations from debug_sync_print_state_delayed() and debug_sync_print_backtrace_delayed().

Must call debug_sync_init() first.

Typical Initialization

// In main():
// Now safe to use delayed printing:
// At shutdown:
Note
Thread will block until first scheduled job arrives
No overhead if no delayed jobs are scheduled
Call debug_sync_cleanup_thread() during shutdown
See also
debug_sync_cleanup_thread() for Shutdown System

Definition at line 657 of file sync.c.

657 {
658 // Initialize mutex and condition variable for signal wakeup
659 if (!g_debug_state_request.initialized) {
660 mutex_init(&g_debug_state_request.mutex, "debug_sync_state");
661 cond_init(&g_debug_state_request.cond, "debug_sync_signal");
662 g_debug_state_request.initialized = true;
663 }
664
665 atomic_store_bool(&g_debug_state_request.should_exit, false);
666 int err = asciichat_thread_create(&g_debug_thread, "debug_sync", debug_print_thread_fn, NULL);
667 return err;
668}
int cond_init(cond_t *cond, const char *name)
Initialize a condition variable with a name.
#define asciichat_thread_create(thread_ptr, attr, start_routine, arg)

References asciichat_thread_create, atomic_store_bool(), debug_state_request_t::cond, cond_init(), debug_state_request_t::initialized, debug_state_request_t::mutex, mutex_init(), and debug_state_request_t::should_exit.

Referenced by main().

◆ debug_sync_trigger_print()

void debug_sync_trigger_print ( void  )

#include <sync.h>

Trigger sync state print immediately (synchronous)

Immediately calls debug_sync_print_state() on the current thread. Unlike debug_sync_print_state_delayed(), this is synchronous and blocking.

Useful for:

  • Debugging code (breakpoint followed by print)
  • Signal handlers that need instant output
  • Testing and validation

Example: Signal Handler

void handle_debug_signal(int sig) {
// Print state immediately in signal handler
}
signal(SIGUSR2, handle_debug_signal); // SIGUSR2 is mapped to sync state printing
// Now: kill -USR2 <pid> triggers immediate state print
Note
Blocks until print is complete
Can be called from signal handlers safely
See also
debug_sync_print_state() for the underlying function
debug_sync_print_state_delayed() for scheduled printing

Definition at line 710 of file sync.c.

710 {
711 // Set flag to trigger printing on debug thread (from SIGUSR1 handler).
712 // We don't call debug_sync_print_state() directly here to avoid logging
713 // in signal handler context, which could deadlock with logging mutexes.
714 // Uses atomic_store for thread-safe flag setting from signal handler.
715 //
716 // Signal the condition variable to wake up the debug thread immediately
717 // (without waiting for the 100ms timeout).
718 atomic_store_bool(&g_debug_state_request.signal_triggered, true);
719 cond_signal(&g_debug_state_request.cond);
720}
atomic_t signal_triggered
Definition sync.c:446

References atomic_store_bool(), debug_state_request_t::cond, cond_signal(), and debug_state_request_t::signal_triggered.

Referenced by session_handle_keyboard_input().