[IOMultiplexer] added possibility for higher resolution timer (select backend - all...
authorpk910 <philipp@zoelle1.de>
Sat, 17 Nov 2012 03:02:39 +0000 (04:02 +0100)
committerpk910 <philipp@zoelle1.de>
Sat, 17 Nov 2012 03:36:07 +0000 (04:36 +0100)
src/IOEngine_select.c
src/IOHandler.c
src/IOHandler.h
src/test/timer/iotest.c

index 2d85d7c..48d7fed 100644 (file)
@@ -16,6 +16,7 @@
  */
 #include "IOEngine.h"
 #include <errno.h>
+#include <time.h>
 #ifdef WIN32
 #define _WIN32_WINNT 0x501
 #include <windows.h>
 #include <stdio.h>
 #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;
index 52b27b3..07cd794 100644 (file)
@@ -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;
     }
index 72e2705..003f9b0 100644 (file)
@@ -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);
index 02b8c4b..7ce2d44 100644 (file)
@@ -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);