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"
33112918Sjeff#include <signal.h>
34112918Sjeff#include <stdlib.h>
35112918Sjeff#include <string.h>
36112918Sjeff#include <errno.h>
37112918Sjeff#include <pthread.h>
38157457Sdavidxu#include "un-namespace.h"
39211860Sdavidxu#include "libc_private.h"
40144518Sdavidxu
41112918Sjeff#include "thr_private.h"
42112918Sjeff
43112918Sjeff/* Static variables: */
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
53144518Sdavidxu_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	/* Lock the key table: */
63144518Sdavidxu	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
64144518Sdavidxu	for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
65112918Sjeff
66144518Sdavidxu		if (_thread_keytable[i].allocated == 0) {
67144518Sdavidxu			_thread_keytable[i].allocated = 1;
68144518Sdavidxu			_thread_keytable[i].destructor = destructor;
69144518Sdavidxu			_thread_keytable[i].seqno++;
70144518Sdavidxu
71144518Sdavidxu			/* Unlock the key table: */
72144518Sdavidxu			THR_LOCK_RELEASE(curthread, &_keytable_lock);
73262221Sjhb			*key = i + 1;
74112918Sjeff			return (0);
75112918Sjeff		}
76112918Sjeff
77112918Sjeff	}
78144518Sdavidxu	/* Unlock the key table: */
79144518Sdavidxu	THR_LOCK_RELEASE(curthread, &_keytable_lock);
80112918Sjeff	return (EAGAIN);
81112918Sjeff}
82112918Sjeff
83112918Sjeffint
84262221Sjhb_pthread_key_delete(pthread_key_t userkey)
85112918Sjeff{
86144518Sdavidxu	struct pthread *curthread = _get_curthread();
87262221Sjhb	int key = userkey - 1;
88112918Sjeff	int ret = 0;
89112918Sjeff
90144518Sdavidxu	if ((unsigned int)key < PTHREAD_KEYS_MAX) {
91144518Sdavidxu		/* Lock the key table: */
92144518Sdavidxu		THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
93112918Sjeff
94144518Sdavidxu		if (_thread_keytable[key].allocated)
95144518Sdavidxu			_thread_keytable[key].allocated = 0;
96112918Sjeff		else
97112918Sjeff			ret = EINVAL;
98112918Sjeff
99144518Sdavidxu		/* Unlock the key table: */
100144518Sdavidxu		THR_LOCK_RELEASE(curthread, &_keytable_lock);
101112918Sjeff	} else
102112918Sjeff		ret = EINVAL;
103112918Sjeff	return (ret);
104112918Sjeff}
105112918Sjeff
106112918Sjeffvoid
107112918Sjeff_thread_cleanupspecific(void)
108112918Sjeff{
109144518Sdavidxu	struct pthread	*curthread = _get_curthread();
110144518Sdavidxu	void		(*destructor)( void *);
111157457Sdavidxu	const void	*data = NULL;
112112918Sjeff	int		key;
113144518Sdavidxu	int		i;
114112918Sjeff
115144518Sdavidxu	if (curthread->specific == NULL)
116144518Sdavidxu		return;
117112918Sjeff
118144518Sdavidxu	/* Lock the key table: */
119144518Sdavidxu	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
120144518Sdavidxu	for (i = 0; (i < PTHREAD_DESTRUCTOR_ITERATIONS) &&
121144518Sdavidxu	    (curthread->specific_data_count > 0); i++) {
122144518Sdavidxu		for (key = 0; (key < PTHREAD_KEYS_MAX) &&
123144518Sdavidxu		    (curthread->specific_data_count > 0); key++) {
124144518Sdavidxu			destructor = NULL;
125144518Sdavidxu
126144518Sdavidxu			if (_thread_keytable[key].allocated &&
127144518Sdavidxu			    (curthread->specific[key].data != NULL)) {
128144518Sdavidxu				if (curthread->specific[key].seqno ==
129144518Sdavidxu				    _thread_keytable[key].seqno) {
130157457Sdavidxu					data = curthread->specific[key].data;
131144518Sdavidxu					destructor = _thread_keytable[key].destructor;
132112918Sjeff				}
133144518Sdavidxu				curthread->specific[key].data = NULL;
134144518Sdavidxu				curthread->specific_data_count--;
135144518Sdavidxu			}
136197477Sdavidxu			else if (curthread->specific[key].data != NULL) {
137197477Sdavidxu				/*
138197477Sdavidxu				 * This can happen if the key is deleted via
139197477Sdavidxu				 * pthread_key_delete without first setting the value
140197477Sdavidxu				 * to NULL in all threads.  POSIX says that the
141197477Sdavidxu				 * destructor is not invoked in this case.
142197477Sdavidxu				 */
143197477Sdavidxu				curthread->specific[key].data = NULL;
144197477Sdavidxu				curthread->specific_data_count--;
145197477Sdavidxu			}
146112918Sjeff
147144518Sdavidxu			/*
148197477Sdavidxu			 * If there is a destructor, call it
149144518Sdavidxu			 * with the key table entry unlocked:
150144518Sdavidxu			 */
151144518Sdavidxu			if (destructor != NULL) {
152112918Sjeff				/*
153144518Sdavidxu				 * Don't hold the lock while calling the
154144518Sdavidxu				 * destructor:
155112918Sjeff				 */
156144518Sdavidxu				THR_LOCK_RELEASE(curthread, &_keytable_lock);
157157457Sdavidxu				destructor(__DECONST(void *, data));
158144518Sdavidxu				THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
159112918Sjeff			}
160112918Sjeff		}
161112918Sjeff	}
162144518Sdavidxu	THR_LOCK_RELEASE(curthread, &_keytable_lock);
163144518Sdavidxu	free(curthread->specific);
164144518Sdavidxu	curthread->specific = NULL;
165144518Sdavidxu	if (curthread->specific_data_count > 0)
166144518Sdavidxu		stderr_debug("Thread %p has exited with leftover "
167144518Sdavidxu		    "thread-specific data after %d destructor iterations\n",
168144518Sdavidxu		    curthread, PTHREAD_DESTRUCTOR_ITERATIONS);
169112918Sjeff}
170112918Sjeff
171112918Sjeffstatic inline struct pthread_specific_elem *
172112918Sjeffpthread_key_allocate_data(void)
173112918Sjeff{
174112918Sjeff	struct pthread_specific_elem *new_data;
175112918Sjeff
176112918Sjeff	new_data = (struct pthread_specific_elem *)
177159090Sdelphij	    calloc(1, sizeof(struct pthread_specific_elem) * PTHREAD_KEYS_MAX);
178112918Sjeff	return (new_data);
179112918Sjeff}
180112918Sjeff
181112918Sjeffint
182262221Sjhb_pthread_setspecific(pthread_key_t userkey, const void *value)
183112918Sjeff{
184144518Sdavidxu	struct pthread	*pthread;
185262221Sjhb	pthread_key_t	key = userkey - 1;
186112918Sjeff	int		ret = 0;
187112918Sjeff
188144518Sdavidxu	/* Point to the running thread: */
189144518Sdavidxu	pthread = _get_curthread();
190144518Sdavidxu
191112918Sjeff	if ((pthread->specific) ||
192112918Sjeff	    (pthread->specific = pthread_key_allocate_data())) {
193144518Sdavidxu		if ((unsigned int)key < PTHREAD_KEYS_MAX) {
194144518Sdavidxu			if (_thread_keytable[key].allocated) {
195112918Sjeff				if (pthread->specific[key].data == NULL) {
196112918Sjeff					if (value != NULL)
197112918Sjeff						pthread->specific_data_count++;
198144518Sdavidxu				} else if (value == NULL)
199144518Sdavidxu					pthread->specific_data_count--;
200112918Sjeff				pthread->specific[key].data = value;
201112918Sjeff				pthread->specific[key].seqno =
202144518Sdavidxu				    _thread_keytable[key].seqno;
203112918Sjeff				ret = 0;
204112918Sjeff			} else
205112918Sjeff				ret = EINVAL;
206112918Sjeff		} else
207112918Sjeff			ret = EINVAL;
208112918Sjeff	} else
209112918Sjeff		ret = ENOMEM;
210112918Sjeff	return (ret);
211112918Sjeff}
212112918Sjeff
213112918Sjeffvoid *
214262221Sjhb_pthread_getspecific(pthread_key_t userkey)
215112918Sjeff{
216144518Sdavidxu	struct pthread	*pthread;
217262221Sjhb	pthread_key_t	key = userkey - 1;
218157457Sdavidxu	const void	*data;
219112918Sjeff
220144518Sdavidxu	/* Point to the running thread: */
221144518Sdavidxu	pthread = _get_curthread();
222144518Sdavidxu
223112918Sjeff	/* Check if there is specific data: */
224144518Sdavidxu	if (pthread->specific != NULL && (unsigned int)key < PTHREAD_KEYS_MAX) {
225112918Sjeff		/* Check if this key has been used before: */
226144518Sdavidxu		if (_thread_keytable[key].allocated &&
227144518Sdavidxu		    (pthread->specific[key].seqno == _thread_keytable[key].seqno)) {
228112918Sjeff			/* Return the value: */
229157457Sdavidxu			data = pthread->specific[key].data;
230112918Sjeff		} else {
231112918Sjeff			/*
232112918Sjeff			 * This key has not been used before, so return NULL
233112918Sjeff			 * instead:
234112918Sjeff			 */
235112918Sjeff			data = NULL;
236112918Sjeff		}
237112918Sjeff	} else
238112918Sjeff		/* No specific data has been created, so just return NULL: */
239112918Sjeff		data = NULL;
240157457Sdavidxu	return (__DECONST(void *, data));
241112918Sjeff}
242211860Sdavidxu
243211860Sdavidxuvoid
244211860Sdavidxu_thr_tsd_unload(struct dl_phdr_info *phdr_info)
245211860Sdavidxu{
246211860Sdavidxu	struct pthread *curthread = _get_curthread();
247211860Sdavidxu	void (*destructor)(void *);
248211860Sdavidxu	int key;
249211860Sdavidxu
250211860Sdavidxu	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
251211860Sdavidxu	for (key = 0; key < PTHREAD_KEYS_MAX; key++) {
252211860Sdavidxu		if (_thread_keytable[key].allocated) {
253211860Sdavidxu			destructor = _thread_keytable[key].destructor;
254211860Sdavidxu			if (destructor != NULL) {
255211860Sdavidxu				if (__elf_phdr_match_addr(phdr_info, destructor))
256211860Sdavidxu					_thread_keytable[key].destructor = NULL;
257211860Sdavidxu			}
258211860Sdavidxu		}
259211860Sdavidxu	}
260211860Sdavidxu	THR_LOCK_RELEASE(curthread, &_keytable_lock);
261211860Sdavidxu}
262