From ed338b98358d21646b129624ee0d353e511450b0 Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Tue, 1 Mar 2016 19:47:50 -0800 Subject: [PATCH] INitial commit. --- README.md | 12 ++++- asyncaddrinfo.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++ asyncaddrinfo.h | 8 ++++ 3 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 asyncaddrinfo.c create mode 100644 asyncaddrinfo.h diff --git a/README.md b/README.md index bfd282a..c881429 100644 --- a/README.md +++ b/README.md @@ -1 +1,11 @@ -# asyncaddrinfo \ No newline at end of file +# asyncaddrinfo + +Asynchronous version of [getaddrinfo()](http://man7.org/linux/man-pages/man3/getaddrinfo.3.html). + +## Features + +* Uses getaddrinfo() underneath, so is quirk-compatible +* Configurable number of resolver threads +* Signals completion via a single file descriptor + * Compatible with select, poll, epoll, or blocking results +* Simple API; splits getaddrinfo() into two functions, with a file descriptor in between diff --git a/asyncaddrinfo.c b/asyncaddrinfo.c new file mode 100644 index 0000000..74edf69 --- /dev/null +++ b/asyncaddrinfo.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "asyncaddrinfo.h" + +struct asyncaddrinfo_resolution { + int return_fd; + + char *node; + char *service; + struct addrinfo _hints, *hints; + + int err; + struct addrinfo *addrs; +}; + +static size_t asyncaddrinfo_num_threads; +static pthread_t *asyncaddrinfo_threads = NULL; +static int asyncaddrinfo_write_fd; + +static void *asyncaddrinfo_main(void *arg) { + int fd = (int) (intptr_t) arg; + struct asyncaddrinfo_resolution *res; + ssize_t len; + while ((len = recv(fd, &res, sizeof(res), 0)) == sizeof(res)) { + res->err = getaddrinfo(res->node, res->service, res->hints, &res->addrs); + int return_fd = res->return_fd; + res->return_fd = -1; + assert(send(return_fd, &res, sizeof(res), MSG_EOR) == sizeof(res)); + // Main thread now owns res + assert(!close(return_fd)); + } + assert(!len); + assert(!close(fd)); + return NULL; +} + +static void asyncaddrinfo_del(struct asyncaddrinfo_resolution *res) { + if (res->node) { + free(res->node); + res->node = NULL; + } + if (res->service) { + free(res->service); + res->service = NULL; + } + free(res); +} + +void asyncaddrinfo_init(size_t threads) { + assert(!asyncaddrinfo_threads); + + int fds[2]; + assert(!socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, fds)); + asyncaddrinfo_write_fd = fds[1]; + + asyncaddrinfo_num_threads = threads; + asyncaddrinfo_threads = malloc(asyncaddrinfo_num_threads * sizeof(*asyncaddrinfo_threads)); + assert(asyncaddrinfo_threads); + + for (size_t i = 0; i < asyncaddrinfo_num_threads; i++) { + int subfd = dup(fds[0]); + assert(subfd >= 0); + assert(!pthread_create(&asyncaddrinfo_threads[i], NULL, asyncaddrinfo_main, (void *) (intptr_t) subfd)); + } + assert(!close(fds[0])); +} + +void asyncaddrinfo_cleanup() { + assert(asyncaddrinfo_threads); + assert(!close(asyncaddrinfo_write_fd)); + asyncaddrinfo_write_fd = -1; + for (size_t i = 0; i < asyncaddrinfo_num_threads; i++) { + assert(!pthread_join(asyncaddrinfo_threads[i], NULL)); + } + free(asyncaddrinfo_threads); + asyncaddrinfo_threads = NULL; +} + +int asyncaddrinfo_resolve(const char *node, const char *service, struct addrinfo *hints) { + int fds[2]; + assert(!socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, fds)); + + struct asyncaddrinfo_resolution *res = malloc(sizeof(*res)); + assert(res); + res->return_fd = fds[1]; + if (node) { + res->node = strdup(node); + assert(res->node); + } else { + res->node = NULL; + } + if (service) { + res->service = strdup(service); + assert(res->service); + } else { + res->service = NULL; + } + if (hints) { + memcpy(&res->_hints, hints, sizeof(res->_hints)); + res->hints = &res->_hints; + } else { + res->hints = NULL; + } + assert(send(asyncaddrinfo_write_fd, &res, sizeof(res), MSG_EOR) == sizeof(res)); + // Resolve thread now owns res + + return fds[0]; +} + +int asyncaddrinfo_result(int fd, struct addrinfo **addrs) { + struct asyncaddrinfo_resolution *res; + assert(recv(fd, &res, sizeof(res), 0) == sizeof(res)); + assert(!close(fd)); + *addrs = res->addrs; + int err = res->err; + asyncaddrinfo_del(res); + return err; +} diff --git a/asyncaddrinfo.h b/asyncaddrinfo.h new file mode 100644 index 0000000..a0a7ef6 --- /dev/null +++ b/asyncaddrinfo.h @@ -0,0 +1,8 @@ +#pragma once + +struct addrinfo; + +void asyncaddrinfo_init(size_t threads); +void asyncaddrinfo_cleanup(void); +int asyncaddrinfo_resolve(const char *node, const char *service, struct addrinfo *hints); +int asyncaddrinfo_result(int fd, struct addrinfo **addrs);