7#include "ascii-chat/common/error_codes.h"
8#include <ascii-chat/util/time.h>
10#undef SOKOL_TIME_INCLUDED
11#define SOKOL_TIME_IMPL
12#include <ascii-chat-deps/sokol/sokol_time.h>
13#include <ascii-chat/common.h>
14#include <ascii-chat/asciichat_errno.h>
15#include <ascii-chat/platform/rwlock.h>
16#include <ascii-chat/platform/abstraction.h>
42static bool g_sokol_time_initialized =
false;
52 if (!g_sokol_time_initialized) {
54 g_sokol_time_initialized =
true;
56 return (uint64_t)stm_ns(stm_now());
64 GetSystemTimePreciseAsFileTime(&ft);
66 uint64_t filetime = ((uint64_t)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
68 return filetime * 100;
72 if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
83 unsigned int usec = (
unsigned int)(ns / 1000);
84 if (usec == 0 && ns > 0) {
93 if (end_ns >= start_ns) {
94 return end_ns - start_ns;
97 return (UINT64_MAX - start_ns) + end_ns + 1;
106 SET_ERRNO(ERROR_INVALID_PARAM,
"null ts");
109 return (uint64_t)ts->tv_sec * NS_PER_SEC_INT + (uint64_t)ts->tv_nsec;
114 SET_ERRNO(ERROR_INVALID_PARAM,
"null ts");
117 ts->tv_sec = (time_t)(ns / NS_PER_SEC_INT);
118 ts->tv_nsec = (long)(ns % NS_PER_SEC_INT);
126 if (g_timer_manager.initialized) {
135 SET_ERRNO(ERROR_PLATFORM_INIT,
"Failed to initialize timer rwlock");
139 g_timer_manager.timers = NULL;
140 g_timer_manager.initialized =
true;
142 log_dev(
"Timer system initialized");
147 if (!g_timer_manager.initialized) {
151 rwlock_wrlock(&g_timer_manager.rwlock);
155 timer_record_t *current, *tmp;
156 HASH_ITER(hh, g_timer_manager.timers, current, tmp) {
157 HASH_DEL(g_timer_manager.timers, current);
158 SAFE_FREE(current->name);
163 g_timer_manager.timers = NULL;
164 g_timer_manager.initialized =
false;
166 rwlock_wrunlock(&g_timer_manager.rwlock);
167 rwlock_destroy(&g_timer_manager.rwlock);
169 log_debug(
"Timer system cleaned up (freed %d timers)", timer_count);
173 if (!g_timer_manager.initialized) {
174 SET_ERRNO(ERROR_INVALID_STATE,
"Timer system not initialized");
179 SET_ERRNO(ERROR_INVALID_PARAM,
"Timer name is NULL");
183 rwlock_wrlock(&g_timer_manager.rwlock);
186 timer_record_t *existing = NULL;
187 HASH_FIND_STR(g_timer_manager.timers, name, existing);
190 rwlock_wrunlock(&g_timer_manager.rwlock);
191 SET_ERRNO(ERROR_INVALID_STATE,
"Timer '%s' already exists - timers must have unique names", name);
196 timer_record_t *timer = SAFE_MALLOC(
sizeof(timer_record_t), timer_record_t *);
198 rwlock_wrunlock(&g_timer_manager.rwlock);
199 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate timer record");
204 size_t name_len = strlen(name) + 1;
205 timer->name = SAFE_MALLOC(name_len,
char *);
208 rwlock_wrunlock(&g_timer_manager.rwlock);
209 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate timer name");
212 SAFE_STRNCPY(timer->name, name, name_len);
214 timer->start_ticks = stm_now();
217 HASH_ADD_KEYPTR(hh, g_timer_manager.timers, timer->name, strlen(timer->name), timer);
219 rwlock_wrunlock(&g_timer_manager.rwlock);
224 if (!g_timer_manager.initialized) {
225 SET_ERRNO(ERROR_INVALID_STATE,
"Timer system not initialized");
230 SET_ERRNO(ERROR_INVALID_PARAM,
"Timer name is NULL");
234 rwlock_wrlock(&g_timer_manager.rwlock);
237 timer_record_t *timer = NULL;
238 HASH_FIND_STR(g_timer_manager.timers, name, timer);
241 rwlock_wrunlock(&g_timer_manager.rwlock);
242 log_warn(
"Timer '%s' not found", name);
247 uint64_t end_ticks = stm_now();
248 uint64_t elapsed_ticks = stm_diff(end_ticks, timer->start_ticks);
249 double elapsed_ns = stm_ns(elapsed_ticks);
252 char duration_str[32];
256 log_dev(
"Timer '%s': %s", name, duration_str);
259 HASH_DEL(g_timer_manager.timers, timer);
260 SAFE_FREE(timer->name);
263 rwlock_wrunlock(&g_timer_manager.rwlock);
268 return g_timer_manager.initialized;
281 if (nanoseconds < 0) {
282 nanoseconds = -nanoseconds;
288 if (nanoseconds < NS_PER_US) {
292 else if (nanoseconds < NS_PER_MS) {
293 double us = nanoseconds / NS_PER_US;
301 else if (nanoseconds < NS_PER_SEC) {
302 double ms = nanoseconds / NS_PER_MS;
310 else if (nanoseconds < NS_PER_MIN) {
311 double s = nanoseconds / NS_PER_SEC;
319 else if (nanoseconds < NS_PER_HOUR) {
320 int minutes = (int)(nanoseconds / NS_PER_MIN);
321 int seconds = (int)((nanoseconds - (minutes * NS_PER_MIN)) / NS_PER_SEC);
325 else if (nanoseconds < NS_PER_DAY) {
326 int hours = (int)(nanoseconds / NS_PER_HOUR);
327 double remaining_ns = nanoseconds - ((double)hours * NS_PER_HOUR);
328 int minutes = (int)(remaining_ns / NS_PER_MIN);
329 double remaining_after_min = remaining_ns - ((double)minutes * NS_PER_MIN);
330 int seconds = (int)(remaining_after_min / NS_PER_SEC);
334 else if (nanoseconds < NS_PER_YEAR) {
335 int days = (int)(nanoseconds / NS_PER_DAY);
336 double remaining_ns = nanoseconds - ((double)days * NS_PER_DAY);
337 int hours = (int)(remaining_ns / NS_PER_HOUR);
338 remaining_ns = remaining_ns - ((double)hours * NS_PER_HOUR);
339 int minutes = (int)(remaining_ns / NS_PER_MIN);
340 double remaining_after_min = remaining_ns - ((double)minutes * NS_PER_MIN);
341 int seconds = (int)(remaining_after_min / NS_PER_SEC);
346 double years = nanoseconds / NS_PER_YEAR;
350 if (written < 0 || (
size_t)written >=
buffer_size) {
359 double nanoseconds = milliseconds * NS_PER_MS;
365 double nanoseconds = seconds * NS_PER_SEC;
375 if (hours < 0 || minutes < 0 || minutes >= 60 || seconds < 0 || seconds >= 60) {
382 if (written < 0 || (
size_t)written >=
buffer_size) {
394 if (!state || !config) {
399 state->config = *config;
402 state->current_speed_multiplier = config->min_speed_multiplier;
405 state->last_sleep_ns = config->baseline_sleep_ns;
413 const adaptive_sleep_config_t *cfg = &state->config;
416 double desired_multiplier;
418 if (queue_depth > target_depth) {
421 size_t backlog = queue_depth - target_depth;
426 double backlog_factor = 1.0 + ((double)backlog / (
double)(target_depth + 1));
427 desired_multiplier = cfg->min_speed_multiplier * backlog_factor;
430 if (desired_multiplier > cfg->max_speed_multiplier) {
431 desired_multiplier = cfg->max_speed_multiplier;
435 double delta = desired_multiplier - state->current_speed_multiplier;
436 state->current_speed_multiplier += delta * cfg->speedup_rate;
440 desired_multiplier = cfg->min_speed_multiplier;
443 double delta = desired_multiplier - state->current_speed_multiplier;
444 state->current_speed_multiplier += delta * cfg->slowdown_rate;
448 if (state->current_speed_multiplier < cfg->min_speed_multiplier) {
449 state->current_speed_multiplier = cfg->min_speed_multiplier;
451 if (state->current_speed_multiplier > cfg->max_speed_multiplier) {
452 state->current_speed_multiplier = cfg->max_speed_multiplier;
457 uint64_t sleep_ns = (uint64_t)(cfg->baseline_sleep_ns / state->current_speed_multiplier);
460 state->last_sleep_ns = sleep_ns;
469 platform_sleep_ns(sleep_ns);
481static const char SUPPORTED_SPECIFIERS[] =
"YmmdHMSaAbBjwuIpZzcxXFTsGgV";
488 for (
const char *p = format_str; *p; p++) {
493 log_error(
"Invalid time format: unterminated %% at end");
503 if (*p ==
'-' || *p ==
'0' || *p ==
'+' || *p ==
' ') {
506 log_error(
"Invalid time format: flag character without specifier");
512 if (*p ==
'*' || isdigit((
unsigned char)*p)) {
513 while (*p && (isdigit((
unsigned char)*p) || *p ==
'*')) {
517 log_error(
"Invalid time format: width specifier without specifier character");
526 log_error(
"Invalid time format: precision specifier incomplete");
529 while (*p && (isdigit((
unsigned char)*p) || *p ==
'*')) {
533 log_error(
"Invalid time format: precision specifier without specifier character");
539 if (*p ==
'E' || *p ==
'O') {
542 log_error(
"Invalid time format: modifier character without specifier");
548 if (!strchr(SUPPORTED_SPECIFIERS, *p)) {
549 log_error(
"Invalid time format: unsupported specifier %%%c (at position %ld)", *p, (p - format_str));
559 if (!format_str || !buf || buf_size < 2) {
560 SET_ERRNO(ERROR_INVALID_PARAM,
"time_format_now: invalid arguments - format_str=%p, buf=%p, buf_size=%zu",
561 format_str, buf, buf_size);
569 time_t seconds = (time_t)(ts_ns / NS_PER_SEC_INT);
570 long nanoseconds = (long)(ts_ns % NS_PER_SEC_INT);
577 size_t len = strftime(buf, buf_size, format_str, &tm_info);
582 log_debug(
"strftime returned 0 for format: %s", format_str);
586 if (len >= buf_size - 1) {
588 log_error(
"time_format_now: buffer too small (need %zu, have %zu)", len + 1, buf_size);
598 return SET_ERRNO(ERROR_INVALID_STATE,
"time_format_safe: format_str is NULL");
602 return SET_ERRNO(ERROR_INVALID_STATE,
"time_format_safe: buf is NULL");
607 return SET_ERRNO(ERROR_INVALID_STATE,
"time_format_safe: buffer too small (minimum 64 bytes, got %zu)", buf_size);
612 return SET_ERRNO(ERROR_INVALID_STATE,
"time_format_safe: invalid time format: %s", format_str);
618 return SET_ERRNO(ERROR_INVALID_STATE,
"time_format_safe: strftime formatting failed");
int buffer_size
Size of circular buffer.
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
int rwlock_init(rwlock_t *rwlock)
double timer_stop(const char *name)
bool timer_start(const char *name)
bool time_format_is_valid_strftime(const char *format_str)
rwlock_t rwlock
Read-write lock for thread-safe access (uthash requires external locking)
uint64_t time_get_ns(void)
void time_ns_to_timespec(uint64_t ns, struct timespec *ts)
uint64_t time_get_realtime_ns(void)
int format_duration_ns(double nanoseconds, char *buffer, size_t buffer_size)
uint64_t adaptive_sleep_calculate(adaptive_sleep_state_t *state, size_t queue_depth, size_t target_depth)
int time_format_now(const char *format_str, char *buf, size_t buf_size)
uint64_t time_timespec_to_ns(const struct timespec *ts)
void timer_system_destroy(void)
int format_duration_s(double seconds, char *buffer, size_t buffer_size)
void adaptive_sleep_init(adaptive_sleep_state_t *state, const adaptive_sleep_config_t *config)
uint64_t time_elapsed_ns(uint64_t start_ns, uint64_t end_ns)
int format_duration_ms(double milliseconds, char *buffer, size_t buffer_size)
int format_uptime_hms(int hours, int minutes, int seconds, char *buffer, size_t buffer_size)
timer_record_t * timers
Hash table of active timers (uthash head pointer)
void time_sleep_ns(uint64_t ns)
bool timer_is_initialized(void)
bool timer_system_init(void)
void adaptive_sleep_do(adaptive_sleep_state_t *state, size_t queue_depth, size_t target_depth)
asciichat_error_t time_format_safe(const char *format_str, char *buf, size_t buf_size)
bool initialized
Initialization state.
asciichat_error_t platform_localtime(const time_t *timer, struct tm *result)