xref: /freebsd/lib/libthr/thread/thr_spec.c (revision 37a6f4619063f73461e306a5586e91979c6f7198)
1 /*
2  * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the author nor the names of any co-contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include "namespace.h"
34 #include <sys/mman.h>
35 #include <signal.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <pthread.h>
40 #include "un-namespace.h"
41 #include "libc_private.h"
42 
43 #include "thr_private.h"
44 
45 /* Used in symbol lookup of libthread_db */
46 struct pthread_key _thread_keytable[PTHREAD_KEYS_MAX];
47 
48 __weak_reference(_pthread_key_create, pthread_key_create);
49 __weak_reference(_pthread_key_delete, pthread_key_delete);
50 __weak_reference(_pthread_getspecific, pthread_getspecific);
51 __weak_reference(_pthread_setspecific, pthread_setspecific);
52 
53 
54 int
55 _pthread_key_create(pthread_key_t *key, void (*destructor)(void *))
56 {
57 	struct pthread *curthread;
58 	int i;
59 
60 	_thr_check_init();
61 
62 	curthread = _get_curthread();
63 
64 	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
65 	for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
66 
67 		if (_thread_keytable[i].allocated == 0) {
68 			_thread_keytable[i].allocated = 1;
69 			_thread_keytable[i].destructor = destructor;
70 			_thread_keytable[i].seqno++;
71 
72 			THR_LOCK_RELEASE(curthread, &_keytable_lock);
73 			*key = i + 1;
74 			return (0);
75 		}
76 
77 	}
78 	THR_LOCK_RELEASE(curthread, &_keytable_lock);
79 	return (EAGAIN);
80 }
81 
82 int
83 _pthread_key_delete(pthread_key_t userkey)
84 {
85 	struct pthread *curthread;
86 	int key, ret;
87 
88 	key = userkey - 1;
89 	if ((unsigned int)key >= PTHREAD_KEYS_MAX)
90 		return (EINVAL);
91 	curthread = _get_curthread();
92 	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
93 	if (_thread_keytable[key].allocated) {
94 		_thread_keytable[key].allocated = 0;
95 		ret = 0;
96 	} else {
97 		ret = EINVAL;
98 	}
99 	THR_LOCK_RELEASE(curthread, &_keytable_lock);
100 	return (ret);
101 }
102 
103 void
104 _thread_cleanupspecific(void)
105 {
106 	struct pthread *curthread;
107 	void (*destructor)(void *);
108 	const void *data;
109 	int i, key;
110 
111 	curthread = _get_curthread();
112 	if (curthread->specific == NULL)
113 		return;
114 	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
115 	for (i = 0; i < PTHREAD_DESTRUCTOR_ITERATIONS &&
116 	    curthread->specific_data_count > 0; i++) {
117 		for (key = 0; key < PTHREAD_KEYS_MAX &&
118 		    curthread->specific_data_count > 0; key++) {
119 			destructor = NULL;
120 
121 			if (_thread_keytable[key].allocated &&
122 			    (curthread->specific[key].data != NULL)) {
123 				if (curthread->specific[key].seqno ==
124 				    _thread_keytable[key].seqno) {
125 					data = curthread->specific[key].data;
126 					destructor = _thread_keytable[key].
127 					    destructor;
128 				}
129 				curthread->specific[key].data = NULL;
130 				curthread->specific_data_count--;
131 			} else if (curthread->specific[key].data != NULL) {
132 				/*
133 				 * This can happen if the key is
134 				 * deleted via pthread_key_delete
135 				 * without first setting the value to
136 				 * NULL in all threads.  POSIX says
137 				 * that the destructor is not invoked
138 				 * in this case.
139 				 */
140 				curthread->specific[key].data = NULL;
141 				curthread->specific_data_count--;
142 			}
143 
144 			/*
145 			 * If there is a destructor, call it with the
146 			 * key table entry unlocked.
147 			 */
148 			if (destructor != NULL) {
149 				THR_LOCK_RELEASE(curthread, &_keytable_lock);
150 				destructor(__DECONST(void *, data));
151 				THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
152 			}
153 		}
154 	}
155 	THR_LOCK_RELEASE(curthread, &_keytable_lock);
156 	munmap(curthread->specific, PTHREAD_KEYS_MAX * sizeof(struct
157 	    pthread_specific_elem));
158 	curthread->specific = NULL;
159 	if (curthread->specific_data_count > 0) {
160 		stderr_debug("Thread %p has exited with leftover "
161 		    "thread-specific data after %d destructor iterations\n",
162 		    curthread, PTHREAD_DESTRUCTOR_ITERATIONS);
163 	}
164 }
165 
166 int
167 _pthread_setspecific(pthread_key_t userkey, const void *value)
168 {
169 	struct pthread *pthread;
170 	void *tmp;
171 	pthread_key_t key;
172 
173 	key = userkey - 1;
174 	if ((unsigned int)key >= PTHREAD_KEYS_MAX ||
175 	    !_thread_keytable[key].allocated)
176 		return (EINVAL);
177 
178 	pthread = _get_curthread();
179 	if (pthread->specific == NULL) {
180 		tmp = mmap(NULL, PTHREAD_KEYS_MAX *
181 		    sizeof(struct pthread_specific_elem),
182 		    PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
183 		if (tmp == MAP_FAILED)
184 			return (ENOMEM);
185 		pthread->specific = tmp;
186 	}
187 	if (pthread->specific[key].data == NULL) {
188 		if (value != NULL)
189 			pthread->specific_data_count++;
190 	} else if (value == NULL)
191 		pthread->specific_data_count--;
192 	pthread->specific[key].data = value;
193 	pthread->specific[key].seqno = _thread_keytable[key].seqno;
194 	return (0);
195 }
196 
197 void *
198 _pthread_getspecific(pthread_key_t userkey)
199 {
200 	struct pthread *pthread;
201 	const void *data;
202 	pthread_key_t key;
203 
204 	/* Check if there is specific data. */
205 	key = userkey - 1;
206 	if ((unsigned int)key >= PTHREAD_KEYS_MAX)
207 		return (NULL);
208 
209 	pthread = _get_curthread();
210 	/* Check if this key has been used before. */
211 	if (_thread_keytable[key].allocated && pthread->specific != NULL &&
212 	    pthread->specific[key].seqno == _thread_keytable[key].seqno) {
213 		/* Return the value: */
214 		data = pthread->specific[key].data;
215 	} else {
216 		/*
217 		 * This key has not been used before, so return NULL
218 		 * instead.
219 		 */
220 		data = NULL;
221 	}
222 	return (__DECONST(void *, data));
223 }
224 
225 void
226 _thr_tsd_unload(struct dl_phdr_info *phdr_info)
227 {
228 	struct pthread *curthread;
229 	void (*destructor)(void *);
230 	int key;
231 
232 	curthread = _get_curthread();
233 	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
234 	for (key = 0; key < PTHREAD_KEYS_MAX; key++) {
235 		if (!_thread_keytable[key].allocated)
236 			continue;
237 		destructor = _thread_keytable[key].destructor;
238 		if (destructor == NULL)
239 			continue;
240 		if (__elf_phdr_match_addr(phdr_info, destructor))
241 			_thread_keytable[key].destructor = NULL;
242 	}
243 	THR_LOCK_RELEASE(curthread, &_keytable_lock);
244 }
245