[IOMultiplexerV2] dev snapshot
authorpk910 <philipp@zoelle1.de>
Sat, 18 Jan 2014 02:54:41 +0000 (03:54 +0100)
committerpk910 <philipp@zoelle1.de>
Sat, 18 Jan 2014 02:54:41 +0000 (03:54 +0100)
15 files changed:
src/IODNSEngine_cares.c [new file with mode: 0644]
src/IODNSEngine_default.c [new file with mode: 0644]
src/IODNSLookup.c [new file with mode: 0644]
src/IODNSLookup.h [new file with mode: 0644]
src/IOGarbageCollector.c [new file with mode: 0644]
src/IOGarbageCollector.h [new file with mode: 0644]
src/IOHandler.c [new file with mode: 0644]
src/IOHandler.h [new file with mode: 0644]
src/IOInternal.h [new file with mode: 0644]
src/IOLog.c [new file with mode: 0644]
src/IOLog.h [new file with mode: 0644]
src/IOSockets.h [new file with mode: 0644]
src/IOTimer.c [new file with mode: 0644]
src/IOTimer.h [new file with mode: 0644]
src/compat/utime.c [new file with mode: 0644]

diff --git a/src/IODNSEngine_cares.c b/src/IODNSEngine_cares.c
new file mode 100644 (file)
index 0000000..0852470
--- /dev/null
@@ -0,0 +1,50 @@
+/* IODNSEngine_cares.c - IOMultiplexer
+ * Copyright (C) 2014  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IODNSHandler.h"
+
+static int dnsengine_cares_init() {
+       /* TODO */
+    return 0;
+}
+
+static void dnsengine_cares_stop() {
+       /* TODO */
+}
+
+static void dnsengine_cares_add(struct _IODNSQuery *iodns) {
+    /* TODO */
+}
+
+static void dnsengine_cares_remove(struct _IODNSQuery *iodns) {
+    /* TODO */
+}
+
+static void dnsengine_cares_loop() {
+    /* TODO */
+}
+
+struct IODNSEngine dnsengine_cares = {
+    .name = "c-ares",
+    .init = dnsengine_cares_init,
+       .stop = dnsengine_cares_stop,
+    .add = dnsengine_cares_add,
+    .remove = dnsengine_cares_remove,
+    .loop = dnsengine_cares_loop,
+};
diff --git a/src/IODNSEngine_default.c b/src/IODNSEngine_default.c
new file mode 100644 (file)
index 0000000..07e46c6
--- /dev/null
@@ -0,0 +1,182 @@
+/* IODNSEngine_default.c - IOMultiplexer
+ * Copyright (C) 2014  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IODNSHandler.h"
+
+#ifdef HAVE_PTHREAD_H
+static pthread_t *iodns_thread;
+static int iodns_thread_running = 1;
+
+static pthread_cond_t iodns_cond;
+static pthread_mutex_t iodns_sync2;
+#endif
+static int iodns_loop_blocking = 0;
+
+static void iodns_process_queries();
+
+static void dnsengine_worker_main(void *arg) {
+       struct _IODNSQuery *query;
+       while(iodns_thread_running) {
+               IOSYNCHRONIZE(iodns_sync);
+               for(query = iodnsquery_first; query; query = query->next) {
+                       if((query->flags & IODNSFLAG_RUNNING))
+                               break;
+               }
+               IODESYNCHRONIZE(iodns_sync);
+               if(!query)
+                       pthread_cond_wait(&iodns_cond, &iodns_sync2);
+               
+               if(iodns_thread_running)
+                       iodns_process_queries();
+       }
+}
+
+static int dnsengine_default_init() {
+       #ifdef HAVE_PTHREAD_H
+       /* create worker thread */
+       pthread_cond_init(&iodns_cond, NULL);
+       IOTHREAD_MUTEX_INIT(iodns_sync2);
+       
+       iodns_thread_running = 1;
+       
+       int thread_err;
+       thread_err = pthread_create(&iodns_thread, NULL, dnsengine_worker_main, NULL);
+       if(thread_err) {
+               iolog_trigger(IOLOG_ERROR, "could not create pthread in %s:%d (Returned: %i)", __FILE__, __LINE__, thread_err);
+               iodns_loop_blocking = 1;
+               iodns_thread = NULL;
+               iodns_thread_running = 0;
+       }
+       #else
+       iodns_loop_blocking = 1;
+       #endif
+    return 1;
+}
+
+static void dnsengine_default_stop() {
+       #ifdef HAVE_PTHREAD_H
+       if(iodns_thread_running) {
+               iodns_thread_running = 0;
+               IOSYNCHRONIZE(iodns_sync2);
+               pthread_cond_signal(&iodns_cond);
+               IODESYNCHRONIZE(iodns_sync2);
+               pthread_join(iodns_thread, NULL);
+       }
+       #endif
+}
+
+static void dnsengine_default_add(struct _IODNSQuery *iodns) {
+    #ifdef HAVE_PTHREAD_H
+       if(iodns_thread_running) {
+               IOSYNCHRONIZE(iodns_sync2);
+               pthread_cond_signal(&iodns_cond);
+               IODESYNCHRONIZE(iodns_sync2);
+       }
+       #endif
+}
+
+static void dnsengine_default_remove(struct _IODNSQuery *iodns) {
+    /* unused */
+}
+
+static void dnsengine_default_loop() {
+    if(iodns_loop_blocking)
+               iodns_process_queries();
+}
+
+static void iodns_process_queries() {
+       enum IODNSEventType querystate;
+    struct addrinfo hints, *res, *next_res;
+    struct _IODNSQuery *iodns, *next_iodns;
+    struct IODNSResult *dnsresult;
+       iodns_process_queries_start:
+       IOSYNCHRONIZE(iodns_sync);
+    for(iodns = first_dnsquery; iodns; iodns = next_iodns) {
+        next_iodns = iodns->next;
+               
+               if(!(iodns->flags & IODNSFLAG_RUNNING))
+                       continue;
+               if((iodns->flags & IODNSFLAG_PROCESSING))
+                       continue;
+               
+               IODESYNCHRONIZE(iodns_sync);
+               
+        querystate = IODNSEVENT_FAILED;
+        
+        if((iodns->type & IODNS_FORWARD)) {
+            memset (&hints, 0, sizeof (hints));
+            hints.ai_family = PF_UNSPEC;
+            hints.ai_socktype = SOCK_STREAM;
+            hints.ai_flags |= AI_CANONNAME;
+            if (!getaddrinfo(iodns->request.host, NULL, &hints, &res)) {
+                while (res) {
+                    switch (res->ai_family) {
+                    case AF_INET:
+                        if((iodns->type & IODNS_RECORD_A)) {
+                            dnsresult = malloc(sizeof(*dnsresult));
+                            dnsresult->type = IODNS_RECORD_A;
+                            dnsresult->result.addr.addresslen = res->ai_addrlen;
+                            dnsresult->result.addr.address = malloc(dnsresult->addresslen);
+                            memcpy(dnsresult->address, res->ai_addr, dnsresult->addresslen);
+                            dnsresult->next = iodns->result;
+                            iodns->result = dnsresult;
+                            querystate = IODNSEVENT_SUCCESS;
+                        }
+                        break;
+                    case AF_INET6:
+                        if((iodns->type & IODNS_RECORD_AAAA)) {
+                            dnsresult = malloc(sizeof(*dnsresult));
+                            dnsresult->type = IODNS_RECORD_AAAA;
+                            dnsresult->result.addr.addresslen = res->ai_addrlen;
+                            dnsresult->result.addr.address = malloc(dnsresult->addresslen);
+                            memcpy(dnsresult->address, res->ai_addr, dnsresult->addresslen);
+                            dnsresult->next = iodns->result;
+                            iodns->result = dnsresult;
+                            querystate = IODNSEVENT_SUCCESS;
+                        }
+                        break;
+                    }
+                    next_res = res->ai_next;
+                    freeaddrinfo(res);
+                    res = next_res;
+                }
+            }
+        }
+               IOSYNCHRONIZE(iodns_sync);
+               if(!(iodns->flags & IODNSFLAG_RUNNING)) {
+                       iodns_free_result(iodns->result);
+                       _free_dnsquery(iodns);
+                       IODESYNCHRONIZE(iodns_sync);
+                       goto iodns_process_queries_start;
+               }
+               iodns->flags &= ~(IODNSFLAG_PROCESSING | IODNSFLAG_RUNNING);
+               IODESYNCHRONIZE(iodns_sync);
+               iodns_event_callback(iodns, querystate);
+               goto iodns_process_queries_start;
+    }
+}
+
+struct IODNSEngine dnsengine_default = {
+    .name = "default",
+    .init = dnsengine_default_init,
+       .stop = dnsengine_default_stop,
+    .add = dnsengine_default_add,
+    .remove = dnsengine_default_remove,
+    .loop = dnsengine_default_loop,
+};
diff --git a/src/IODNSLookup.c b/src/IODNSLookup.c
new file mode 100644 (file)
index 0000000..3024f84
--- /dev/null
@@ -0,0 +1,221 @@
+/* IODNSLookup.c - IOMultiplexer
+ * Copyright (C) 2014  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IODNSLookup.h"
+#include "IOLog.h"
+
+#ifdef HAVE_PTHREAD_H
+pthread_mutex_t iodns_sync;
+#endif
+
+struct _IODNSQuery *iodnsquery_first = NULL;
+struct _IODNSQuery *iodnsquery_last = NULL;
+
+struct IODNSEngine *dnsengine = NULL;
+
+static void iodns_init_engine() {
+       if(dnsengine)
+               return;
+       //try DNS engines
+       if(dnsengine_cares.init && dnsengine_cares.init())
+               dnsengine = &dnsengine_cares;
+       else if(dnsengine_default.init && dnsengine_default.init())
+               dnsengine = &dnsengine_default;
+       else {
+               iohandler_log(IOLOG_FATAL, "found no useable IO DNS engine");
+               return;
+       }
+}
+
+void _init_iodns() {
+       IOTHREAD_MUTEX_INIT(iodns_sync);
+       iodns_init_engine();
+}
+
+struct _IODNSQuery *_create_dnsquery() {
+       struct _IODNSQuery *query = calloc(1, sizeof(*query));
+       if(!query) {
+               iolog_trigger(IOLOG_ERROR, "could not allocate memory for _IODNSQuery in %s:%d", __FILE__, __LINE__);
+               return NULL;
+       }
+       IOSYNCHRONIZE(iodns_sync);
+       if(iodnsquery_last)
+               iodnsquery_last->next = query;
+       else
+               iodnsquery_first = query;
+       query->prev = iodnsquery_last;
+       iodnsquery_last = query;
+       IODESYNCHRONIZE(iodns_sync);
+       return query;
+}
+
+void _start_dnsquery(struct _IODNSQuery *query) {
+       IOSYNCHRONIZE(iodns_sync);
+       query->flags |= IODNSFLAG_RUNNING;
+       dnsengine->add(query);
+       IODESYNCHRONIZE(iodns_sync);
+}
+
+void _free_dnsquery(struct _IODNSQuery *query) {
+       IOSYNCHRONIZE(iodns_sync);
+       if(query->prev)
+               query->prev->next = query->next;
+       else
+               iodnsquery_first = query->next;
+       if(query->next)
+               query->next->prev = query->prev;
+       else
+               iodnsquery_last = query->prev;
+       IODESYNCHRONIZE(iodns_sync);
+       if((query->type & IODNS_REVERSE) && query->request.addr.address)
+               free(query->request.addr.address);
+       free(query);
+}
+
+void _stop_dnsquery(struct _IODNSQuery *query) {
+       IOSYNCHRONIZE(iodns_sync);
+       if((query->flags & IODNSFLAG_RUNNING)) {
+               query->flags &= ~IODNSFLAG_RUNNING;
+               dnsengine->remove(query);
+       }
+       if(!(query->flags & IODNSFLAG_PROCESSING))
+               _free_dnsquery(query);
+       IODESYNCHRONIZE(iodns_sync);
+}
+
+void iodns_event_callback(struct _IODNSQuery *query, enum IODNSEventType state) {
+       if((query->flags & IODNSFLAG_PARENT_PUBLIC)) {
+               struct IODNSQuery *descriptor = query->parent;
+               struct IODNSEvent event;
+               event.type = state;
+               event.query = descriptor;
+               
+               descriptor->parent = NULL;
+               _stop_dnsquery(query);
+               
+               if(descriptor->callback)
+                       descriptor->callback(&event);
+               
+               iogc_add(descriptor);
+       }
+}
+
+void iodns_poll() {
+       if(dnsengine)
+               dnsengine.loop();
+}
+
+/* public functions */
+
+struct IODNSQuery *iodns_getaddrinfo(char *hostname, int records, iodns_callback *callback) {
+       if(!(records & IODNS_FORWARD) || !hostname || !callback)
+               return NULL;
+       
+       struct IODNSQuery *descriptor = calloc(1, sizeof(*descriptor));
+       if(!descriptor) {
+               iolog_trigger(IOLOG_ERROR, "could not allocate memory for IODNSQuery in %s:%d", __FILE__, __LINE__);
+               return NULL;
+       }
+       
+       struct _IODNSQuery *query = _create_dnsquery();
+       if(!query) {
+               free(descriptor);
+               return NULL
+       }
+       
+       query->parent = descriptor;
+       query->flags |= IODNSFLAG_PARENT_PUBLIC;
+       descriptor->query = query;
+       
+       query->request.host = strdup(hostname);
+       query->type = (records & IODNS_FORWARD);
+       
+       descriptor->callback = callback;
+       
+       _start_dnsquery(query);
+       return descriptor;
+}
+
+struct IODNSQuery *iodns_getnameinfo(const struct sockaddr *addr, socklen_t addrlen, iodns_callback *callback) {
+       if(!addr || !callback)
+               return NULL;
+       
+       struct IODNSQuery *descriptor = calloc(1, sizeof(*descriptor));
+       if(!descriptor) {
+               iolog_trigger(IOLOG_ERROR, "could not allocate memory for IODNSQuery in %s:%d", __FILE__, __LINE__);
+               return NULL;
+       }
+       
+       struct _IODNSQuery *query = _create_dnsquery();
+       if(!query) {
+               free(descriptor);
+               return NULL
+       }
+       
+       query->parent = descriptor;
+       query->flags |= IODNSFLAG_PARENT_PUBLIC;
+       descriptor->query = query;
+       
+       query->type = IODNS_RECORD_PTR;
+       query->request.addr.addresslen = addrlen;
+       query->request.addr.address = malloc(addrlen);
+       if(!query->request.addr.address) {
+               iolog_trigger(IOLOG_ERROR, "could not allocate memory for sockaddr in %s:%d", __FILE__, __LINE__);
+               _free_dnsquery(query);
+               free(descriptor);
+               return NULL;
+       }
+       memcpy(query->request.addr.address, addr, addrlen);
+       
+       descriptor->callback = callback;
+       
+       _start_dnsquery(query);
+       return descriptor;
+}
+
+void iodns_abort(struct IODNSQuery *descriptor) {
+       if(!descriptor)
+               return;
+       
+       struct _IODNSQuery *query = descriptor->query;
+       if(!query) {
+               iolog_trigger(IOLOG_WARNING, "called iodns_abort for destroyed IODNSQuery in %s:%d", __FILE__, __LINE__);
+               return;
+       }
+       
+       _stop_dnsquery(query)
+}
+
+void iodns_free_result(struct IODNSResult *result) {
+       struct IODNSResult *next;
+       for(;result;result = next) {
+               next = result->next;
+               
+               if((result->type & IODNS_FORWARD)) {
+                       if(result->result.addr.address)
+                               free(result->result.addr.address);
+               }
+               if((result->type & IODNS_REVERSE)) {
+                       if(result->result.host)
+                               free(result->result.host);
+               }
+               free(result);
+       }
+}
+
diff --git a/src/IODNSLookup.h b/src/IODNSLookup.h
new file mode 100644 (file)
index 0000000..c76fdf4
--- /dev/null
@@ -0,0 +1,124 @@
+/* IODNSLookup.h - IOMultiplexer v2
+ * Copyright (C) 2014  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+#ifndef _IODNSLookup_h
+#define _IODNSLookup_h
+#ifndef _IOHandler_internals
+#include "IOHandler.h"
+#else
+
+extern struct IODNSEngine dnsengine_cares;
+extern struct IODNSEngine dnsengine_default;
+
+#ifdef HAVE_PTHREAD_H
+extern pthread_mutex_t iodns_sync;
+#endif
+
+struct IODNSAddress;
+
+#define IODNSFLAG_RUNNING        0x01
+#define IODNSFLAG_PROCESSING     0x02
+#define IODNSFLAG_PARENT_PUBLIC  0x04
+
+struct _IODNSQuery {
+       void *query;
+       
+       unsigned int flags : 8;
+       unsigned int type : 8;
+    union {
+               struct IODNSAddress addr;
+               char *host;
+       } request;
+       
+       struct IODNSResult *result;
+       
+       void *parent;
+       
+       struct _IODNSQuery *next, *prev;
+};
+
+struct IODNSEngine {
+    const char *name;
+    int (*init)();
+       void (*stop)();
+    void (*add)(struct _IODNSQuery *query);
+    void (*remove)(struct _IODNSQuery *query);
+    void (*loop)();
+};
+
+void _init_iodns();
+void _stop_iodns();
+struct _IODNSQuery *_create_dnsquery();
+void _start_dnsquery(struct _IODNSQuery *query);
+void _stop_dnsquery(struct _IODNSQuery *query);
+
+/* call only from engines! */
+void _free_dnsquery(struct _IODNSQuery *query)
+void iodns_event_callback(struct _IODNSQuery *query, enum IODNSEventType state);
+
+void iodns_poll();
+
+#endif
+
+#define IODNS_CALLBACK(NAME) void NAME(struct IODNSEvent *event)
+typedef IODNS_CALLBACK(iodns_callback);
+
+enum IODNSEventType {
+    IODNSEVENT_SUCCESS,
+    IODNSEVENT_FAILED
+};
+
+#define IODNS_RECORD_A    0x01
+#define IODNS_RECORD_AAAA 0x02
+#define IODNS_RECORD_PTR  0x04
+
+#define IODNS_FORWARD     0x03
+#define IODNS_REVERSE     0x04
+
+struct IODNSAddress {
+       size_t addresslen;
+       struct sockaddr *address;
+};
+
+struct IODNSQuery {
+       void *query;
+       
+    iodns_callback *callback;
+    void *data;
+};
+
+struct IODNSResult {
+    unsigned int type : 8;
+    union {
+               struct IODNSAddress addr;
+               char *host;
+       } result;
+    struct IODNSResult *next;
+};
+
+struct IODNSEvent {
+    enum IODNSEventType type;
+    struct IODNSQuery *query;
+       struct IODNSResult *result;
+};
+
+struct IODNSQuery *iodns_getaddrinfo(char *hostname, int records, iodns_callback *callback);
+struct IODNSQuery *iodns_getnameinfo(const struct sockaddr *addr, socklen_t addrlen, iodns_callback *callback);
+void iodns_abort(struct IODNSQuery *query);
+
+void iodns_free_result(struct IODNSResult *result);
+
+#endif
diff --git a/src/IOGarbageCollector.c b/src/IOGarbageCollector.c
new file mode 100644 (file)
index 0000000..363d816
--- /dev/null
@@ -0,0 +1,100 @@
+/* IOGarbageCollector.c - IOMultiplexer v2
+ * Copyright (C) 2014  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IOGarbageCollector.h"
+
+#define timeval_is_bigger(x,y) ((x.tv_sec > y.tv_sec) || (x.tv_sec == y.tv_sec && x.tv_usec > y.tv_usec))
+#define timeval_is_smaler(x,y) ((x.tv_sec < y.tv_sec) || (x.tv_sec == y.tv_sec && x.tv_usec < y.tv_usec))
+
+static struct IOGCObject {
+       void *object;
+       iogc_free *free_callback;
+       struct timeval timeout;
+       
+       struct IOGCObject *next;
+};
+
+#ifdef HAVE_PTHREAD_H
+static pthread_mutex_t iogc_sync;
+#endif
+
+static int iogc_enabled = 1;
+static struct timeval iogc_timeout;
+static struct IOGCObject *first_object = NULL, *last_object = NULL;
+
+void iogc_init() {
+       IOTHREAD_MUTEX_INIT(iogc_sync);
+       iogc_timeout.tv_usec = 0;
+       iogc_timeout.tv_sec = 10;
+}
+
+
+void iohandler_set_gc(int enabled) {
+       if(enabled)
+               iogc_enabled = 1;
+       else
+               iogc_enabled = 0;
+}
+
+void iogc_add(void *object) {
+       iogc_add_callback(object, NULL);
+}
+
+void iogc_add_callback(void *object, iogc_free *free_callback) {
+       if(!iogc_enabled) {
+               if(free_callback)
+                       free_callback(object);
+               else
+                       free(object);
+               return;
+       }
+       struct IOGCObject *obj = malloc(sizeof(*obj));
+    if(!obj) {
+        iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOGCObject in %s:%d", __FILE__, __LINE__);
+               if(free_callback)
+                       free_callback(object);
+               else
+                       free(object);
+        return;
+    }
+       obj->object = object;
+       obj->free_callback = free_callback;
+       gettimeofday(&obj->timeout, NULL);
+       
+}
+
+void iogc_exec() {
+       struct timeval ctime;
+       gettimeofday(&ctime, NULL);
+       
+       struct IOGCObject *obj, *next_obj;
+       for(obj = objects; obj; obj = next_obj) {
+               if(timeval_is_smaler(obj->timeout, ctime)) {
+                       next_obj = obj->next;
+                       if(obj->free_callback)
+                               obj->free_callback(obj->object);
+                       else
+                               free(obj->object);
+                       free(obj);
+               } else {
+                       objects = obj;
+                       break;
+               }
+       }
+}
diff --git a/src/IOGarbageCollector.h b/src/IOGarbageCollector.h
new file mode 100644 (file)
index 0000000..d1e848b
--- /dev/null
@@ -0,0 +1,27 @@
+/* IOGarbageCollector.h - IOMultiplexer v2
+ * Copyright (C) 2014  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+#ifndef _IOGarbageCollector_h
+#define _IOGarbageCollector_h
+#ifndef _IOHandler_internals
+#include "IOHandler.h"
+#else
+
+void iogc_init();
+void iogc_exec();
+
+#endif
+#endif
diff --git a/src/IOHandler.c b/src/IOHandler.c
new file mode 100644 (file)
index 0000000..afc92f3
--- /dev/null
@@ -0,0 +1,195 @@
+/* IOHandler.c - IOMultiplexer v2
+ * Copyright (C) 2014  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+
+#include "IOLog.h"
+#include "IOGarbageCollector.h"
+#include "IOTimer.h"
+#include "IODNSLookup.h"
+
+/* compat */
+#include "compat/usleep.c"
+
+#ifdef HAVE_PTHREAD_H
+static pthread_mutex_t iothread_sync;
+#ifdef WIN32
+#define pthread_self_tid() pthread_self().p
+#else
+#define pthread_self_tid() pthread_self()
+#endif
+
+#endif
+
+static struct IOHandlerThread {
+       unsigned int id;
+       unsigned int main : 1;
+       unsigned int run : 1;
+       unsigned int shutdown : 1;
+       #ifdef HAVE_PTHREAD_H
+       static pthread_t *thread;
+       #endif
+       struct IOHandlerThread *next;
+}
+
+static int iohandler_running = 0;
+static int iohandler_treads = 1;
+static struct IOHandlerThread *threads;
+
+void iohandler_init() {
+       IOTHREAD_MUTEX_INIT(iothread_sync);
+       
+       iolog_init();
+       iogc_init();
+       
+       _init_timers();
+       _init_iodns();
+       
+       iohandler_running = 1;
+}
+
+void iohandler_set_threads(int threadcount) {
+       iohandler_treads = threadcount;
+}
+
+void iohandler_stop() {
+       iohandler_treads = 0;
+}
+
+#ifdef HAVE_PTHREAD_H
+static void iohandler_start_worker() {
+       struct IOHandlerThread *thread = calloc(1, sizeof(*thread));
+    if(!thread) {
+        iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOHandlerThread in %s:%d", __FILE__, __LINE__);
+        return;
+    }
+       struct IOHandlerThread *cthread;
+       for(cthread = threads; cthread; cthread = cthread->next) {
+               if(cthread->next == NULL) {
+                       cthread->next = thread;
+                       break;
+               }
+       }
+       
+       thread->run = 1;
+       
+       int thread_err;
+       thread_err = pthread_create(&thread->thread, NULL, iohandler_worker, thread);
+       if(thread_err) {
+               cthread->next = NULL;
+               iolog_trigger(IOLOG_ERROR, "could not create pthread in %s:%d (Returned: %i)", __FILE__, __LINE__, thread_err);
+       }
+}
+#endif
+
+static void iohandler_worker(void *tptr) {
+       struct IOHandlerThread *thread = tptr;
+       
+       #ifdef HAVE_PTHREAD_H
+       if(!thread->main) {
+               thread->id = pthread_self_tid();
+       }
+       #endif
+       
+       while(!thread->shutdown) { // endless loop
+               if(thread->main && iohandler_treads != iohandler_running) {
+                       IOSYNCHRONIZE(iothread_sync);
+                       #ifdef HAVE_PTHREAD_H
+                       int i;
+                       if(iohandler_treads > iohandler_running) {
+                               for(i = 0; i < (iohandler_treads - iohandler_running); i++)
+                                       iohandler_start_worker();
+                       }
+                       if(iohandler_treads < iohandler_running) {
+                               struct IOHandlerThread *cthread;
+                               for(i = 0; i < (iohandler_running - iohandler_treads); i++) {
+                                       for(cthread = threads; cthread; cthread = cthread->next) {
+                                               if(cthread->main)
+                                                       continue;
+                                               cthread->shutdown = 1;
+                                               iolog_trigger(IOLOG_ERROR, "Thread %d marked for shutdown.", cthread->id);
+                                       }
+                                       if(cthread)
+                                               iohandler_running--;
+                               }
+                       }
+                       #endif
+                       if(iohandler_treads == 0) {
+                               #ifdef HAVE_PTHREAD_H
+                               struct IOHandlerThread *cthread;
+                               for(cthread = threads; cthread; cthread = cthread->next) {
+                                       if(cthread->main)
+                                               continue;
+                                       cthread->shutdown = 1;
+                                       pthread_join(cthread->thread, NULL);
+                               }
+                               #endif
+                               thread->shutdown = 1;
+                               IODESYNCHRONIZE(iothread_sync);
+                               break;
+                       }
+                       IODESYNCHRONIZE(iothread_sync);
+               }
+               if(!thread->run) {
+                       usleep(500000); // 500ms
+                       continue;
+               }
+               
+               // iohandler calls
+               iogc_exec();
+               iodns_poll();
+               
+       }
+       IOSYNCHRONIZE(iothread_sync);
+       if(thread == threads) {
+               threads = thread->next;
+       } else {
+               struct IOHandlerThread *cthread;
+               for(cthread = threads; cthread; cthread = cthread->next) {
+                       if(cthread->next == thread) {
+                               cthread->next = thread->next;
+                               break;
+                       }
+               }
+       }
+       iolog_trigger(IOLOG_DEBUG, "Thread %d stopped.", thread->id);
+       free(thread);
+       IODESYNCHRONIZE(iothread_sync);
+}
+
+void iohandler_run() {
+       if(!iohandler_running)
+               return;
+       iohandler_running = 1;
+       
+       struct IOHandlerThread *mainthread = calloc(1, sizeof(*mainthread));
+    if(!mainthread) {
+        iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOHandlerThread in %s:%d", __FILE__, __LINE__);
+        return;
+    }
+       threads = mainthread;
+       
+       mainthread->main = 1;
+       mainthread->run = 1;
+       mainthread->shutdown = 0;
+       
+       iohandler_worker(1);
+       
+       _stop_iodns(); /* possible worker thread */
+}
+
diff --git a/src/IOHandler.h b/src/IOHandler.h
new file mode 100644 (file)
index 0000000..3cba91d
--- /dev/null
@@ -0,0 +1,30 @@
+/* IOHandler.h - IOMultiplexer v2
+ * Copyright (C) 2014  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+#ifndef _IOHandler_h
+#define _IOHandler_h
+#ifdef _IOHandler_internals
+
+#endif
+
+void iohandler_init();
+void iohandler_run();
+void iohandler_stop();
+
+void iohandler_set_threads(int threads); /* default: 1 */
+void iohandler_set_gc(int enabled); /* default: enabled */
+
+#endif
diff --git a/src/IOInternal.h b/src/IOInternal.h
new file mode 100644 (file)
index 0000000..2dd5950
--- /dev/null
@@ -0,0 +1,52 @@
+/* IOInternal.h - IOMultiplexer v2
+ * Copyright (C) 2014  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+#ifndef _IOInternal_h
+#define _IOInternal_h
+#ifndef _IOHandler_internals
+#include "IOHandler.h"
+#else
+
+/* Multithreading */
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#ifdef PTHREAD_MUTEX_RECURSIVE_NP
+#define PTHREAD_MUTEX_RECURSIVE_VAL PTHREAD_MUTEX_RECURSIVE_NP
+#else
+#define PTHREAD_MUTEX_RECURSIVE_VAL PTHREAD_MUTEX_RECURSIVE
+#endif
+#define IOTHREAD_MUTEX_INIT(var) { \
+    pthread_mutexattr_t mutex_attr; \
+    pthread_mutexattr_init(&mutex_attr);\
+    pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE_VAL);\
+    pthread_mutex_init(&var, &mutex_attr); \
+}
+#define IOSYNCHRONIZE(var) pthread_mutex_lock(&var)
+#define IODESYNCHRONIZE(var) pthread_mutex_unlock(&var)
+#else
+#define IOTHREAD_MUTEX_INIT(var)
+#define IOSYNCHRONIZE(var)
+#define IODESYNCHRONIZE(var)
+#endif
+
+
+#define IOGC_FREE(NAME) void NAME(void *object)
+typedef IOGC_FREE(iogc_free);
+void iogc_add(void *object);
+void iogc_add_callback(void *object, iogc_free *free_callback);
+
+#endif
+#endif
diff --git a/src/IOLog.c b/src/IOLog.c
new file mode 100644 (file)
index 0000000..04af476
--- /dev/null
@@ -0,0 +1,39 @@
+/* IOLog.c - IOMultiplexer v2
+ * Copyright (C) 2014  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IOLog.h"
+
+void iolog_init() {
+
+}
+
+void iolog_trigger(enum IOLogType type, char *text, ...) {
+    va_list arg_list;
+    char logBuf[MAXLOG+1];
+    int pos;
+    logBuf[0] = '\0';
+    va_start(arg_list, text);
+    pos = vsnprintf(logBuf, MAXLOG - 1, text, arg_list);
+    va_end(arg_list);
+    if (pos < 0 || pos > (MAXLOG - 1)) pos = MAXLOG - 1;
+    logBuf[pos] = '\n';
+    logBuf[pos+1] = '\0';
+    
+    
+}
diff --git a/src/IOLog.h b/src/IOLog.h
new file mode 100644 (file)
index 0000000..0296b3d
--- /dev/null
@@ -0,0 +1,38 @@
+/* IOLog.h - IOMultiplexer v2
+ * Copyright (C) 2014  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+#ifndef _IOInternal_h
+#define _IOInternal_h
+#ifndef _IOHandler_internals
+#include "IOHandler.h"
+#else
+enum IOLogType;
+
+void iolog_init();
+void iolog_trigger(enum IOLogType type, char *text, ...);
+
+#endif
+
+enum IOLogType {
+    IOLOG_DEBUG,
+    IOLOG_WARNING,
+    IOLOG_ERROR,
+    IOLOG_FATAL
+};
+
+/* TODO: Functions to get messages from IOLog */
+
+#endif
diff --git a/src/IOSockets.h b/src/IOSockets.h
new file mode 100644 (file)
index 0000000..a509bf8
--- /dev/null
@@ -0,0 +1,29 @@
+/* IOSockets.h - IOMultiplexer v2
+ * Copyright (C) 2014  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+#ifndef _IOSockets_h
+#define _IOSockets_h
+#ifndef _IOHandler_internals
+#include "IOHandler.h"
+#else
+
+
+
+#endif
+
+
+
+#endif
diff --git a/src/IOTimer.c b/src/IOTimer.c
new file mode 100644 (file)
index 0000000..edbd98b
--- /dev/null
@@ -0,0 +1,178 @@
+/* IOTimer.c - IOMultiplexer v2
+ * Copyright (C) 2014  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+#define _IOHandler_internals
+#include "IOInternal.h"
+#include "IOHandler.h"
+#include "IOHandlerTimer.h"
+#include "IOLog.h"
+
+#ifdef HAVE_PTHREAD_H
+static pthread_mutex_t iotimer_sync;
+#endif
+
+#define timeval_is_bigger(x,y) ((x->tv_sec > y->tv_sec) || (x->tv_sec == y->tv_sec && x->tv_usec > y->tv_usec))
+#define timeval_is_smaler(x,y) ((x->tv_sec < y->tv_sec) || (x->tv_sec == y->tv_sec && x->tv_usec < y->tv_usec))
+
+static void _rearrange_timer(struct IOTimerDescriptor *timer);
+static void _autoreload_timer(struct IOTimerDescriptor *timer);
+
+struct _IOTimerDescriptor *iotimer_sorted_descriptors;
+
+/* public functions */
+
+struct IOTimerDescriptor iotimer_create(struct timeval *timeout) {
+       struct IOTimerDescriptor *descriptor = calloc(1, sizeof(*descriptor));
+    if(!descriptor) {
+        iolog_trigger(IOLOG_ERROR, "could not allocate memory for IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
+        return NULL;
+    }
+       struct _IOTimerDescriptor *timer = _create_timer(timeout);
+       if(!timer) {
+               free(descriptor);
+               return NULL;
+       }
+       timer->parent = descriptor;
+       timer->flags |= IOTIMERFLAG_PARENT_PUBLIC;
+       descriptor->iotimer = timer;
+       
+       return descriptor;
+}
+
+void iotimer_start(struct IOTimerDescriptor *descriptor) {
+       struct _IOTimerDescriptor *timer = descriptor->iotimer;
+       if(timer == NULL) {
+               iolog_trigger(IOLOG_WARNING, "called iotimer_set_autoreload for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
+               return;
+       }
+       timer->flags |= IOTIMERFLAG_ACTIVE;
+       if(!(timer->flags & IOTIMERFLAG_IN_LIST))
+               _trigger_timer(timer);
+}
+
+void iotimer_set_autoreload(struct IOTimerDescriptor *descriptor, struct timeval *autoreload) {
+       struct _IOTimerDescriptor *timer = descriptor->iotimer;
+       if(timer == NULL) {
+               iolog_trigger(IOLOG_WARNING, "called iotimer_set_autoreload for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
+               return;
+       }
+       if(autoreload) {
+               timer->flags |= IOTIMERFLAG_PERIODIC;
+               timer->autoreload = *autoreload;
+               
+               if(!(timer->flags & IOTIMERFLAG_IN_LIST)) {
+                       struct timeval ctime;
+                       gettimeofday(&ctime, NULL);
+                       timer->timeout = ctime;
+                       _autoreload_timer(timer);
+               }
+       } else {
+               timer->flags &= ~IOTIMERFLAG_PERIODIC;
+       }
+}
+
+void iotimer_set_callback(struct IOTimerDescriptor *descriptor, iotimer_callback *callback) {
+       descriptor->callback = callback;
+}
+
+void iotimer_destroy(struct IOTimerDescriptor *descriptor) {
+       struct _IOTimerDescriptor *timer = descriptor->iotimer;
+       if(timer == NULL) {
+               iolog_trigger(IOLOG_WARNING, "called iotimer_destroy for destroyed IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
+               return;
+       }
+       descriptor->iotimer = NULL;
+       _destroy_timer(timer);
+       
+       iogc_add(descriptor);
+}
+
+/* internal functions */
+void _init_timers() {
+       IOTHREAD_MUTEX_INIT(iotimer_sync);
+}
+
+struct _IOTimerDescriptor _create_timer(struct timeval *timeout) {
+       struct _IOTimerDescriptor *timer = calloc(1, sizeof(*timer));
+    if(!timer) {
+        iolog_trigger(IOLOG_ERROR, "could not allocate memory for _IOTimerDescriptor in %s:%d", __FILE__, __LINE__);
+        return NULL;
+    }
+       if(timeout) {
+               timer->timeout = *timeout;
+               _rearrange_timer(timer);
+       }
+       return timer;
+}
+
+static void _rearrange_timer(struct IOTimerDescriptor *timer) {
+       IOSYNCHRONIZE(iotimer_sync);
+       if((timer->flags & IOTIMERFLAG_IN_LIST)) {
+               if(timer->prev == NULL)
+                       iotimer_sorted_descriptors = timer->next;
+               else
+                       timer->prev->next = timer->next;
+               if(timer->next != NULL)
+                       timer->next->prev = timer->prev;
+       }
+       struct _IOTimerDescriptor *ctimer;
+       for(ctimer = iotimer_sorted_descriptors; ctimer;) {
+               if(timeval_is_bigger(&ctimer->timeout, &timer->timeout)) {
+                       timer->next = ctimer;
+                       timer->prev = ctimer->prev;
+                       if(ctimer->prev)
+                               ctimer->prev->next = timer;
+                       else
+                               iotimer_sorted_descriptors = timer;
+                       ctimer->prev = timer;
+                       break
+               }
+               else if(ctimer->next == NULL) {
+                       ctimer->next = timer;
+                       timer->prev = ctimer;
+                       break;
+               }
+       }
+       if(ctimer == NULL)
+               iotimer_sorted_descriptors = timer;
+       timer->flags |= IOTIMERFLAG_IN_LIST;
+       IODESYNCHRONIZE(iotimer_sync);
+}
+
+void _destroy_timer(struct _IOTimerDescriptor *timer) {
+       if((timer->flags & IOTIMERFLAG_IN_LIST)) {
+               IOSYNCHRONIZE(iotimer_sync);
+               if(timer->prev == NULL)
+                       iotimer_sorted_descriptors = timer->next;
+               else
+                       timer->prev->next = timer->next;
+               if(timer->next != NULL)
+                       timer->next->prev = timer->prev;
+               IODESYNCHRONIZE(iotimer_sync);
+       }
+       free(timer);
+}
+
+static void _autoreload_timer(struct IOTimerDescriptor *timer) {
+       timer->timeout.tv_usec += timer->autoreload.tv_usec;
+       timer->timeout.tv_sec += timer->autoreload.tv_sec;
+       if(timer->timeout.tv_usec > 1000000) {
+               timer->timeout.tv_sec += (timer->timeout.tv_usec / 1000000);
+               timer->timeout.tv_usec %= 1000000;
+       }
+       _rearrange_timer(timer);
+}
+
diff --git a/src/IOTimer.h b/src/IOTimer.h
new file mode 100644 (file)
index 0000000..33014bb
--- /dev/null
@@ -0,0 +1,64 @@
+/* IOTimer.h - IOMultiplexer v2
+ * Copyright (C) 2014  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+#ifndef _IOTimer_h
+#define _IOTimer_h
+#ifndef _IOHandler_internals
+#include "IOHandler.h"
+#else
+
+#define IOTIMERFLAG_PERIODIC       0x01
+#define IOTIMERFLAG_ACTIVE         0x02
+#define IOTIMERFLAG_IN_LIST        0x04
+#define IOTIMERFLAG_PARENT_PUBLIC  0x08
+
+struct _IOTimerDescriptor;
+
+extern struct _IOTimerDescriptor *iotimer_sorted_descriptors;
+
+struct _IOTimerDescriptor {
+    unsigned int flags : 8;
+    void *parent; 
+    
+    struct timeval timeout;
+    struct timeval autoreload;
+    
+    struct _IOTimerDescriptor *prev, *next;
+};
+
+void _init_timers();
+struct _IOTimerDescriptor _create_timer(struct timeval timeout);
+void _destroy_timer(struct _IOTimerDescriptor *timer);
+
+#endif
+
+#define IOTIMER_CALLBACK(NAME) void NAME(struct IOTimerDescriptor *iotimer)
+typedef IOTIMER_CALLBACK(iotimer_callback);
+
+struct IOTimerDescriptor {
+    void *iotimer; /* struct _IOTimerDescriptor */
+    
+    iotimer_callback *callback;
+    void *data;
+};
+
+struct IOTimerDescriptor iotimer_create(struct timeval *timeout);
+void iotimer_start(struct IOTimerDescriptor *iotimer);
+void iotimer_set_autoreload(struct IOTimerDescriptor *iotimer, struct timeval *autoreload);
+void iotimer_set_callback(struct IOTimerDescriptor *iotimer, iotimer_callback *callback);
+void iotimer_destroy(struct IOTimerDescriptor *iotimer);
+
+#endif
diff --git a/src/compat/utime.c b/src/compat/utime.c
new file mode 100644 (file)
index 0000000..2f919fc
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef HAVE_USLEEP
+
+#ifdef HAVE_SELECT
+
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#else
+# include <sys/time.h>
+# include <sys/types.h>
+# include <unistd.h>
+#endif
+
+void usleep(long usec)
+{
+        struct timeval tv;
+
+        tv.tv_sec = usec / 1000000;
+        tv.tv_usec = usec % 1000000;
+        select(0, NULL, NULL, NULL, &tv);
+}
+
+#elif defined WIN32
+
+/* usleep implementation from FreeSCI */
+
+#include <windows.h>
+
+void usleep (long usec)
+{
+        LARGE_INTEGER lFrequency;
+        LARGE_INTEGER lEndTime;
+        LARGE_INTEGER lCurTime;
+
+        QueryPerformanceFrequency (&lFrequency);
+        if (lFrequency.QuadPart) {
+                QueryPerformanceCounter (&lEndTime);
+                lEndTime.QuadPart += (LONGLONG) usec *
+                                        lFrequency.QuadPart / 1000000;
+                do {
+                        QueryPerformanceCounter (&lCurTime);
+                        Sleep(0);
+                } while (lCurTime.QuadPart < lEndTime.QuadPart);
+        }
+}
+
+#endif
+
+#endif /* !HAVE_USLEEP */
\ No newline at end of file