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
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 |
|
}
|
|
|