From a34d4da7558a61838d031fbaaee9902657c5171e Mon Sep 17 00:00:00 2001
From: Richard Nyberg <rnyberg@murmeldjur.se>
Date: Tue, 23 Dec 2008 23:35:07 +0100
Subject: [PATCH] Add a thread for performing asynchronous getaddrinfo's.

---
 btpd/Makefile.am |  2 +-
 btpd/addrinfo.c  | 97 ++++++++++++++++++++++++++++++++++++++++++++++++
 btpd/btpd.c      |  2 +
 btpd/btpd.h      |  6 +++
 4 files changed, 106 insertions(+), 1 deletion(-)
 create mode 100644 btpd/addrinfo.c

diff --git a/btpd/Makefile.am b/btpd/Makefile.am
index 2568763..197ee3b 100644
--- a/btpd/Makefile.am
+++ b/btpd/Makefile.am
@@ -1,6 +1,6 @@
 bin_PROGRAMS=btpd
 btpd_SOURCES=\
-        active.c active.h\
+        active.c active.h addrinfo.c\
 	btpd.c btpd.h\
 	cli_if.c content.c content.h\
 	download.c download_subr.c download.h\
diff --git a/btpd/addrinfo.c b/btpd/addrinfo.c
new file mode 100644
index 0000000..8bff8b2
--- /dev/null
+++ b/btpd/addrinfo.c
@@ -0,0 +1,97 @@
+#include "btpd.h"
+
+#include <pthread.h>
+
+struct ai_ctx {
+    BTPDQ_ENTRY(ai_ctx) entry;
+    struct addrinfo hints;
+    struct addrinfo *res;
+    char node[255], service[6];
+    void (*cb)(void *, int, struct addrinfo *);
+    void *arg;
+    int cancel;
+    int error;
+    short port;
+};
+
+BTPDQ_HEAD(ai_ctx_tq, ai_ctx);
+
+static struct ai_ctx_tq m_aiq = BTPDQ_HEAD_INITIALIZER(m_aiq);
+static pthread_mutex_t m_aiq_lock;
+static pthread_cond_t m_aiq_cond;
+
+struct ai_ctx *
+btpd_addrinfo(const char *node, short port, struct addrinfo *hints,
+    void (*cb)(void *, int, struct addrinfo *), void *arg)
+{
+    struct ai_ctx *ctx = btpd_calloc(1, sizeof(*ctx));
+    ctx->hints = *hints;
+    ctx->cb = cb;
+    ctx->arg = arg;    
+    snprintf(ctx->node, sizeof(ctx->node), "%s", node);
+    ctx->port = port;
+    if (port > 0)
+        snprintf(ctx->service, sizeof(ctx->service), "%hd", port);
+
+    pthread_mutex_lock(&m_aiq_lock);
+    BTPDQ_INSERT_TAIL(&m_aiq, ctx, entry);
+    pthread_mutex_unlock(&m_aiq_lock);
+    pthread_cond_signal(&m_aiq_cond);
+
+    return ctx;
+}
+
+void
+btpd_addrinfo_cancel(struct ai_ctx *ctx)
+{
+    ctx->cancel = 1;
+}
+
+static void
+addrinfo_td_cb(void *arg)
+{
+    struct ai_ctx *ctx = arg;
+    if (!ctx->cancel)
+        ctx->cb(ctx->arg, ctx->error, ctx->res);
+    else if (ctx->error != 0)
+        freeaddrinfo(ctx->res);
+    free(ctx);
+}
+
+static void *
+addrinfo_td(void *arg)
+{
+    struct ai_ctx *ctx;
+    char *service;
+    while (1) {
+        pthread_mutex_lock(&m_aiq_lock);
+        while (BTPDQ_EMPTY(&m_aiq))
+            pthread_cond_wait(&m_aiq_cond, &m_aiq_lock);
+        ctx = BTPDQ_FIRST(&m_aiq);
+        BTPDQ_REMOVE(&m_aiq, ctx, entry);
+        pthread_mutex_unlock(&m_aiq_lock);
+
+        service = ctx->port > 0 ? ctx->service : NULL;
+        ctx->error = getaddrinfo(ctx->node, service, &ctx->hints, &ctx->res);
+
+        td_post_begin();
+        td_post(addrinfo_td_cb, ctx);
+        td_post_end();
+    }
+}
+
+static void
+errdie(int err, const char *str)
+{
+    if (err != 0)
+        btpd_err("addrinfo_init: %s (%s).\n", str, strerror(errno));
+}
+
+void
+addrinfo_init(void)
+{
+    pthread_t td;
+    errdie(pthread_mutex_init(&m_aiq_lock, NULL), "pthread_mutex_init");
+    errdie(pthread_cond_init(&m_aiq_cond, NULL), "pthread_cond_init");
+    errdie(pthread_create(&td, NULL, addrinfo_td, NULL), "pthread_create");
+}
diff --git a/btpd/btpd.c b/btpd/btpd.c
index c8bd5d2..e6e0f30 100644
--- a/btpd/btpd.c
+++ b/btpd/btpd.c
@@ -77,6 +77,7 @@ heartbeat_cb(int fd, short type, void *arg)
 void tr_init(void);
 void ipc_init(void);
 void td_init(void);
+void addrinfo_init(void);
 
 void
 btpd_init(void)
@@ -102,6 +103,7 @@ btpd_init(void)
     srandom(seed);
 
     td_init();
+    addrinfo_init();
     net_init();
     ipc_init();
     ul_init();
diff --git a/btpd/btpd.h b/btpd/btpd.h
index fe6cabf..f03df65 100644
--- a/btpd/btpd.h
+++ b/btpd/btpd.h
@@ -8,6 +8,7 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <netdb.h>
 
 #include <assert.h>
 #include <errno.h>
@@ -84,4 +85,9 @@ void td_post(void (*cb)(void *), void *arg);
 void td_post_end();
 #define td_post_begin td_acquire_lock
 
+typedef struct ai_ctx * aictx_t;
+aictx_t btpd_addrinfo(const char *node, short port, struct addrinfo *hints,
+    void (*cb)(void *, int, struct addrinfo *), void *arg);
+void btpd_addrinfo_cancel(aictx_t ctx); 
+
 #endif