Compile multiple network backends into the binary and choose which one to use at runtime.

This makes it possible to share more code between the backends and also work around
bugs of certain backends on some (versions of) operating systems.
This commit is contained in:
Jan Vidar Krey
2010-02-10 21:57:27 +01:00
parent 6ac78e1a10
commit 896bbfcb5d
9 changed files with 446 additions and 382 deletions

View File

@@ -21,6 +21,9 @@
#include "network/connection.h"
struct net_backend;
struct net_connection;
struct net_cleanup_handler
{
size_t num;
@@ -28,6 +31,157 @@ struct net_cleanup_handler
struct net_connection** queue;
};
struct net_backend
{
struct net_backend_common common;
time_t now; /* the time now (used for timeout handling) */
struct timeout_queue timeout_queue; /* used for timeout handling */
struct net_cleanup_handler* cleaner; /* handler to cleanup connections at a safe point */
struct net_backend_handler handler; /* backend event handler */
struct net_backend* data; /* backend specific data */
};
static struct net_backend* g_backend;
#ifdef USE_EPOLL
extern struct net_backend* net_backend_init_epoll(struct net_backend_handler*, struct net_backend_common*);
#endif
#ifdef USE_KQUEUE
extern struct net_backend* net_backend_init_kqueue(struct net_backend_handler*, struct net_backend_common*);
#endif
#ifdef USE_SELECT
extern struct net_backend* net_backend_init_select(struct net_backend_handler*, struct net_backend_common*);
#endif
static net_backend_init_t net_backend_init_funcs[] = {
#ifdef USE_EPOLL
net_backend_init_epoll,
#endif
#ifdef USE_KQUEUE
net_backend_init_kqueue,
#endif
#ifdef USE_SELECT
net_backend_init_select,
#endif
0
};
int net_backend_init()
{
size_t n;
g_backend = hub_malloc_zero(sizeof(struct net_backend));
g_backend->common.num = 0;
g_backend->common.max = net_get_max_sockets();
g_backend->now = time(0);
timeout_queue_initialize(&g_backend->timeout_queue, g_backend->now, 600); /* FIXME: max 600 secs! */
g_backend->cleaner = net_cleanup_initialize(g_backend->common.max);
for (n = 0; n < sizeof(net_backend_init_funcs); n++)
{
if (!net_backend_init_funcs[n])
break;
g_backend->data = net_backend_init_funcs[n](&g_backend->handler, &g_backend->common);
if (g_backend->data)
{
LOG_DEBUG("Initialized %s network backend.", g_backend->handler.backend_name());
return 1;
}
}
LOG_FATAL("Unable to find a suitable network backend");
return 0;
}
void net_backend_shutdown()
{
g_backend->handler.backend_shutdown(g_backend->data);
timeout_queue_shutdown(&g_backend->timeout_queue);
net_cleanup_shutdown(g_backend->cleaner);
hub_free(g_backend);
g_backend = 0;
}
void net_con_reinitialize(struct net_connection* con, net_connection_cb callback, const void* ptr, int events)
{
con->callback = callback;
con->ptr = (void*) ptr;
net_con_update(con, events);
}
void net_con_update(struct net_connection* con, int events)
{
g_backend->handler.con_mod(g_backend->data, con, events);
}
struct net_connection* net_con_create()
{
return g_backend->handler.con_create(g_backend->data);
}
struct timeout_queue* net_backend_get_timeout_queue()
{
if (!g_backend)
return 0;
return &g_backend->timeout_queue;
}
/**
* Process the network backend.
*/
int net_backend_process()
{
int res;
size_t secs = timeout_queue_get_next_timeout(&g_backend->timeout_queue, g_backend->now);
res = g_backend->handler.backend_poll(g_backend->data, secs * 1000);
g_backend->now = time(0);
timeout_queue_process(&g_backend->timeout_queue, g_backend->now);
if (res == -1)
{
LOG_WARN("backend error.");
return 0;
}
g_backend->handler.backend_process(g_backend->data, res);
net_cleanup_process(g_backend->cleaner);
return 1;
}
void net_con_initialize(struct net_connection* con, int sd, net_connection_cb callback, const void* ptr, int events)
{
g_backend->handler.con_init(g_backend->data, con, sd, callback, ptr);
net_set_nonblocking(con->sd, 1);
net_set_nosigpipe(con->sd, 1);
g_backend->handler.con_add(g_backend->data, con, events);
g_backend->common.num++;
}
void net_con_close(struct net_connection* con)
{
if (con->flags & NET_CLEANUP)
return;
g_backend->common.num--;
net_con_clear_timeout(con);
g_backend->handler.con_del(g_backend->data, con);
net_close(con->sd);
con->sd = -1;
net_cleanup_delayed_free(g_backend->cleaner, con);
}
struct net_cleanup_handler* net_cleanup_initialize(size_t max)
{
struct net_cleanup_handler* handler = (struct net_cleanup_handler*) hub_malloc(sizeof(struct net_cleanup_handler));