Brian Feldman introduced new versions of the resolver and getaddrinfo DNS functions that are mostly* reentrant**.
The getaddrinfo(3) function is defined for protocol-independent nodename-to-address translation. It performs the functionality of gethostbyname(3) and getservbyname(3), but in a more sophisticated manner.
* mostly - because it is still need to be polished and tested to be fully reentrant.
** reentrant function - a function that can be used at the same time from multiple threads, so long as they do it with different data.
From: "Brian F. Feldman" [email blocked] Date: Mon, 09 Feb 2004 21:52:51 -0500 Subject: Re: mostly-reentrant resolver/getaddrinfo(3) List-Id: Technical Discussions relating to FreeBSD <freebsd-hackers.freebsd.org> List-Archive: http://lists.freebsd.org/pipermail/freebsd-hackers List-Subscribe: http://lists.freebsd.org/mailman/listinfo/freebsd-hackers 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