changeset 1133:8ea7366ef45a

lib: revert to using a pipe for irc_bot_post Using a signal is discouraged and requires the bot to setup a dummy handler. Also protect the call to irc_bot_post to be fully threadsafe.
author David Demelier <markand@malikania.fr>
date Thu, 09 Dec 2021 14:13:14 +0100
parents 6f85c4743494
children e16b0e3a137c
files lib/irccd/irccd.c
diffstat 1 files changed, 43 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/lib/irccd/irccd.c	Thu Dec 09 11:34:49 2021 +0100
+++ b/lib/irccd/irccd.c	Thu Dec 09 14:13:14 2021 +0100
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <unistd.h>
 
 #include <utlist.h>
 
@@ -48,7 +49,9 @@
 
 static struct sigaction sa;
 static struct defer *queue;
+static int pipes[2];
 static pthread_t self;
+static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
 
 static int
 is_command(const struct irc_plugin *p, const struct irc_event *ev)
@@ -240,6 +243,17 @@
 	return 0;
 }
 
+static void
+pipe_flush(const struct pollfd *fd)
+{
+	int dummy;
+
+	if (fd->fd != pipes[0] || !(fd->revents & POLLIN))
+		return;
+	if (read(pipes[0], &dummy, sizeof (int)) != sizeof (int))
+		irc_util_die("read: %s\n", strerror(errno));
+}
+
 void
 irc_bot_init(void)
 {
@@ -251,6 +265,8 @@
 
 	if (sigaction(SIGCHLD, &sa, NULL) < 0)
 		irc_util_die("sigaction: %s\n", strerror(errno));
+	if (pipe(pipes) < 0)
+		irc_util_die("pipe: %s\n", strerror(errno));
 
 	self = pthread_self();
 }
@@ -566,7 +582,7 @@
 size_t
 irc_bot_poll_size(void)
 {
-	size_t i = 0;
+	size_t i = 1;
 	struct irc_server *s;
 
 	LL_FOREACH(irc.servers, s)
@@ -582,6 +598,10 @@
 
 	struct irc_server *s;
 
+	fds->fd = pipes[0];
+	fds->events = POLLIN;
+	fds++;
+
 	LL_FOREACH(irc.servers, s)
 		irc_server_prepare(s, fds++);
 }
@@ -594,6 +614,8 @@
 	struct irc_server *s;
 	struct defer *d, *dtmp;
 
+	pipe_flush(fds++);
+
 	LL_FOREACH_SAFE(queue, d, dtmp) {
 		d->exec(d->data);
 		free(d);
@@ -624,6 +646,10 @@
 irc_bot_post(void (*exec)(void *), void *data)
 {
 	struct defer *d;
+	int dummy = 1;
+
+	if (pthread_mutex_lock(&mtx) < 0)
+		irc_util_die("pthread_mutex_lock: %s\n", strerror(errno));
 
 	d = irc_util_calloc(1, sizeof (*d));
 	d->exec = exec;
@@ -633,13 +659,21 @@
 
 	/* Signal only if I'm not the same thread. */
 	if (!pthread_equal(pthread_self(), self))
-		pthread_kill(self, SIGUSR1);
+		if (write(pipes[1], &dummy, sizeof (int)) != sizeof (int))
+			irc_util_die("write: %s\n", strerror(errno));
+
+	if (pthread_mutex_unlock(&mtx) < 0)
+		irc_util_die("pthread_mutex_unlock: %s\n", strerror(errno));
 }
 
 void
 irc_bot_finish(void)
 {
 	struct irc_plugin_loader *ld, *ldtmp;
+	struct defer *d, *dtmp;
+
+	close(pipes[0]);
+	close(pipes[1]);
 
 	/*
 	 * First remove all loaders to mkae sure plugins won't try to load
@@ -648,7 +682,14 @@
 	LL_FOREACH_SAFE(irc.plugin_loaders, ld, ldtmp)
 		irc_plugin_loader_finish(ld);
 
+	/* Remove all deferred calls. */
+	LL_FOREACH_SAFE(queue, d, dtmp)
+		free(d);
+
+	queue = NULL;
 	irc_bot_server_clear();
 	irc_bot_plugin_clear();
 	irc_bot_rule_clear();
+
+	pthread_mutex_destroy(&mtx);
 }