thr_spec.c revision 118984
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. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by John Birrell.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $FreeBSD: head/lib/libkse/thread/thr_spec.c 118984 2003-08-16 05:19:00Z davidxu $
33 */
34#include <signal.h>
35#include <stdlib.h>
36#include <string.h>
37#include <errno.h>
38#include <pthread.h>
39#include "thr_private.h"
40
41struct pthread_key {
42	volatile int	allocated;
43	volatile int	count;
44	int		seqno;
45	void            (*destructor) ();
46};
47
48/* Static variables: */
49static struct pthread_key key_table[PTHREAD_KEYS_MAX];
50
51__weak_reference(_pthread_key_create, pthread_key_create);
52__weak_reference(_pthread_key_delete, pthread_key_delete);
53__weak_reference(_pthread_getspecific, pthread_getspecific);
54__weak_reference(_pthread_setspecific, pthread_setspecific);
55
56
57int
58_pthread_key_create(pthread_key_t *key, void (*destructor) (void *))
59{
60	struct pthread *curthread = _get_curthread();
61	int i;
62
63	/* Lock the key table: */
64	THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
65	for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
66
67		if (key_table[i].allocated == 0) {
68			key_table[i].allocated = 1;
69			key_table[i].destructor = destructor;
70			key_table[i].seqno++;
71
72			/* Unlock the key table: */
73			THR_LOCK_RELEASE(curthread, &_keytable_lock);
74			*key = i;
75			return (0);
76		}
77
78	}
79	/* Unlock the key table: */
80	THR_LOCK_RELEASE(curthread, &_keytable_lock);
81	return (EAGAIN);
82}
83
84int
85_pthread_key_delete(pthread_key_t key)
86{
87	struct pthread *curthread = _get_curthread();
88	int ret = 0;
89
90	if ((unsigned int)key < PTHREAD_KEYS_MAX) {
91		/* Lock the key table: */
92		THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
93
94		if (key_table[key].allocated)
95			key_table[key].allocated = 0;
96		else
97			ret = EINVAL;
98
99		/* Unlock the key table: */
100		THR_LOCK_RELEASE(curthread, &_keytable_lock);
101	} else
102		ret = EINVAL;
103	return (ret);
104}
105
106void
107_thread_cleanupspecific(void)
108{
109	struct pthread	*curthread = _get_curthread();
110	void		*data = NULL;
111	int		key;
112	void		(*destructor)( void *);
113
114	if (curthread->specific != NULL) {
115		/* Lock the key table: */
116		THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
117		for (key = 0; (key < PTHREAD_KEYS_MAX) &&
118		    (curthread->specific_data_count > 0); key++) {
119			destructor = NULL;
120
121			if (key_table[key].allocated &&
122			    (curthread->specific[key].data != NULL)) {
123				if (curthread->specific[key].seqno ==
124				    key_table[key].seqno) {
125					data = (void *)curthread->specific[key].data;
126					destructor = key_table[key].destructor;
127				}
128				curthread->specific[key].data = NULL;
129				curthread->specific_data_count--;
130			}
131
132			/*
133			 * If there is a destructore, call it
134			 * with the key table entry unlocked:
135			 */
136			if (destructor != NULL) {
137				/*
138				 * Don't hold the lock while calling the
139				 * destructor:
140				 */
141				THR_LOCK_RELEASE(curthread, &_keytable_lock);
142				destructor(data);
143				THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
144			}
145		}
146		THR_LOCK_RELEASE(curthread, &_keytable_lock);
147		free(curthread->specific);
148		curthread->specific = NULL;
149	}
150}
151
152static inline struct pthread_specific_elem *
153pthread_key_allocate_data(void)
154{
155	struct pthread_specific_elem *new_data;
156
157	new_data = (struct pthread_specific_elem *)
158	    malloc(sizeof(struct pthread_specific_elem) * PTHREAD_KEYS_MAX);
159	if (new_data != NULL) {
160		memset((void *) new_data, 0,
161		    sizeof(struct pthread_specific_elem) * PTHREAD_KEYS_MAX);
162	}
163	return (new_data);
164}
165
166int
167_pthread_setspecific(pthread_key_t key, const void *value)
168{
169	struct pthread	*pthread;
170	int		ret = 0;
171
172	/* Point to the running thread: */
173	pthread = _get_curthread();
174
175	if ((pthread->specific) ||
176	    (pthread->specific = pthread_key_allocate_data())) {
177		if ((unsigned int)key < PTHREAD_KEYS_MAX) {
178			if (key_table[key].allocated) {
179				if (pthread->specific[key].data == NULL) {
180					if (value != NULL)
181						pthread->specific_data_count++;
182				} else {
183					if (value == NULL)
184						pthread->specific_data_count--;
185				}
186				pthread->specific[key].data = value;
187				pthread->specific[key].seqno =
188				    key_table[key].seqno;
189				ret = 0;
190			} else
191				ret = EINVAL;
192		} else
193			ret = EINVAL;
194	} else
195		ret = ENOMEM;
196	return (ret);
197}
198
199void *
200_pthread_getspecific(pthread_key_t key)
201{
202	struct pthread	*pthread;
203	void		*data;
204
205	/* Point to the running thread: */
206	pthread = _get_curthread();
207
208	/* Check if there is specific data: */
209	if (pthread->specific != NULL && (unsigned int)key < PTHREAD_KEYS_MAX) {
210		/* Check if this key has been used before: */
211		if (key_table[key].allocated &&
212		    (pthread->specific[key].seqno == key_table[key].seqno)) {
213			/* Return the value: */
214			data = (void *) 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	} else
223		/* No specific data has been created, so just return NULL: */
224		data = NULL;
225	return (data);
226}
227