ascii-chat 0.6.0
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
time.c
Go to the documentation of this file.
1
7#define SOKOL_IMPL
8#include "time.h"
9#include "../common.h"
10#include "../asciichat_errno.h"
11#include "../platform/rwlock.h"
12#include "../platform/abstraction.h"
13#include "uthash.h"
14#include <stdlib.h>
15#include <string.h>
16#include <stdio.h>
17#include <math.h>
18
19// ============================================================================
20// Global State
21// ============================================================================
22
26static struct {
30} g_timer_manager = {
31 .timers = NULL,
32 .initialized = false,
33};
34
35// ============================================================================
36// Timer System Implementation
37// ============================================================================
38
40 if (g_timer_manager.initialized) {
41 return true;
42 }
43
44 // Initialize sokol_time
45 stm_setup();
46
47 // Initialize rwlock (uthash requires external locking for writes, rwlock allows concurrent reads)
48 if (rwlock_init(&g_timer_manager.rwlock) != 0) {
49 SET_ERRNO(ERROR_PLATFORM_INIT, "Failed to initialize timer rwlock");
50 return false;
51 }
52
53 g_timer_manager.timers = NULL;
54 g_timer_manager.initialized = true;
55
56 log_debug("Timer system initialized");
57 return true;
58}
59
61 if (!g_timer_manager.initialized) {
62 return;
63 }
64
65 rwlock_wrlock(&g_timer_manager.rwlock);
66
67 // Free all timer records
68 timer_record_t *current, *tmp;
69 HASH_ITER(hh, g_timer_manager.timers, current, tmp) {
70 HASH_DEL(g_timer_manager.timers, current);
71 SAFE_FREE(current->name);
72 SAFE_FREE(current);
73 }
74
75 g_timer_manager.timers = NULL;
76 g_timer_manager.initialized = false;
77
78 rwlock_wrunlock(&g_timer_manager.rwlock);
79 rwlock_destroy(&g_timer_manager.rwlock);
80
81 log_debug("Timer system cleaned up");
82}
83
84bool timer_start(const char *name) {
85 if (!g_timer_manager.initialized) {
86 SET_ERRNO(ERROR_INVALID_STATE, "Timer system not initialized");
87 return false;
88 }
89
90 if (!name) {
91 SET_ERRNO(ERROR_INVALID_PARAM, "Timer name is NULL");
92 return false;
93 }
94
95 rwlock_wrlock(&g_timer_manager.rwlock);
96
97 // Check if timer already exists - this is an error, timers must be unique
98 timer_record_t *existing = NULL;
99 HASH_FIND_STR(g_timer_manager.timers, name, existing);
100
101 if (existing) {
102 rwlock_wrunlock(&g_timer_manager.rwlock);
103 SET_ERRNO(ERROR_INVALID_STATE, "Timer '%s' already exists - timers must have unique names", name);
104 return false;
105 }
106
107 // Create new timer record
109 if (!timer) {
110 rwlock_wrunlock(&g_timer_manager.rwlock);
111 SET_ERRNO(ERROR_MEMORY, "Failed to allocate timer record");
112 return false;
113 }
114
115 // Use platform-safe string duplication
116 size_t name_len = strlen(name) + 1;
117 timer->name = SAFE_MALLOC(name_len, char *);
118 if (!timer->name) {
119 SAFE_FREE(timer);
120 rwlock_wrunlock(&g_timer_manager.rwlock);
121 SET_ERRNO(ERROR_MEMORY, "Failed to allocate timer name");
122 return false;
123 }
124 SAFE_STRNCPY(timer->name, name, name_len);
125
126 timer->start_ticks = stm_now();
127
128 // Add to hashtable
129 HASH_ADD_KEYPTR(hh, g_timer_manager.timers, timer->name, strlen(timer->name), timer);
130
131 rwlock_wrunlock(&g_timer_manager.rwlock);
132 return true;
133}
134
135double timer_stop(const char *name) {
136 if (!g_timer_manager.initialized) {
137 SET_ERRNO(ERROR_INVALID_STATE, "Timer system not initialized");
138 return -1.0;
139 }
140
141 if (!name) {
142 SET_ERRNO(ERROR_INVALID_PARAM, "Timer name is NULL");
143 return -1.0;
144 }
145
146 rwlock_wrlock(&g_timer_manager.rwlock);
147
148 // Find timer
149 timer_record_t *timer = NULL;
150 HASH_FIND_STR(g_timer_manager.timers, name, timer);
151
152 if (!timer) {
153 rwlock_wrunlock(&g_timer_manager.rwlock);
154 log_warn("Timer '%s' not found", name);
155 return -1.0;
156 }
157
158 // Calculate elapsed time in nanoseconds for maximum precision
159 uint64_t end_ticks = stm_now();
160 uint64_t elapsed_ticks = stm_diff(end_ticks, timer->start_ticks);
161 double elapsed_ns = stm_ns(elapsed_ticks);
162
163 // Format duration for human-readable logging
164 char duration_str[32];
165 format_duration_ns(elapsed_ns, duration_str, sizeof(duration_str));
166
167 // Log the result (debug level - caller can use return value for production logging)
168 log_debug("Timer '%s': %s", name, duration_str);
169
170 // Remove from hashtable
171 HASH_DEL(g_timer_manager.timers, timer);
172 SAFE_FREE(timer->name);
173 SAFE_FREE(timer);
174
175 rwlock_wrunlock(&g_timer_manager.rwlock);
176 return elapsed_ns; // Return nanoseconds
177}
178
180 return g_timer_manager.initialized;
181}
182
183// ============================================================================
184// Time Formatting Implementation
185// ============================================================================
186
187int format_duration_ns(double nanoseconds, char *buffer, size_t buffer_size) {
188 if (!buffer || buffer_size == 0) {
189 return -1;
190 }
191
192 // Handle negative durations
193 if (nanoseconds < 0) {
194 nanoseconds = -nanoseconds;
195 }
196
197 int written = 0;
198
199 // Nanoseconds (< 1µs)
200 if (nanoseconds < NS_PER_US) {
201 written = snprintf(buffer, buffer_size, "%.0fns", nanoseconds);
202 }
203 // Microseconds (< 1ms)
204 else if (nanoseconds < NS_PER_MS) {
205 double us = nanoseconds / NS_PER_US;
206 if (us < 10.0) {
207 written = snprintf(buffer, buffer_size, "%.1fµs", us);
208 } else {
209 written = snprintf(buffer, buffer_size, "%.0fµs", us);
210 }
211 }
212 // Milliseconds (< 1s)
213 else if (nanoseconds < NS_PER_SEC) {
214 double ms = nanoseconds / NS_PER_MS;
215 if (ms < 10.0) {
216 written = snprintf(buffer, buffer_size, "%.1fms", ms);
217 } else {
218 written = snprintf(buffer, buffer_size, "%.0fms", ms);
219 }
220 }
221 // Seconds (< 1m)
222 else if (nanoseconds < NS_PER_MIN) {
223 double s = nanoseconds / NS_PER_SEC;
224 if (s < 10.0) {
225 written = snprintf(buffer, buffer_size, "%.2fs", s);
226 } else {
227 written = snprintf(buffer, buffer_size, "%.1fs", s);
228 }
229 }
230 // Minutes (< 1h) - show minutes and seconds
231 else if (nanoseconds < NS_PER_HOUR) {
232 int minutes = (int)(nanoseconds / NS_PER_MIN);
233 int seconds = (int)((nanoseconds - (minutes * NS_PER_MIN)) / NS_PER_SEC);
234 written = snprintf(buffer, buffer_size, "%dm%ds", minutes, seconds);
235 }
236 // Hours (< 1d) - show hours, minutes, and seconds
237 else if (nanoseconds < NS_PER_DAY) {
238 int hours = (int)(nanoseconds / NS_PER_HOUR);
239 double remaining_ns = nanoseconds - ((double)hours * NS_PER_HOUR);
240 int minutes = (int)(remaining_ns / NS_PER_MIN);
241 double remaining_after_min = remaining_ns - ((double)minutes * NS_PER_MIN);
242 int seconds = (int)(remaining_after_min / NS_PER_SEC);
243 written = snprintf(buffer, buffer_size, "%dh%dm%ds", hours, minutes, seconds);
244 }
245 // Days (< 1y) - show days, hours, minutes, and seconds
246 else if (nanoseconds < NS_PER_YEAR) {
247 int days = (int)(nanoseconds / NS_PER_DAY);
248 double remaining_ns = nanoseconds - ((double)days * NS_PER_DAY);
249 int hours = (int)(remaining_ns / NS_PER_HOUR);
250 remaining_ns = remaining_ns - ((double)hours * NS_PER_HOUR);
251 int minutes = (int)(remaining_ns / NS_PER_MIN);
252 double remaining_after_min = remaining_ns - ((double)minutes * NS_PER_MIN);
253 int seconds = (int)(remaining_after_min / NS_PER_SEC);
254 written = snprintf(buffer, buffer_size, "%dd%dh%dm%ds", days, hours, minutes, seconds);
255 }
256 // Years - show years with one decimal
257 else {
258 double years = nanoseconds / NS_PER_YEAR;
259 written = snprintf(buffer, buffer_size, "%.1fy", years);
260 }
261
262 if (written < 0 || (size_t)written >= buffer_size) {
263 return -1;
264 }
265
266 return written;
267}
268
269int format_duration_ms(double milliseconds, char *buffer, size_t buffer_size) {
270 // Convert milliseconds to nanoseconds and use the nanosecond formatter
271 double nanoseconds = milliseconds * NS_PER_MS;
272 return format_duration_ns(nanoseconds, buffer, buffer_size);
273}
274
275int format_duration_s(double seconds, char *buffer, size_t buffer_size) {
276 // Convert seconds to nanoseconds and use the nanosecond formatter
277 double nanoseconds = seconds * NS_PER_SEC;
278 return format_duration_ns(nanoseconds, buffer, buffer_size);
279}
280
281// ============================================================================
282// Adaptive Sleep Implementation
283// ============================================================================
284
286 if (!state || !config) {
287 return;
288 }
289
290 // Copy configuration
291 state->config = *config;
292
293 // Start at baseline speed (no speedup initially)
295
296 // Initial sleep is the baseline
297 state->last_sleep_ns = config->baseline_sleep_ns;
298}
299
300uint64_t adaptive_sleep_calculate(adaptive_sleep_state_t *state, size_t queue_depth, size_t target_depth) {
301 if (!state) {
302 return 0;
303 }
304
305 const adaptive_sleep_config_t *cfg = &state->config;
306
307 // Determine if we need to speed up or slow down
308 double desired_multiplier;
309
310 if (queue_depth > target_depth) {
311 // Queue is building up - speed up processing
312 // The more items in queue, the faster we should process
313 size_t backlog = queue_depth - target_depth;
314
315 // Calculate desired speed based on backlog
316 // Each item above target increases speed proportionally
317 // This ensures we drain the queue faster the more backed up we are
318 double backlog_factor = 1.0 + ((double)backlog / (double)(target_depth + 1));
319 desired_multiplier = cfg->min_speed_multiplier * backlog_factor;
320
321 // Clamp to max speed
322 if (desired_multiplier > cfg->max_speed_multiplier) {
323 desired_multiplier = cfg->max_speed_multiplier;
324 }
325
326 // Ramp up gradually based on speedup_rate
327 double delta = desired_multiplier - state->current_speed_multiplier;
328 state->current_speed_multiplier += delta * cfg->speedup_rate;
329
330 } else {
331 // Queue is at or below target - slow down to baseline
332 desired_multiplier = cfg->min_speed_multiplier;
333
334 // Ramp down gradually based on slowdown_rate
335 double delta = desired_multiplier - state->current_speed_multiplier;
336 state->current_speed_multiplier += delta * cfg->slowdown_rate;
337 }
338
339 // Ensure we stay within configured bounds
342 }
345 }
346
347 // Calculate actual sleep time: baseline / speed_multiplier
348 // Higher multiplier = shorter sleep = faster processing
350
351 // Store for debugging
352 state->last_sleep_ns = sleep_ns;
353
354 return sleep_ns;
355}
356
357void adaptive_sleep_do(adaptive_sleep_state_t *state, size_t queue_depth, size_t target_depth) {
358 uint64_t sleep_ns = adaptive_sleep_calculate(state, queue_depth, target_depth);
359
360 if (sleep_ns > 0) {
361 // Convert nanoseconds to microseconds for platform_sleep_usec
362 uint64_t sleep_us = sleep_ns / 1000;
363 if (sleep_us > 0) {
364 platform_sleep_usec(sleep_us);
365 }
366 }
367}
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:358
#define SAFE_FREE(ptr)
Definition common.h:320
#define SAFE_MALLOC(size, cast)
Definition common.h:208
unsigned long long uint64_t
Definition common.h:59
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
@ ERROR_INVALID_STATE
@ ERROR_PLATFORM_INIT
Definition error_codes.h:57
@ ERROR_MEMORY
Definition error_codes.h:53
@ ERROR_INVALID_PARAM
#define log_warn(...)
Log a WARN message.
#define log_debug(...)
Log a DEBUG message.
double timer_stop(const char *name)
Stop a named timer and return elapsed time.
Definition time.c:135
void timer_system_cleanup(void)
Cleanup the timing system.
Definition time.c:60
bool timer_start(const char *name)
Start a named timer.
Definition time.c:84
int format_duration_ns(double nanoseconds, char *buffer, size_t buffer_size)
Format nanoseconds as human-readable duration string.
Definition time.c:187
#define NS_PER_HOUR
Definition time.h:184
uint64_t adaptive_sleep_calculate(adaptive_sleep_state_t *state, size_t queue_depth, size_t target_depth)
Calculate adaptive sleep time based on queue depth.
Definition time.c:300
#define NS_PER_MIN
Definition time.h:183
#define NS_PER_DAY
Definition time.h:185
int format_duration_s(double seconds, char *buffer, size_t buffer_size)
Format seconds as human-readable duration string.
Definition time.c:275
#define NS_PER_YEAR
Definition time.h:186
#define NS_PER_MS
Definition time.h:181
void adaptive_sleep_init(adaptive_sleep_state_t *state, const adaptive_sleep_config_t *config)
Initialize adaptive sleep state with configuration.
Definition time.c:285
int format_duration_ms(double milliseconds, char *buffer, size_t buffer_size)
Format milliseconds as human-readable duration string.
Definition time.c:269
bool timer_is_initialized(void)
Check if timing system is initialized.
Definition time.c:179
bool timer_system_init(void)
Initialize the timing system.
Definition time.c:39
void adaptive_sleep_do(adaptive_sleep_state_t *state, size_t queue_depth, size_t target_depth)
Calculate sleep time and immediately sleep for that duration.
Definition time.c:357
#define NS_PER_US
Definition time.h:180
#define NS_PER_SEC
Definition time.h:182
#define rwlock_wrunlock(lock)
Release a write lock (with debug tracking in debug builds)
Definition rwlock.h:249
int rwlock_destroy(rwlock_t *lock)
Destroy a read-write lock.
void platform_sleep_usec(unsigned int usec)
High-precision sleep function with microsecond precision.
pthread_rwlock_t rwlock_t
Read-write lock type (POSIX: pthread_rwlock_t)
Definition rwlock.h:40
#define rwlock_wrlock(lock)
Acquire a write lock (with debug tracking in debug builds)
Definition rwlock.h:213
int rwlock_init(rwlock_t *lock)
Initialize a read-write lock.
🔢 Mathematical Utility Functions
Configuration for adaptive sleep behavior.
Definition time.h:294
double min_speed_multiplier
Minimum speed (max sleep) - usually 1.0 (baseline speed)
Definition time.h:296
double max_speed_multiplier
Maximum speed (min sleep) - e.g., 4.0 = process 4x faster.
Definition time.h:297
uint64_t baseline_sleep_ns
Normal sleep time in nanoseconds (when queue is at target)
Definition time.h:295
double speedup_rate
Ramp-up rate when queue builds (0.0-1.0, higher = faster ramp)
Definition time.h:298
double slowdown_rate
Ramp-down rate when queue empties (0.0-1.0, higher = faster ramp)
Definition time.h:299
Runtime state for adaptive sleep.
Definition time.h:310
double current_speed_multiplier
Current speed state (1.0 = baseline, >1.0 = faster)
Definition time.h:312
uint64_t last_sleep_ns
Last calculated sleep time (for debugging)
Definition time.h:313
adaptive_sleep_config_t config
Configuration (copied, not referenced)
Definition time.h:311
Individual timer record for a named timing operation.
Definition time.h:56
uint64_t start_ticks
Start time in sokol ticks.
Definition time.h:58
char * name
Timer name (heap-allocated, unique key)
Definition time.h:57
rwlock_t rwlock
Read-write lock for thread-safe access (uthash requires external locking)
Definition time.c:28
timer_record_t * timers
Hash table of active timers (uthash head pointer)
Definition time.c:27
bool initialized
Initialization state.
Definition time.c:29
⏱️ High-precision timing utilities using sokol_time.h and uthash
#️⃣ Wrapper for uthash.h that ensures common.h is included first
🔤 String Manipulation and Shell Escaping Utilities