From: pk910 Date: Sat, 18 Jan 2014 02:54:41 +0000 (+0100) Subject: [IOMultiplexerV2] dev snapshot X-Git-Url: http://git.pk910.de/?p=NextIRCd.git;a=commitdiff_plain;h=1c31fc37d833b9d289dceb6ae5e6b32f0ccff824 [IOMultiplexerV2] dev snapshot --- diff --git a/src/IODNSEngine_cares.c b/src/IODNSEngine_cares.c new file mode 100644 index 0000000..0852470 --- /dev/null +++ b/src/IODNSEngine_cares.c @@ -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 . + */ +#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 index 0000000..07e46c6 --- /dev/null +++ b/src/IODNSEngine_default.c @@ -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 . + */ +#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 index 0000000..3024f84 --- /dev/null +++ b/src/IODNSLookup.c @@ -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 . + */ +#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 index 0000000..c76fdf4 --- /dev/null +++ b/src/IODNSLookup.h @@ -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 . + */ +#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 index 0000000..363d816 --- /dev/null +++ b/src/IOGarbageCollector.c @@ -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 . + */ +#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 index 0000000..d1e848b --- /dev/null +++ b/src/IOGarbageCollector.h @@ -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 . + */ +#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 index 0000000..afc92f3 --- /dev/null +++ b/src/IOHandler.c @@ -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 . + */ +#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 index 0000000..3cba91d --- /dev/null +++ b/src/IOHandler.h @@ -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 . + */ +#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 index 0000000..2dd5950 --- /dev/null +++ b/src/IOInternal.h @@ -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 . + */ +#ifndef _IOInternal_h +#define _IOInternal_h +#ifndef _IOHandler_internals +#include "IOHandler.h" +#else + +/* Multithreading */ +#ifdef HAVE_PTHREAD_H +#include +#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 index 0000000..04af476 --- /dev/null +++ b/src/IOLog.c @@ -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 . + */ +#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 index 0000000..0296b3d --- /dev/null +++ b/src/IOLog.h @@ -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 . + */ +#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 index 0000000..a509bf8 --- /dev/null +++ b/src/IOSockets.h @@ -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 . + */ +#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 index 0000000..edbd98b --- /dev/null +++ b/src/IOTimer.c @@ -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 . + */ +#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 index 0000000..33014bb --- /dev/null +++ b/src/IOTimer.h @@ -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 . + */ +#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 index 0000000..2f919fc --- /dev/null +++ b/src/compat/utime.c @@ -0,0 +1,48 @@ +#ifndef HAVE_USLEEP + +#ifdef HAVE_SELECT + +#ifdef HAVE_SYS_SELECT_H +# include +#else +# include +# include +# include +#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 + +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