]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - lib/libkse/thread/thr_spec.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / lib / libkse / thread / thr_spec.c
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  * $FreeBSD$
30  */
31 #include <signal.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <pthread.h>
36
37 #include "thr_private.h"
38
39
40 struct pthread_key _thread_keytable[PTHREAD_KEYS_MAX];
41
42 /*
43  * XXX - This breaks the linker if LT10_COMPAT_DEFAULT doesn't
44  * also include a weak reference to the default symbol.
45  */
46 LT10_COMPAT_PRIVATE(_thread_keytable);
47
48 LT10_COMPAT_PRIVATE(_pthread_key_create);
49 LT10_COMPAT_DEFAULT(pthread_key_create);
50 LT10_COMPAT_PRIVATE(_pthread_key_delete);
51 LT10_COMPAT_DEFAULT(pthread_key_delete);
52 LT10_COMPAT_PRIVATE(_pthread_getspecific);
53 LT10_COMPAT_DEFAULT(pthread_getspecific);
54 LT10_COMPAT_PRIVATE(_pthread_setspecific);
55 LT10_COMPAT_DEFAULT(pthread_setspecific);
56
57 __weak_reference(_pthread_key_create, pthread_key_create);
58 __weak_reference(_pthread_key_delete, pthread_key_delete);
59 __weak_reference(_pthread_getspecific, pthread_getspecific);
60 __weak_reference(_pthread_setspecific, pthread_setspecific);
61
62
63 int
64 _pthread_key_create(pthread_key_t *key, void (*destructor) (void *))
65 {
66         struct pthread *curthread;
67         int i;
68
69         if (_thr_initial == NULL)
70                 _libpthread_init(NULL);
71         curthread = _get_curthread();
72
73         /* Lock the key table: */
74         THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
75         for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
76
77                 if (_thread_keytable[i].allocated == 0) {
78                         _thread_keytable[i].allocated = 1;
79                         _thread_keytable[i].destructor = destructor;
80                         _thread_keytable[i].seqno++;
81
82                         /* Unlock the key table: */
83                         THR_LOCK_RELEASE(curthread, &_keytable_lock);
84                         *key = i;
85                         return (0);
86                 }
87
88         }
89         /* Unlock the key table: */
90         THR_LOCK_RELEASE(curthread, &_keytable_lock);
91         return (EAGAIN);
92 }
93
94 int
95 _pthread_key_delete(pthread_key_t key)
96 {
97         struct pthread *curthread = _get_curthread();
98         int ret = 0;
99
100         if ((unsigned int)key < PTHREAD_KEYS_MAX) {
101                 /* Lock the key table: */
102                 THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
103
104                 if (_thread_keytable[key].allocated)
105                         _thread_keytable[key].allocated = 0;
106                 else
107                         ret = EINVAL;
108
109                 /* Unlock the key table: */
110                 THR_LOCK_RELEASE(curthread, &_keytable_lock);
111         } else
112                 ret = EINVAL;
113         return (ret);
114 }
115
116 void 
117 _thread_cleanupspecific(void)
118 {
119         struct pthread  *curthread = _get_curthread();
120         void            (*destructor)( void *);
121         void            *data = NULL;
122         int             key;
123         int             i;
124
125         if (curthread->specific == NULL)
126                 return;
127
128         /* Lock the key table: */
129         THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
130         for (i = 0; (i < PTHREAD_DESTRUCTOR_ITERATIONS) &&
131             (curthread->specific_data_count > 0); i++) {
132                 for (key = 0; (key < PTHREAD_KEYS_MAX) &&
133                     (curthread->specific_data_count > 0); key++) {
134                         destructor = NULL;
135
136                         if (_thread_keytable[key].allocated &&
137                             (curthread->specific[key].data != NULL)) {
138                                 if (curthread->specific[key].seqno ==
139                                     _thread_keytable[key].seqno) {
140                                         data = (void *)
141                                             curthread->specific[key].data;
142                                         destructor = _thread_keytable[key].destructor;
143                                 }
144                                 curthread->specific[key].data = NULL;
145                                 curthread->specific_data_count--;
146                         }
147
148                         /*
149                          * If there is a destructore, call it
150                          * with the key table entry unlocked:
151                          */
152                         if (destructor != NULL) {
153                                 /*
154                                  * Don't hold the lock while calling the
155                                  * destructor:
156                                  */
157                                 THR_LOCK_RELEASE(curthread, &_keytable_lock);
158                                 destructor(data);
159                                 THR_LOCK_ACQUIRE(curthread, &_keytable_lock);
160                         }
161                 }
162         }
163         THR_LOCK_RELEASE(curthread, &_keytable_lock);
164         free(curthread->specific);
165         curthread->specific = NULL;
166         if (curthread->specific_data_count > 0)
167                 stderr_debug("Thread %p has exited with leftover "
168                     "thread-specific data after %d destructor iterations\n",
169                     curthread, PTHREAD_DESTRUCTOR_ITERATIONS);
170 }
171
172 static inline struct pthread_specific_elem *
173 pthread_key_allocate_data(void)
174 {
175         struct pthread_specific_elem *new_data;
176
177         new_data = (struct pthread_specific_elem *)
178             malloc(sizeof(struct pthread_specific_elem) * PTHREAD_KEYS_MAX);
179         if (new_data != NULL) {
180                 memset((void *) new_data, 0,
181                     sizeof(struct pthread_specific_elem) * PTHREAD_KEYS_MAX);
182         }
183         return (new_data);
184 }
185
186 int 
187 _pthread_setspecific(pthread_key_t key, const void *value)
188 {
189         struct pthread  *pthread;
190         int             ret = 0;
191
192         /* Point to the running thread: */
193         pthread = _get_curthread();
194
195         if ((pthread->specific) ||
196             (pthread->specific = pthread_key_allocate_data())) {
197                 if ((unsigned int)key < PTHREAD_KEYS_MAX) {
198                         if (_thread_keytable[key].allocated) {
199                                 if (pthread->specific[key].data == NULL) {
200                                         if (value != NULL)
201                                                 pthread->specific_data_count++;
202                                 } else if (value == NULL)
203                                         pthread->specific_data_count--;
204                                 pthread->specific[key].data = value;
205                                 pthread->specific[key].seqno =
206                                     _thread_keytable[key].seqno;
207                                 ret = 0;
208                         } else
209                                 ret = EINVAL;
210                 } else
211                         ret = EINVAL;
212         } else
213                 ret = ENOMEM;
214         return (ret);
215 }
216
217 void *
218 _pthread_getspecific(pthread_key_t key)
219 {
220         struct pthread  *pthread;
221         void            *data;
222
223         /* Point to the running thread: */
224         pthread = _get_curthread();
225
226         /* Check if there is specific data: */
227         if (pthread->specific != NULL && (unsigned int)key < PTHREAD_KEYS_MAX) {
228                 /* Check if this key has been used before: */
229                 if (_thread_keytable[key].allocated &&
230                     (pthread->specific[key].seqno == _thread_keytable[key].seqno)) {
231                         /* Return the value: */
232                         data = (void *) pthread->specific[key].data;
233                 } else {
234                         /*
235                          * This key has not been used before, so return NULL
236                          * instead: 
237                          */
238                         data = NULL;
239                 }
240         } else
241                 /* No specific data has been created, so just return NULL: */
242                 data = NULL;
243         return (data);
244 }