Skip site navigation (1)Skip section navigation (2)
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>