Import optimized event loop from ponymap

This commit is contained in:
2015-02-12 01:53:03 +01:00
parent cd1a55a0d1
commit e8fe0dad81
3 changed files with 392 additions and 246 deletions

101
zyklonb.c
View File

@@ -73,6 +73,9 @@ struct plugin_data
int read_fd; ///< The read end of the comm. pipe
int write_fd; ///< The write end of the comm. pipe
struct poller_fd read_event; ///< Read FD event
struct poller_fd write_event; ///< Write FD event
struct str read_buffer; ///< Unprocessed input
struct str write_buffer; ///< Output yet to be sent out
};
@@ -118,8 +121,14 @@ struct bot_context
int irc_fd; ///< Socket FD of the server
struct str read_buffer; ///< Input yet to be processed
struct poller_fd irc_event; ///< IRC FD event
bool irc_ready; ///< Whether we may send messages now
struct poller_fd signal_event; ///< Signal FD event
struct poller_timer ping_tmr; ///< We should send a ping
struct poller_timer timeout_tmr; ///< Connection seems to be dead
struct poller_timer reconnect_tmr; ///< We should reconnect now
SSL_CTX *ssl_ctx; ///< SSL context
SSL *ssl; ///< SSL connection
@@ -131,6 +140,10 @@ struct bot_context
bool polling; ///< The event loop is running
};
static void on_irc_ping_timeout (void *user_data);
static void on_irc_timeout (void *user_data);
static void on_irc_reconnect_timeout (void *user_data);
static void
bot_context_init (struct bot_context *self)
{
@@ -152,6 +165,18 @@ bot_context_init (struct bot_context *self)
poller_init (&self->poller);
self->quitting = false;
self->polling = false;
poller_timer_init (&self->timeout_tmr, &self->poller);
self->timeout_tmr.dispatcher = on_irc_timeout;
self->timeout_tmr.user_data = self;
poller_timer_init (&self->ping_tmr, &self->poller);
self->ping_tmr.dispatcher = on_irc_ping_timeout;
self->ping_tmr.user_data = self;
poller_timer_init (&self->reconnect_tmr, &self->poller);
self->reconnect_tmr.dispatcher = on_irc_reconnect_timeout;
self->reconnect_tmr.user_data = self;
}
static void
@@ -172,7 +197,10 @@ bot_context_free (struct bot_context *self)
}
if (self->irc_fd != -1)
{
xclose (self->irc_fd);
poller_fd_reset (&self->irc_event);
}
if (self->ssl)
SSL_free (self->ssl);
if (self->ssl_ctx)
@@ -1060,10 +1088,7 @@ plugin_zombify (struct plugin_data *plugin)
// empty before closing it... and then on EOF check if `pid == -1' and
// only then dispose of it (it'd be best to simulate that both of these
// cases may happen).
ssize_t poller_idx =
poller_find_by_fd (&plugin->ctx->poller, plugin->write_fd);
if (poller_idx != -1)
poller_remove_at_index (&plugin->ctx->poller, poller_idx);
poller_fd_reset (&plugin->write_event);
// TODO: try to flush the write buffer (non-blocking)?
@@ -1083,7 +1108,6 @@ plugin_zombify (struct plugin_data *plugin)
static void
on_plugin_writable (const struct pollfd *fd, struct plugin_data *plugin)
{
struct bot_context *ctx = plugin->ctx;
struct str *buf = &plugin->write_buffer;
size_t written_total = 0;
@@ -1124,12 +1148,8 @@ on_plugin_writable (const struct pollfd *fd, struct plugin_data *plugin)
str_remove_slice (buf, 0, written_total);
if (buf->len == 0)
{
// Everything has been written, there's no need to end up in here again
ssize_t index = poller_find_by_fd (&ctx->poller, fd->fd);
if (index != -1)
poller_remove_at_index (&ctx->poller, index);
}
poller_fd_reset (&plugin->write_event);
}
static void
@@ -1148,9 +1168,7 @@ plugin_queue_write (struct plugin_data *plugin)
plugin_zombify (plugin);
return;
}
poller_set (&plugin->ctx->poller, plugin->write_fd, POLLOUT,
(poller_dispatcher_fn) on_plugin_writable, plugin);
poller_fd_set (&plugin->write_event, POLLOUT);
}
static void
@@ -1412,11 +1430,18 @@ plugin_load (struct bot_context *ctx, const char *name, struct error **e)
plugin->read_fd = stdout_pipe[0];
plugin->write_fd = stdin_pipe[1];
poller_fd_init (&plugin->read_event, &ctx->poller, plugin->read_fd);
plugin->read_event.dispatcher = (poller_fd_fn) on_plugin_readable;
plugin->read_event.user_data = plugin;
poller_fd_init (&plugin->write_event, &ctx->poller, plugin->write_fd);
plugin->write_event.dispatcher = (poller_fd_fn) on_plugin_writable;
plugin->write_event.user_data = plugin;
LIST_PREPEND (ctx->plugins, plugin);
str_map_set (&ctx->plugins_by_name, name, plugin);
poller_set (&ctx->poller, stdout_pipe[0], POLLIN,
(poller_dispatcher_fn) on_plugin_readable, plugin);
poller_fd_set (&plugin->read_event, POLLIN);
return true;
fail_3:
@@ -1818,14 +1843,13 @@ static void irc_queue_reconnect (struct bot_context *);
static void
irc_cancel_timers (struct bot_context *ctx)
{
ssize_t i;
struct poller_timers *timers = &ctx->poller.timers;
while ((i = poller_timers_find_by_data (timers, ctx)) != -1)
poller_timers_remove_at_index (timers, i);
poller_timer_reset (&ctx->timeout_tmr);
poller_timer_reset (&ctx->ping_tmr);
poller_timer_reset (&ctx->reconnect_tmr);
}
static void
irc_on_reconnect_timeout (void *user_data)
on_irc_reconnect_timeout (void *user_data)
{
struct bot_context *ctx = user_data;
@@ -1847,8 +1871,7 @@ irc_queue_reconnect (struct bot_context *ctx)
hard_assert (ctx->irc_fd == -1);
print_status ("trying to reconnect in %ld seconds...",
ctx->reconnect_delay);
poller_timers_add (&ctx->poller.timers,
irc_on_reconnect_timeout, ctx, ctx->reconnect_delay * 1000);
poller_timer_set (&ctx->reconnect_tmr, ctx->reconnect_delay * 1000);
}
static void
@@ -1863,14 +1886,13 @@ on_irc_disconnected (struct bot_context *ctx)
ctx->ssl_ctx = NULL;
}
ssize_t i = poller_find_by_fd (&ctx->poller, ctx->irc_fd);
if (i != -1)
poller_remove_at_index (&ctx->poller, i);
xclose (ctx->irc_fd);
ctx->irc_fd = -1;
ctx->irc_ready = false;
ctx->irc_event.closed = true;
poller_fd_reset (&ctx->irc_event);
// TODO: inform plugins about the disconnect event
// All of our timers have lost their meaning now
@@ -1905,10 +1927,8 @@ static void
irc_reset_connection_timeouts (struct bot_context *ctx)
{
irc_cancel_timers (ctx);
poller_timers_add (&ctx->poller.timers,
on_irc_timeout, ctx, 3 * 60 * 1000);
poller_timers_add (&ctx->poller.timers,
on_irc_ping_timeout, ctx, (3 * 60 + 30) * 1000);
poller_timer_set (&ctx->timeout_tmr, 3 * 60 * 1000);
poller_timer_set (&ctx->ping_tmr, (3 * 60 + 30) * 1000);
}
static void
@@ -2026,11 +2046,14 @@ irc_connect (struct bot_context *ctx, struct error **e)
}
print_status ("connection established");
poller_fd_init (&ctx->irc_event, &ctx->poller, ctx->irc_fd);
ctx->irc_event.dispatcher = (poller_fd_fn) on_irc_readable;
ctx->irc_event.user_data = ctx;
// TODO: in exec try: 1/ set blocking, 2/ setsockopt() SO_LINGER,
// (struct linger) { .l_onoff = true; .l_linger = 1 /* 1s should do */; }
// 3/ /* O_CLOEXEC */ But only if the QUIT message proves unreliable.
poller_set (&ctx->poller, ctx->irc_fd, POLLIN,
(poller_dispatcher_fn) on_irc_readable, ctx);
poller_fd_set (&ctx->irc_event, POLLIN);
irc_reset_connection_timeouts (ctx);
irc_send (ctx, "NICK %s", nickname);
@@ -2134,13 +2157,12 @@ on_signal_pipe_readable (const struct pollfd *fd, struct bot_context *ctx)
plugin->pid = -1;
ssize_t poller_idx = poller_find_by_fd (&ctx->poller, plugin->read_fd);
if (poller_idx != -1)
poller_remove_at_index (&ctx->poller, poller_idx);
xclose (plugin->read_fd);
plugin->read_fd = -1;
plugin->read_event.closed = true;
poller_fd_reset (&plugin->read_event);
LIST_UNLINK (ctx->plugins, plugin);
plugin_data_free (plugin);
free (plugin);
@@ -2215,8 +2237,11 @@ main (int argc, char *argv[])
}
setup_recovery_handler (&ctx);
poller_set (&ctx.poller, g_signal_pipe[0], POLLIN,
(poller_dispatcher_fn) on_signal_pipe_readable, &ctx);
poller_fd_init (&ctx.signal_event, &ctx.poller, g_signal_pipe[0]);
ctx.signal_event.dispatcher = (poller_fd_fn) on_signal_pipe_readable;
ctx.signal_event.user_data = &ctx;
poller_fd_set (&ctx.signal_event, POLLIN);
plugin_load_all_from_config (&ctx);
if (!parse_config (&ctx, &e)