1/*
2 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6
7#include <port_before.h>
8#ifdef DO_PTHREADS
9#include <pthread.h>
10#endif
11#include <errno.h>
12#include <netdb.h>
13#include <stdlib.h>
14#include <string.h>
15#include <resolv_mt.h>
16#include <irs.h>
17#include <port_after.h>
18
19#ifdef DO_PTHREADS
20static pthread_key_t	key;
21static int		mt_key_initialized = 0;
22
23static int		__res_init_ctx(void);
24static void		__res_destroy_ctx(void *);
25
26#if defined(sun) && !defined(__GNUC__)
27#pragma init	(_mtctxres_init)
28#endif
29#endif
30
31static mtctxres_t	sharedctx;
32
33#ifdef DO_PTHREADS
34/*
35 * Initialize the TSD key. By doing this at library load time, we're
36 * implicitly running without interference from other threads, so there's
37 * no need for locking.
38 */
39static void
40_mtctxres_init(void) {
41	int pthread_keycreate_ret;
42
43	pthread_keycreate_ret = pthread_key_create(&key, __res_destroy_ctx);
44	if (pthread_keycreate_ret == 0)
45		mt_key_initialized = 1;
46}
47#endif
48
49/*
50 * To support binaries that used the private MT-safe interface in
51 * Solaris 8, we still need to provide the __res_enable_mt()
52 * and __res_disable_mt() entry points. They're do-nothing routines.
53 */
54int
55__res_enable_mt(void) {
56	return (-1);
57}
58
59int
60__res_disable_mt(void) {
61	return (0);
62}
63
64#ifdef DO_PTHREADS
65static int
66__res_init_ctx(void) {
67
68	mtctxres_t	*mt;
69	int		ret;
70
71
72	if (pthread_getspecific(key) != 0) {
73		/* Already exists */
74		return (0);
75	}
76
77	if ((mt = malloc(sizeof (mtctxres_t))) == 0) {
78		errno = ENOMEM;
79		return (-1);
80	}
81
82	memset(mt, 0, sizeof (mtctxres_t));
83
84	if ((ret = pthread_setspecific(key, mt)) != 0) {
85		free(mt);
86		errno = ret;
87		return (-1);
88	}
89
90	return (0);
91}
92
93static void
94__res_destroy_ctx(void *value) {
95
96	mtctxres_t	*mt = (mtctxres_t *)value;
97
98	if (mt != 0)
99		free(mt);
100}
101#endif
102
103mtctxres_t *
104___mtctxres(void) {
105#ifdef DO_PTHREADS
106	mtctxres_t	*mt;
107
108	/*
109	 * This if clause should only be executed if we are linking
110	 * statically.  When linked dynamically _mtctxres_init() should
111	 * be called at binding time due the #pragma above.
112	 */
113	if (!mt_key_initialized) {
114		static pthread_mutex_t keylock = PTHREAD_MUTEX_INITIALIZER;
115                if (pthread_mutex_lock(&keylock) == 0) {
116			_mtctxres_init();
117			(void) pthread_mutex_unlock(&keylock);
118		}
119	}
120
121	/*
122	 * If we have already been called in this thread return the existing
123	 * context.  Otherwise recreat a new context and return it.  If
124	 * that fails return a global context.
125	 */
126	if (mt_key_initialized) {
127		if (((mt = pthread_getspecific(key)) != 0) ||
128		    (__res_init_ctx() == 0 &&
129		     (mt = pthread_getspecific(key)) != 0)) {
130			return (mt);
131		}
132	}
133#endif
134	return (&sharedctx);
135}
136