Date: Mon, 09 Feb 2004 21:52:51 -0500 From: "Brian F. Feldman" <green@FreeBSD.org> To: "Jacques A. Vidrine" <nectar@FreeBSD.org>, hackers@FreeBSD.org Subject: Re: mostly-reentrant resolver/getaddrinfo(3) Message-ID: <200402100252.i1A2qpHl033207@green.homeunix.org> In-Reply-To: Message from "Brian F. Feldman" <green@FreeBSD.org> <200402091636.i19GaYYk024697@green.homeunix.org>
next in thread | previous in thread | raw e-mail | index | archive | help
Alright, here we go! I simplified some things out a bit and used pthread_once(3) to make things look a little cleaner. The RES_BOGUS flag was unnecessary, and now single-threaded programs and the first thread of multi-threaded programs do not incur the allocation of per-thread resolver storage. I also allocated all the per-thread storage at once because the user is not privy to that, anyway. I've gotten good feedback so far, and the latest round of changes at the least "works for me" :) Try it in your multi-tab Mozilla! Index: include/resolv.h =================================================================== RCS file: /usr/ncvs/src/include/resolv.h,v retrieving revision 1.23 diff -u -r1.23 resolv.h --- include/resolv.h 7 Dec 2003 12:32:23 -0000 1.23 +++ include/resolv.h 10 Feb 2004 00:55:35 -0000 @@ -200,7 +200,12 @@ char * humanname; /* Its fun name, like "mail exchanger" */ }; -extern struct __res_state _res; +__BEGIN_DECLS +extern struct __res_state *___res(void); +extern struct __res_state_ext *___res_ext(void); +__END_DECLS +#define _res (*___res()) +#define _res_ext (*___res_ext()) /* for INET6 */ extern struct __res_state_ext _res_ext; Index: lib/libc/include/reentrant.h =================================================================== RCS file: /usr/ncvs/src/lib/libc/include/reentrant.h,v retrieving revision 1.2 diff -u -r1.2 reentrant.h --- lib/libc/include/reentrant.h 1 Nov 2002 09:37:17 -0000 1.2 +++ lib/libc/include/reentrant.h 10 Feb 2004 01:11:45 -0000 @@ -94,10 +94,12 @@ #define mutex_t pthread_mutex_t #define cond_t pthread_cond_t #define rwlock_t pthread_rwlock_t +#define once_t pthread_once_t #define thread_key_t pthread_key_t #define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER #define RWLOCK_INITIALIZER PTHREAD_RWLOCK_INITIALIZER +#define ONCE_INITIALIZER PTHREAD_ONCE_INIT #define mutex_init(m, a) _pthread_mutex_init(m, a) #define mutex_lock(m) if (__isthreaded) \ @@ -127,6 +129,7 @@ #define thr_getspecific(k) _pthread_getspecific(k) #define thr_sigsetmask(f, n, o) _pthread_sigmask(f, n, o) +#define thr_once(o, i) _pthread_once(o, i) #define thr_self() _pthread_self() #define thr_exit(x) _pthread_exit(x) #define thr_main() _pthread_main_np() Index: lib/libc/net/getaddrinfo.c =================================================================== RCS file: /usr/ncvs/src/lib/libc/net/getaddrinfo.c,v retrieving revision 1.48 diff -u -r1.48 getaddrinfo.c --- lib/libc/net/getaddrinfo.c 30 Oct 2003 17:36:53 -0000 1.48 +++ lib/libc/net/getaddrinfo.c 6 Feb 2004 06:20:23 -0000 @@ -1511,6 +1511,7 @@ return 0; } + THREAD_UNLOCK(); switch (_nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo", default_dns_files, hostname, pai)) { case NS_TRYAGAIN: @@ -1524,20 +1525,20 @@ goto free; case NS_SUCCESS: error = 0; + THREAD_LOCK(); for (cur = result; cur; cur = cur->ai_next) { GET_PORT(cur, servname); /* canonname should be filled already */ } + THREAD_UNLOCK(); break; } - THREAD_UNLOCK(); *res = result; return 0; free: - THREAD_UNLOCK(); if (result) freeaddrinfo(result); return error; @@ -2037,6 +2038,7 @@ memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; + THREAD_LOCK(); _sethtent(); while ((p = _gethtent(name, pai)) != NULL) { cur->ai_next = p; @@ -2044,6 +2046,7 @@ cur = cur->ai_next; } _endhtent(); + THREAD_UNLOCK(); *((struct addrinfo **)rv) = sentinel.ai_next; if (sentinel.ai_next == NULL) @@ -2152,9 +2155,12 @@ memset(&sentinel, 0, sizeof(sentinel)); cur = &sentinel; + THREAD_LOCK(); if (!__ypdomain) { - if (_yp_check(&__ypdomain) == 0) + if (_yp_check(&__ypdomain) == 0) { + THREAD_UNLOCK(); return NS_UNAVAIL; + } } if (__ypcurrent) free(__ypcurrent); @@ -2189,6 +2195,7 @@ cur = cur->ai_next; } } + THREAD_UNLOCK(); if (sentinel.ai_next == NULL) { h_errno = HOST_NOT_FOUND; Index: lib/libc/net/res_init.c =================================================================== RCS file: /usr/ncvs/src/lib/libc/net/res_init.c,v retrieving revision 1.31 diff -u -r1.31 res_init.c --- lib/libc/net/res_init.c 7 Dec 2003 12:32:24 -0000 1.31 +++ lib/libc/net/res_init.c 10 Feb 2004 01:01:04 -0000 @@ -91,7 +91,11 @@ #include <unistd.h> #include <netdb.h> +#include "namespace.h" +#include "reentrant.h" +#include "un-namespace.h" #include "res_config.h" +#include "res_send_private.h" static void res_setoptions(char *, char *); @@ -106,16 +110,13 @@ #endif /* - * Resolver state default settings. + * Check structure for failed per-thread allocations. */ - -struct __res_state _res -# if defined(__BIND_RES_TEXT) - = { RES_TIMEOUT, } /* Motorola, et al. */ -# endif - ; - -struct __res_state_ext _res_ext; +static struct res_per_thread { + struct __res_state res_state; + struct __res_state_ext res_state_ext; + struct __res_send_private res_send_private; +} _res_per_thread_bogus; /* * Set up default settings. If the configuration file exist, the values @@ -142,6 +143,7 @@ res_init() { FILE *fp; + struct __res_send_private *rsp; char *cp, **pp; int n; char buf[MAXDNAME]; @@ -157,6 +159,19 @@ #endif /* + * If allocation of memory for this thread's resolver has failed, + * return the error to the user. + */ + if (&_res == &_res_per_thread_bogus.res_state) + return (-1); + rsp = ___res_send_private(); + rsp->s = -1; + rsp->connected = 0; + rsp->vc = 0; + rsp->af = 0; + rsp->Qhook = NULL; + rsp->Rhook = NULL; + /* * These three fields used to be statically initialized. This made * it hard to use this code in a shared library. It is necessary, * now that we're doing dynamic initialization here, that we preserve @@ -595,6 +610,88 @@ gettimeofday(&now, NULL); return (0xffff & (now.tv_sec ^ now.tv_usec ^ getpid())); +} + +/* + * Resolver state default settings. + */ + +#undef _res +#undef _res_ext +#ifdef __BIND_RES_TEXT +struct __res_state _res = { RES_TIMEOUT }; /* Motorola, et al. */ +#else +struct __res_state _res; +#endif +struct __res_state_ext _res_ext; +static struct __res_send_private _res_send_private; + +static thread_key_t res_key; +static once_t res_init_once = ONCE_INITIALIZER; +static int res_thr_keycreated = 0; + +static void +free_res(void *ptr) +{ + struct res_per_thread *myrsp = ptr; + + if (myrsp->res_state.options & RES_INIT) + res_close(); + free(myrsp); +} + +static void +res_keycreate(void) +{ + res_thr_keycreated = thr_keycreate(&res_key, free_res) == 0; +} + +static struct res_per_thread * +allocate_res(void) +{ + struct res_per_thread *myrsp; + + if (thr_once(&res_init_once, res_keycreate) != 0 || + !res_thr_keycreated) + return (&_res_per_thread_bogus); + + myrsp = thr_getspecific(res_key); + if (myrsp != NULL) + return (myrsp); + myrsp = calloc(1, sizeof(*myrsp)); + if (myrsp == NULL) + return (&_res_per_thread_bogus); +#ifdef __BIND_RES_TEXT + myrsp->res_state.options = RES_TIMEOUT; /* Motorola, et al. */ +#endif + if (thr_setspecific(res_key, myrsp) == 0) + return (myrsp); + free(myrsp); + return (&_res_per_thread_bogus); +} + +struct __res_state * +___res(void) +{ + if (thr_main() != 0) + return (&_res); + return (&allocate_res()->res_state); +} + +struct __res_state_ext * +___res_ext(void) +{ + if (thr_main() != 0) + return (&_res_ext); + return (&allocate_res()->res_state_ext); +} + +struct __res_send_private * +___res_send_private(void) +{ + if (thr_main() != 0) + return (&_res_send_private); + return (&allocate_res()->res_send_private); } /* Index: lib/libc/net/res_send.c =================================================================== RCS file: /usr/ncvs/src/lib/libc/net/res_send.c,v retrieving revision 1.46 diff -u -r1.46 res_send.c --- lib/libc/net/res_send.c 6 Jan 2004 18:45:13 -0000 1.46 +++ lib/libc/net/res_send.c 6 Feb 2004 06:07:50 -0000 @@ -101,14 +101,15 @@ #include "un-namespace.h" #include "res_config.h" +#include "res_send_private.h" -static int s = -1; /* socket used for communications */ -static int connected = 0; /* is the socket connected */ -static int vc = 0; /* is the socket a virtual circuit? */ -static int af = 0; /* address family of socket */ -static res_send_qhook Qhook = NULL; -static res_send_rhook Rhook = NULL; +#define s ___res_send_private()->s +#define connected ___res_send_private()->connected +#define vc ___res_send_private()->vc +#define af ___res_send_private()->af +#define Qhook ___res_send_private()->Qhook +#define Rhook ___res_send_private()->Rhook #define CAN_RECONNECT 1 @@ -123,8 +124,6 @@ fprintf args;\ __fp_nquery(query, size, stdout);\ } else {} -static char abuf[NI_MAXHOST]; -static char pbuf[NI_MAXSERV]; static void Aerror(FILE *, char *, int, struct sockaddr *); static void Perror(FILE *, char *, int); @@ -138,6 +137,9 @@ int save = errno; if (_res.options & RES_DEBUG) { + char abuf[NI_MAXHOST]; + char pbuf[NI_MAXSERV]; + if (getnameinfo(address, address->sa_len, abuf, sizeof(abuf), pbuf, sizeof(pbuf), NI_NUMERICHOST|NI_NUMERICSERV|NI_WITHSCOPEID) != 0) { @@ -388,6 +390,7 @@ */ for (try = 0; try < _res.retry; try++) { for (ns = 0; ns < _res.nscount; ns++) { + char abuf[NI_MAXHOST]; struct sockaddr *nsap = get_nsaddr(ns); socklen_t salen; Index: lib/libc/net/res_send_private.h =================================================================== RCS file: lib/libc/net/res_send_private.h diff -N lib/libc/net/res_send_private.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ lib/libc/net/res_send_private.h 9 Feb 2004 02:53:27 -0000 @@ -0,0 +1,10 @@ +struct __res_send_private { + int s; /* socket used for communications */ + int connected; /* is the socket connected */ + int vc; /* is the socket a virtual circuit? */ + int af; /* address family of socket */ + res_send_qhook Qhook; + res_send_rhook Rhook; +}; + +struct __res_send_private *___res_send_private(void); -- Brian Fundakowski Feldman \'[ FreeBSD ]''''''''''\ <> green@FreeBSD.org \ The Power to Serve! \ Opinions expressed are my own. \,,,,,,,,,,,,,,,,,,,,,,\
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200402100252.i1A2qpHl033207>