From 6b235c4b3e0ff537bff312c5f18104e657b02883 Mon Sep 17 00:00:00 2001 From: pk910 Date: Sat, 17 Nov 2012 04:02:39 +0100 Subject: [PATCH] [IOMultiplexer] added possibility for higher resolution timer (select backend - all the others are quite imprecise!) --- src/IOEngine_select.c | 37 ++++++++++++++++++++++++++++++++----- src/IOHandler.c | 37 ++++++++++++++++++++++++++++--------- src/IOHandler.h | 4 ++++ src/test/timer/iotest.c | 2 ++ 4 files changed, 66 insertions(+), 14 deletions(-) diff --git a/src/IOEngine_select.c b/src/IOEngine_select.c index 2d85d7c..48d7fed 100644 --- a/src/IOEngine_select.c +++ b/src/IOEngine_select.c @@ -16,6 +16,7 @@ */ #include "IOEngine.h" #include +#include #ifdef WIN32 #define _WIN32_WINNT 0x501 #include @@ -25,8 +26,10 @@ #include #endif +static int engine_select_timer_delay_fix; + static int engine_select_init() { - /* empty */ + engine_select_timer_delay_fix = 0; return 1; } @@ -53,6 +56,7 @@ static void engine_select_loop(struct timeval *timeout) { struct IODescriptor *iofd, *tmp_iofd; struct timeval now, tdiff; int select_result; + int timer_fix; gettimeofday(&now, NULL); @@ -60,6 +64,7 @@ static void engine_select_loop(struct timeval *timeout) { FD_ZERO(&read_fds); FD_ZERO(&write_fds); + select_result = 0; for(iofd = first_descriptor; iofd; iofd = tmp_iofd) { tmp_iofd = iofd->next; if(iofd->type == IOTYPE_STDIN) { @@ -96,6 +101,7 @@ static void engine_select_loop(struct timeval *timeout) { if(iofd->fd > fds_size) fds_size = iofd->fd; FD_SET(iofd->fd, &read_fds); + select_result++; #endif } else if(iofd->type == IOTYPE_SERVER || iofd->type == IOTYPE_CLIENT) { @@ -104,6 +110,7 @@ static void engine_select_loop(struct timeval *timeout) { if(iofd->fd > fds_size) fds_size = iofd->fd; FD_SET(iofd->fd, &read_fds); + select_result++; if(iohandler_wants_writes(iofd)) FD_SET(iofd->fd, &write_fds); } @@ -122,13 +129,28 @@ static void engine_select_loop(struct timeval *timeout) { } if(timeval_is_smaler((&tdiff), timeout)) { timeout->tv_sec = tdiff.tv_sec; - timeout->tv_usec = tdiff.tv_usec; + timeout->tv_usec = tdiff.tv_usec + engine_select_timer_delay_fix; + if(timeout->tv_usec < 0) { + timeout->tv_sec--; + timeout->tv_usec += 1000000; + } } break; } - //select system call - select_result = select(fds_size + 1, &read_fds, &write_fds, NULL, timeout); + if(select_result) { + //select system call + select_result = select(fds_size + 1, &read_fds, &write_fds, NULL, timeout); + } else { + #ifdef WIN32 + Sleep((timeout->tv_sec * 1000) + (timeout->tv_usec / 1000) + 1); + #else + struct timespec usleep_time; + usleep_time.tv_sec = timeout->tv_sec; + usleep_time.tv_nsec = timeout->tv_usec * 1000; + nanosleep(&usleep_time, NULL); + #endif + } if (select_result < 0) { if (errno != EINTR) { @@ -154,7 +176,12 @@ static void engine_select_loop(struct timeval *timeout) { while(timer_priority) { tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec; tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec; - if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) { + timer_fix = (tdiff.tv_sec * 1000000) + tdiff.tv_usec; + if(timer_fix <= 100) { + if(timer_fix + 100 < engine_select_timer_delay_fix || timer_fix - 100 > engine_select_timer_delay_fix) { + engine_select_timer_delay_fix = ((engine_select_timer_delay_fix * 19) + timer_fix) / 20; + iohandler_log(IOLOG_DEBUG, "timer delay fix set to: %d us", engine_select_timer_delay_fix); + } iohandler_events(timer_priority, 0, 0); iohandler_close(timer_priority); //also sets timer_priority to the next timed element continue; diff --git a/src/IOHandler.c b/src/IOHandler.c index 52b27b3..07cd794 100644 --- a/src/IOHandler.c +++ b/src/IOHandler.c @@ -73,20 +73,30 @@ extern struct IOEngine engine_kevent; extern struct IOEngine engine_epoll; extern struct IOEngine engine_win32; +int iohandler_settings = 0; struct IOEngine *engine = NULL; +void iohandler_set(int setting, int value) { + if(value) + iohandler_settings |= setting; + else + iohandler_settings &= ~setting; +} + static void iohandler_init_engine() { if(engine) return; IOTHREAD_MUTEX_INIT(io_thread_sync); IOTHREAD_MUTEX_INIT(io_poll_sync); //try other engines - if(!engine && engine_kevent.init && engine_kevent.init()) - engine = &engine_kevent; - if(!engine && engine_epoll.init && engine_epoll.init()) - engine = &engine_epoll; - if(!engine && engine_win32.init && engine_win32.init()) - engine = &engine_win32; + if(!(iohandler_settings & IOHANDLER_SETTING_HIGH_PRECISION_TIMER)) { + if(!engine && engine_kevent.init && engine_kevent.init()) + engine = &engine_kevent; + if(!engine && engine_epoll.init && engine_epoll.init()) + engine = &engine_epoll; + if(!engine && engine_win32.init && engine_win32.init()) + engine = &engine_win32; + } if (!engine) { if(engine_select.init()) @@ -185,8 +195,13 @@ struct IODescriptor *iohandler_add(int sockfd, enum IOType type, struct timeval descriptor->type = type; descriptor->state = (type == IOTYPE_STDIN ? IO_CONNECTED : IO_CLOSED); descriptor->callback = callback; - if(timeout) + if(timeout) { descriptor->timeout = *timeout; + if(descriptor->timeout.tv_usec > 1000000) { + descriptor->timeout.tv_usec -= 1000000; + descriptor->timeout.tv_sec++; + } + } if(type != IOTYPE_TIMER) { descriptor->readbuf.buffer = malloc(IO_READ_BUFLEN + 2); descriptor->readbuf.bufpos = 0; @@ -220,9 +235,13 @@ void iohandler_set_timeout(struct IODescriptor *descriptor, struct timeval *time descriptor->next->prev = descriptor->prev; if(descriptor == timer_priority) timer_priority = descriptor->next; - if(timeout) + if(timeout) { descriptor->timeout = *timeout; - else { + if(descriptor->timeout.tv_usec > 1000000) { + descriptor->timeout.tv_usec -= 1000000; + descriptor->timeout.tv_sec++; + } + } else { descriptor->timeout.tv_sec = 0; descriptor->timeout.tv_usec = 0; } diff --git a/src/IOHandler.h b/src/IOHandler.h index 72e2705..003f9b0 100644 --- a/src/IOHandler.h +++ b/src/IOHandler.h @@ -114,6 +114,10 @@ struct IOEvent { #define IOHANDLER_CONNECT_IPV4 0x01 #define IOHANDLER_CONNECT_IPV6 0x02 /* overrides IOHANDLER_CONNECT_IPV4 */ +#define IOHANDLER_SETTING_HIGH_PRECISION_TIMER 0x01 + +void iohandler_set(int setting, int value); + struct IODescriptor *iohandler_add(int sockfd, enum IOType type, struct timeval *timeout, iohandler_callback *callback); struct IODescriptor *iohandler_timer(struct timeval timeout, iohandler_callback *callback); struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, int ssl, const char *bind, iohandler_callback *callback); diff --git a/src/test/timer/iotest.c b/src/test/timer/iotest.c index 02b8c4b..7ce2d44 100644 --- a/src/test/timer/iotest.c +++ b/src/test/timer/iotest.c @@ -41,6 +41,8 @@ static void add_timer(int ms) { int main(int argc, char *argv[]) { iolog_backend = io_log; + iohandler_set(IOHANDLER_SETTING_HIGH_PRECISION_TIMER, 1); + gettimeofday(&test_clock1, NULL); gettimeofday(&test_clock2, NULL); add_timer(TEST_DURATION); -- 2.20.1