You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

100 lines
3.9 KiB

#include "test_clock.h"
#include "../lib/debug_config.h"
#include <stdlib.h>
#include <time.h>
#ifdef __linux__
#include <sys/epoll.h>
#include <poll.h>
static int accelerated;
static uint64_t offset_us, limit_us, advances, io_events;
static struct UASYNC* polling;
static uint64_t step_us;
int __real_clock_gettime(clockid_t id, struct timespec* ts);
int __wrap_clock_gettime(clockid_t id, struct timespec* ts) {
int result = __real_clock_gettime(id, ts);
if (!result && (id == CLOCK_BOOTTIME || id == CLOCK_MONOTONIC)) {
uint64_t ns = (uint64_t)ts->tv_nsec + __atomic_load_n(&offset_us, __ATOMIC_RELAXED) * 1000;
ts->tv_sec += ns / 1000000000; ts->tv_nsec = ns % 1000000000;
}
return result;
}
/* Продвигаем время внутри wait: uasync видит нормальное ожидание, а не искусственный stall между poll. */
static void advance_idle(int result, int timeout_ms) {
if (!polling) return;
if (result > 0) { io_events += result; return; }
if (result < 0 || !timeout_ms || polling->immediate_queue_head) return;
uint64_t now = get_time_us(), end = now + step_us;
TimeoutEntry next;
if (!timeout_heap_peek(polling->timeout_heap, &next) && next.expiration * 1000 < end) end = next.expiration * 1000;
if (limit_us && limit_us < end) end = limit_us;
if (end <= now) return;
__atomic_fetch_add(&offset_us, end - now, __ATOMIC_RELAXED); advances++;
}
int __real_epoll_wait(int fd, struct epoll_event* events, int count, int timeout);
int __wrap_epoll_wait(int fd, struct epoll_event* events, int count, int timeout) {
int wait_ms = polling ? (timeout && polling->socket_alloc_count - polling->socket_free_count > 1 ? 1 : 0) : timeout;
int result = __real_epoll_wait(fd, events, count, wait_ms);
advance_idle(result, timeout);
return result;
}
int __real_poll(struct pollfd* fds, nfds_t count, int timeout);
int __wrap_poll(struct pollfd* fds, nfds_t count, int timeout) {
int wait_ms = polling ? (timeout && count > 1 ? 1 : 0) : timeout;
int result = __real_poll(fds, count, wait_ms);
advance_idle(result, timeout);
return result;
}
#endif
void test_clock_init(int max_step_tb) {
if (max_step_tb <= 0) { DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "test clock: invalid step=%d", max_step_tb); abort(); }
debug_set_category_level(DEBUG_CATEGORY_DEBUG, DEBUG_LEVEL_INFO);
#ifdef __linux__
accelerated = getenv("UTUN_TEST_REAL_TIME") == NULL;
step_us = (uint64_t)max_step_tb * 100;
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "test clock: mode=%s max_step_tb=%d; native timers and real I/O",
accelerated ? "accelerated" : "real", max_step_tb);
#else
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "test clock: real time on this platform");
#endif
}
void test_clock_poll(struct UASYNC* ua, int timeout_tb) {
#ifdef __linux__
if (accelerated && timeout_tb > 0) {
if (polling) { DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "test clock: nested poll is unsupported"); abort(); }
polling = ua;
uasync_poll(ua, timeout_tb);
polling = NULL;
return;
}
#endif
uasync_poll(ua, timeout_tb);
}
void test_clock_run_for(struct UASYNC* ua, int duration_tb) {
if (duration_tb < 0) { DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "test clock: negative duration=%d", duration_tb); abort(); }
uint64_t end = get_time_us() + (uint64_t)duration_tb * 100;
#ifdef __linux__
if (limit_us) { DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "test clock: nested time interval is unsupported"); abort(); }
limit_us = end;
#endif
while (get_time_us() < end) test_clock_poll(ua, 5);
#ifdef __linux__
limit_us = 0;
#endif
}
void test_clock_finish(void) {
#ifdef __linux__
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "test clock: completed mode=%s advanced_tb=%llu steps=%llu real_io_events=%llu",
accelerated ? "accelerated" : "real", (unsigned long long)(offset_us / 100),
(unsigned long long)advances, (unsigned long long)io_events);
#endif
}