]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - 6/lib/libthr/thread/thr_list.c
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.git] / 6 / lib / libthr / thread / thr_list.c
1 /*
2  * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
3  * Copyright (C) 2003 Daniel M. Eischen <deischen@freebsd.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice unmodified, this list of conditions, and the following
11  *    disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  */
29
30 #include <sys/types.h>
31 #include <sys/queue.h>
32
33 #include <stdlib.h>
34 #include <string.h>
35 #include <pthread.h>
36
37 #include "thr_private.h"
38 #include "libc_private.h"
39
40 /*#define DEBUG_THREAD_LIST */
41 #ifdef DEBUG_THREAD_LIST
42 #define DBG_MSG         stdout_debug
43 #else
44 #define DBG_MSG(x...)
45 #endif
46
47 /*
48  * Define a high water mark for the maximum number of threads that
49  * will be cached.  Once this level is reached, any extra threads
50  * will be free()'d.
51  */
52 #define MAX_CACHED_THREADS      100
53
54 /*
55  * We've got to keep track of everything that is allocated, not only
56  * to have a speedy free list, but also so they can be deallocated
57  * after a fork().
58  */
59 static TAILQ_HEAD(, pthread)    free_threadq;
60 static umtx_t                   free_thread_lock;
61 static umtx_t                   tcb_lock;
62 static int                      free_thread_count = 0;
63 static int                      inited = 0;
64
65 LIST_HEAD(thread_hash_head, pthread);
66 #define HASH_QUEUES     128
67 static struct thread_hash_head  thr_hashtable[HASH_QUEUES];
68 #define THREAD_HASH(thrd)       (((unsigned long)thrd >> 8) % HASH_QUEUES)
69
70 static void thr_destroy(struct pthread *curthread, struct pthread *thread);
71
72 void
73 _thr_list_init(void)
74 {
75         int i;
76
77         _gc_count = 0;
78         _thr_umtx_init(&_thr_list_lock);
79         TAILQ_INIT(&_thread_list);
80         TAILQ_INIT(&free_threadq);
81         _thr_umtx_init(&free_thread_lock);
82         _thr_umtx_init(&tcb_lock);
83         if (inited) {
84                 for (i = 0; i < HASH_QUEUES; ++i)
85                         LIST_INIT(&thr_hashtable[i]);
86         }
87         inited = 1;
88 }
89
90 void
91 _thr_gc(struct pthread *curthread)
92 {
93         struct pthread *td, *td_next;
94         TAILQ_HEAD(, pthread) worklist;
95
96         TAILQ_INIT(&worklist);
97         THREAD_LIST_LOCK(curthread);
98
99         /* Check the threads waiting for GC. */
100         for (td = TAILQ_FIRST(&_thread_gc_list); td != NULL; td = td_next) {
101                 td_next = TAILQ_NEXT(td, gcle);
102                 if (td->tid != TID_TERMINATED) {
103                         /* make sure we are not still in userland */
104                         continue;
105                 }
106                 _thr_stack_free(&td->attr);
107                 if (((td->tlflags & TLFLAGS_DETACHED) != 0) &&
108                     (td->refcount == 0)) {
109                         THR_GCLIST_REMOVE(td);
110                         /*
111                          * The thread has detached and is no longer
112                          * referenced.  It is safe to remove all
113                          * remnants of the thread.
114                          */
115                         THR_LIST_REMOVE(td);
116                         TAILQ_INSERT_HEAD(&worklist, td, gcle);
117                 }
118         }
119         THREAD_LIST_UNLOCK(curthread);
120
121         while ((td = TAILQ_FIRST(&worklist)) != NULL) {
122                 TAILQ_REMOVE(&worklist, td, gcle);
123                 /*
124                  * XXX we don't free initial thread, because there might
125                  * have some code referencing initial thread.
126                  */
127                 if (td == _thr_initial) {
128                         DBG_MSG("Initial thread won't be freed\n");
129                         continue;
130                 }
131
132                 _thr_free(curthread, td);
133         }
134 }
135
136 struct pthread *
137 _thr_alloc(struct pthread *curthread)
138 {
139         struct pthread  *thread = NULL;
140         struct tcb      *tcb;
141
142         if (curthread != NULL) {
143                 if (GC_NEEDED())
144                         _thr_gc(curthread);
145                 if (free_thread_count > 0) {
146                         THR_LOCK_ACQUIRE(curthread, &free_thread_lock);
147                         if ((thread = TAILQ_FIRST(&free_threadq)) != NULL) {
148                                 TAILQ_REMOVE(&free_threadq, thread, tle);
149                                 free_thread_count--;
150                         }
151                         THR_LOCK_RELEASE(curthread, &free_thread_lock);
152                 }
153         }
154         if (thread == NULL) {
155                 thread = malloc(sizeof(struct pthread));
156                 if (thread == NULL)
157                         return (NULL);
158         }
159         if (curthread != NULL) {
160                 THR_LOCK_ACQUIRE(curthread, &tcb_lock);
161                 tcb = _tcb_ctor(thread, 0 /* not initial tls */);
162                 THR_LOCK_RELEASE(curthread, &tcb_lock);
163         } else {
164                 tcb = _tcb_ctor(thread, 1 /* initial tls */);
165         }
166         if (tcb != NULL) {
167                 memset(thread, 0, sizeof(*thread));
168                 thread->tcb = tcb;
169         } else {
170                 thr_destroy(curthread, thread);
171                 thread = NULL;
172         }
173         return (thread);
174 }
175
176 void
177 _thr_free(struct pthread *curthread, struct pthread *thread)
178 {
179         DBG_MSG("Freeing thread %p\n", thread);
180         if (thread->name) {
181                 free(thread->name);
182                 thread->name = NULL;
183         }
184         /*
185          * Always free tcb, as we only know it is part of RTLD TLS
186          * block, but don't know its detail and can not assume how
187          * it works, so better to avoid caching it here.
188          */
189         if (curthread != NULL) {
190                 THR_LOCK_ACQUIRE(curthread, &tcb_lock);
191                 _tcb_dtor(thread->tcb);
192                 THR_LOCK_RELEASE(curthread, &tcb_lock);
193         } else {
194                 _tcb_dtor(thread->tcb);
195         }
196         thread->tcb = NULL;
197         if ((curthread == NULL) || (free_thread_count >= MAX_CACHED_THREADS)) {
198                 thr_destroy(curthread, thread);
199         } else {
200                 /*
201                  * Add the thread to the free thread list, this also avoids
202                  * pthread id is reused too quickly, may help some buggy apps.
203                  */
204                 THR_LOCK_ACQUIRE(curthread, &free_thread_lock);
205                 TAILQ_INSERT_TAIL(&free_threadq, thread, tle);
206                 free_thread_count++;
207                 THR_LOCK_RELEASE(curthread, &free_thread_lock);
208         }
209 }
210
211 static void
212 thr_destroy(struct pthread *curthread __unused, struct pthread *thread)
213 {
214         free(thread);
215 }
216
217 /*
218  * Add the thread to the list of all threads and increment
219  * number of active threads.
220  */
221 void
222 _thr_link(struct pthread *curthread, struct pthread *thread)
223 {
224         THREAD_LIST_LOCK(curthread);
225         THR_LIST_ADD(thread);
226         _thread_active_threads++;
227         THREAD_LIST_UNLOCK(curthread);
228 }
229
230 /*
231  * Remove an active thread.
232  */
233 void
234 _thr_unlink(struct pthread *curthread, struct pthread *thread)
235 {
236         THREAD_LIST_LOCK(curthread);
237         THR_LIST_REMOVE(thread);
238         _thread_active_threads--;
239         THREAD_LIST_UNLOCK(curthread);
240 }
241
242 void
243 _thr_hash_add(struct pthread *thread)
244 {
245         struct thread_hash_head *head;
246
247         head = &thr_hashtable[THREAD_HASH(thread)];
248         LIST_INSERT_HEAD(head, thread, hle);
249 }
250
251 void
252 _thr_hash_remove(struct pthread *thread)
253 {
254         LIST_REMOVE(thread, hle);
255 }
256
257 struct pthread *
258 _thr_hash_find(struct pthread *thread)
259 {
260         struct pthread *td;
261         struct thread_hash_head *head;
262
263         head = &thr_hashtable[THREAD_HASH(thread)];
264         LIST_FOREACH(td, head, hle) {
265                 if (td == thread)
266                         return (thread);
267         }
268         return (NULL);
269 }
270
271 /*
272  * Find a thread in the linked list of active threads and add a reference
273  * to it.  Threads with positive reference counts will not be deallocated
274  * until all references are released.
275  */
276 int
277 _thr_ref_add(struct pthread *curthread, struct pthread *thread,
278     int include_dead)
279 {
280         int ret;
281
282         if (thread == NULL)
283                 /* Invalid thread: */
284                 return (EINVAL);
285
286         THREAD_LIST_LOCK(curthread);
287         if ((ret = _thr_find_thread(curthread, thread, include_dead)) == 0) {
288                 thread->refcount++;
289         }
290         THREAD_LIST_UNLOCK(curthread);
291
292         /* Return zero if the thread exists: */
293         return (ret);
294 }
295
296 void
297 _thr_ref_delete(struct pthread *curthread, struct pthread *thread)
298 {
299         THREAD_LIST_LOCK(curthread);
300         _thr_ref_delete_unlocked(curthread, thread);
301         THREAD_LIST_UNLOCK(curthread);
302 }
303
304 void
305 _thr_ref_delete_unlocked(struct pthread *curthread, struct pthread *thread)
306 {
307         if (thread != NULL) {
308                 thread->refcount--;
309                 if ((thread->refcount == 0) && thread->state == PS_DEAD &&
310                     (thread->tlflags & TLFLAGS_DETACHED) != 0)
311                         THR_GCLIST_ADD(thread);
312         }
313 }
314
315 int
316 _thr_find_thread(struct pthread *curthread, struct pthread *thread,
317     int include_dead)
318 {
319         struct pthread *pthread;
320
321         if (thread == NULL)
322                 /* Invalid thread: */
323                 return (EINVAL);
324
325         pthread = _thr_hash_find(thread);
326         if (pthread) {
327                 if (include_dead == 0 && pthread->state == PS_DEAD) {
328                         pthread = NULL;
329                 }       
330         }
331
332         /* Return zero if the thread exists: */
333         return ((pthread != NULL) ? 0 : ESRCH);
334 }