1112918Sjeff/*
2112918Sjeff * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>.
3112918Sjeff * All rights reserved.
4112918Sjeff *
5112918Sjeff * Redistribution and use in source and binary forms, with or without
6112918Sjeff * modification, are permitted provided that the following conditions
7112918Sjeff * are met:
8112918Sjeff * 1. Redistributions of source code must retain the above copyright
9112918Sjeff *    notice, this list of conditions and the following disclaimer.
10112918Sjeff * 2. Redistributions in binary form must reproduce the above copyright
11112918Sjeff *    notice, this list of conditions and the following disclaimer in the
12112918Sjeff *    documentation and/or other materials provided with the distribution.
13165967Simp * 3. Neither the name of the author nor the names of any co-contributors
14112918Sjeff *    may be used to endorse or promote products derived from this software
15112918Sjeff *    without specific prior written permission.
16112918Sjeff *
17112918Sjeff * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
18112918Sjeff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19112918Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20112918Sjeff * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21112918Sjeff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22112918Sjeff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23112918Sjeff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24112918Sjeff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25112918Sjeff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26112918Sjeff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27112918Sjeff * SUCH DAMAGE.
28112918Sjeff *
29112918Sjeff * $FreeBSD$
30112918Sjeff */
31144518Sdavidxu
32157457Sdavidxu#include "namespace.h"
33283690Skib#include <sys/mman.h>
34112918Sjeff#include <signal.h>
35112918Sjeff#include <stdlib.h>
36112918Sjeff#include <string.h>
37112918Sjeff#include <errno.h>
38112918Sjeff#include <pthread.h>
39157457Sdavidxu#include "un-namespace.h"
40211860Sdavidxu#include "libc_private.h"
41144518Sdavidxu
42112918Sjeff#include "thr_private.h"
43112918Sjeff
44144518Sdavidxustruct pthread_key _thread_keytable[PTHREAD_KEYS_MAX];
45112918Sjeff
46112918Sjeff__weak_reference(_pthread_key_create, pthread_key_create);
47112918Sjeff__weak_reference(_pthread_key_delete, pthread_key_delete);
48112918Sjeff__weak_reference(_pthread_getspecific, pthread_getspecific);
49112918Sjeff__weak_reference(_pthread_setspecific, pthread_setspecific);
50112918Sjeff
51112918Sjeff
52112918Sjeffint
53283690Skib_pthread_key_create(pthread_key_t *key, void (*destructor)(void *))
54112918Sjeff{
55173394Smarius	struct pthread *curthread;
56144518Sdavidxu	int i;
57112918Sjeff
58173394Smarius	_thr_check_init();
59173394Smarius
60173394Smarius	curthread = _get_curthread();
61173394Smarius
62144518Sdavidxu	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
63144518Sdavidxu	for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
64112918Sjeff
65144518Sdavidxu		if (_thread_keytable[i].allocated == 0) {
66144518Sdavidxu			_thread_keytable[i].allocated = 1;
67144518Sdavidxu			_thread_keytable[i].destructor = destructor;
68144518Sdavidxu			_thread_keytable[i].seqno++;
69144518Sdavidxu
70144518Sdavidxu			THR_LOCK_RELEASE(curthread, &_keytable_lock);
71250691Sdavidxu			*key = i + 1;
72112918Sjeff			return (0);
73112918Sjeff		}
74112918Sjeff
75112918Sjeff	}
76144518Sdavidxu	THR_LOCK_RELEASE(curthread, &_keytable_lock);
77112918Sjeff	return (EAGAIN);
78112918Sjeff}
79112918Sjeff
80112918Sjeffint
81250691Sdavidxu_pthread_key_delete(pthread_key_t userkey)
82112918Sjeff{
83283690Skib	struct pthread *curthread;
84283690Skib	int key, ret;
85112918Sjeff
86283690Skib	key = userkey - 1;
87283690Skib	if ((unsigned int)key >= PTHREAD_KEYS_MAX)
88283690Skib		return (EINVAL);
89283690Skib	curthread = _get_curthread();
90283690Skib	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
91283690Skib	if (_thread_keytable[key].allocated) {
92283690Skib		_thread_keytable[key].allocated = 0;
93283690Skib		ret = 0;
94283690Skib	} else {
95112918Sjeff		ret = EINVAL;
96283690Skib	}
97283690Skib	THR_LOCK_RELEASE(curthread, &_keytable_lock);
98112918Sjeff	return (ret);
99112918Sjeff}
100112918Sjeff
101112918Sjeffvoid
102112918Sjeff_thread_cleanupspecific(void)
103112918Sjeff{
104283690Skib	struct pthread *curthread;
105283690Skib	void (*destructor)(void *);
106283690Skib	const void *data;
107283690Skib	int i, key;
108112918Sjeff
109283690Skib	curthread = _get_curthread();
110144518Sdavidxu	if (curthread->specific == NULL)
111144518Sdavidxu		return;
112144518Sdavidxu	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
113283690Skib	for (i = 0; i < PTHREAD_DESTRUCTOR_ITERATIONS &&
114283690Skib	    curthread->specific_data_count > 0; i++) {
115283690Skib		for (key = 0; key < PTHREAD_KEYS_MAX &&
116283690Skib		    curthread->specific_data_count > 0; key++) {
117144518Sdavidxu			destructor = NULL;
118144518Sdavidxu
119144518Sdavidxu			if (_thread_keytable[key].allocated &&
120144518Sdavidxu			    (curthread->specific[key].data != NULL)) {
121144518Sdavidxu				if (curthread->specific[key].seqno ==
122144518Sdavidxu				    _thread_keytable[key].seqno) {
123157457Sdavidxu					data = curthread->specific[key].data;
124283690Skib					destructor = _thread_keytable[key].
125283690Skib					    destructor;
126112918Sjeff				}
127144518Sdavidxu				curthread->specific[key].data = NULL;
128144518Sdavidxu				curthread->specific_data_count--;
129283690Skib			} else if (curthread->specific[key].data != NULL) {
130197477Sdavidxu				/*
131283690Skib				 * This can happen if the key is
132283690Skib				 * deleted via pthread_key_delete
133283690Skib				 * without first setting the value to
134283690Skib				 * NULL in all threads.  POSIX says
135283690Skib				 * that the destructor is not invoked
136283690Skib				 * in this case.
137197477Sdavidxu				 */
138197477Sdavidxu				curthread->specific[key].data = NULL;
139197477Sdavidxu				curthread->specific_data_count--;
140197477Sdavidxu			}
141112918Sjeff
142144518Sdavidxu			/*
143283690Skib			 * If there is a destructor, call it with the
144283690Skib			 * key table entry unlocked.
145144518Sdavidxu			 */
146144518Sdavidxu			if (destructor != NULL) {
147144518Sdavidxu				THR_LOCK_RELEASE(curthread, &_keytable_lock);
148157457Sdavidxu				destructor(__DECONST(void *, data));
149144518Sdavidxu				THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
150112918Sjeff			}
151112918Sjeff		}
152112918Sjeff	}
153144518Sdavidxu	THR_LOCK_RELEASE(curthread, &_keytable_lock);
154283690Skib	munmap(curthread->specific, PTHREAD_KEYS_MAX * sizeof(struct
155283690Skib	    pthread_specific_elem));
156144518Sdavidxu	curthread->specific = NULL;
157283690Skib	if (curthread->specific_data_count > 0) {
158144518Sdavidxu		stderr_debug("Thread %p has exited with leftover "
159144518Sdavidxu		    "thread-specific data after %d destructor iterations\n",
160144518Sdavidxu		    curthread, PTHREAD_DESTRUCTOR_ITERATIONS);
161283690Skib	}
162112918Sjeff}
163112918Sjeff
164112918Sjeffint
165250691Sdavidxu_pthread_setspecific(pthread_key_t userkey, const void *value)
166112918Sjeff{
167283690Skib	struct pthread *pthread;
168283690Skib	void *tmp;
169283690Skib	pthread_key_t key;
170112918Sjeff
171283690Skib	key = userkey - 1;
172283690Skib	if ((unsigned int)key >= PTHREAD_KEYS_MAX ||
173283690Skib	    !_thread_keytable[key].allocated)
174283690Skib		return (EINVAL);
175283690Skib
176144518Sdavidxu	pthread = _get_curthread();
177283690Skib	if (pthread->specific == NULL) {
178283690Skib		tmp = mmap(NULL, PTHREAD_KEYS_MAX *
179283690Skib		    sizeof(struct pthread_specific_elem),
180283690Skib		    PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
181283690Skib		if (tmp == MAP_FAILED)
182283690Skib			return (ENOMEM);
183283690Skib		pthread->specific = tmp;
184283690Skib	}
185283690Skib	if (pthread->specific[key].data == NULL) {
186283690Skib		if (value != NULL)
187283690Skib			pthread->specific_data_count++;
188283690Skib	} else if (value == NULL)
189283690Skib		pthread->specific_data_count--;
190283690Skib	pthread->specific[key].data = value;
191283690Skib	pthread->specific[key].seqno = _thread_keytable[key].seqno;
192283690Skib	return (0);
193112918Sjeff}
194112918Sjeff
195112918Sjeffvoid *
196250691Sdavidxu_pthread_getspecific(pthread_key_t userkey)
197112918Sjeff{
198283690Skib	struct pthread *pthread;
199283690Skib	const void *data;
200283690Skib	pthread_key_t key;
201112918Sjeff
202283690Skib	/* Check if there is specific data. */
203283690Skib	key = userkey - 1;
204283690Skib	if ((unsigned int)key >= PTHREAD_KEYS_MAX)
205283690Skib		return (NULL);
206283690Skib
207144518Sdavidxu	pthread = _get_curthread();
208283690Skib	/* Check if this key has been used before. */
209283690Skib	if (_thread_keytable[key].allocated && pthread->specific != NULL &&
210283690Skib	    pthread->specific[key].seqno == _thread_keytable[key].seqno) {
211283690Skib		/* Return the value: */
212283690Skib		data = pthread->specific[key].data;
213283690Skib	} else {
214283690Skib		/*
215283690Skib		 * This key has not been used before, so return NULL
216283690Skib		 * instead.
217283690Skib		 */
218112918Sjeff		data = NULL;
219283690Skib	}
220157457Sdavidxu	return (__DECONST(void *, data));
221112918Sjeff}
222211860Sdavidxu
223211860Sdavidxuvoid
224211860Sdavidxu_thr_tsd_unload(struct dl_phdr_info *phdr_info)
225211860Sdavidxu{
226283690Skib	struct pthread *curthread;
227211860Sdavidxu	void (*destructor)(void *);
228211860Sdavidxu	int key;
229211860Sdavidxu
230283690Skib	curthread = _get_curthread();
231211860Sdavidxu	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
232211860Sdavidxu	for (key = 0; key < PTHREAD_KEYS_MAX; key++) {
233283690Skib		if (!_thread_keytable[key].allocated)
234283690Skib			continue;
235283690Skib		destructor = _thread_keytable[key].destructor;
236283690Skib		if (destructor == NULL)
237283690Skib			continue;
238283690Skib		if (__elf_phdr_match_addr(phdr_info, destructor))
239283690Skib			_thread_keytable[key].destructor = NULL;
240211860Sdavidxu	}
241211860Sdavidxu	THR_LOCK_RELEASE(curthread, &_keytable_lock);
242211860Sdavidxu}
243