Oops, disable debuging code.
[dragonfly.git] / lib / libthread_xu / thread / thr_list.c
CommitLineData
71b3fa15
DX
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 *
66bf1134 27 * $DragonFly: src/lib/libthread_xu/thread/thr_list.c,v 1.5 2006/03/12 12:25:57 davidxu Exp $
71b3fa15
DX
28 */
29
30#include <sys/cdefs.h>
31#include <sys/types.h>
32#include <sys/queue.h>
33
34#include <stdlib.h>
35#include <string.h>
36#include <pthread.h>
37
38#include "thr_private.h"
39#include "libc_private.h"
40
66bf1134 41/* #define DEBUG_THREAD_LIST */
71b3fa15
DX
42#ifdef DEBUG_THREAD_LIST
43#define DBG_MSG stdout_debug
44#else
45#define DBG_MSG(x...)
46#endif
47
48/*
49 * Define a high water mark for the maximum number of threads that
50 * will be cached. Once this level is reached, any extra threads
51 * will be free()'d.
52 */
53#define MAX_CACHED_THREADS 100
54
55/*
56 * We've got to keep track of everything that is allocated, not only
57 * to have a speedy free list, but also so they can be deallocated
58 * after a fork().
59 */
60static TAILQ_HEAD(, pthread) free_threadq;
61static umtx_t free_thread_lock;
62static umtx_t tcb_lock;
63static int free_thread_count = 0;
64static int inited = 0;
65static u_int64_t next_uniqueid = 1;
66
67LIST_HEAD(thread_hash_head, pthread);
68#define HASH_QUEUES 128
69static struct thread_hash_head thr_hashtable[HASH_QUEUES];
70#define THREAD_HASH(thrd) (((unsigned long)thrd >> 12) % HASH_QUEUES)
71
72static void thr_destroy(struct pthread *curthread, struct pthread *thread);
73
74void
75_thr_list_init(void)
76{
77 int i;
78
79 _gc_count = 0;
80 _thr_umtx_init(&_thr_list_lock);
81 TAILQ_INIT(&_thread_list);
82 TAILQ_INIT(&free_threadq);
83 _thr_umtx_init(&free_thread_lock);
84 _thr_umtx_init(&tcb_lock);
85 if (inited) {
86 for (i = 0; i < HASH_QUEUES; ++i)
87 LIST_INIT(&thr_hashtable[i]);
88 }
89 inited = 1;
90}
91
92void
93_thr_gc(struct pthread *curthread)
94{
95 struct pthread *td, *td_next;
96 TAILQ_HEAD(, pthread) worklist;
97
98 TAILQ_INIT(&worklist);
99 THREAD_LIST_LOCK(curthread);
100
101 /* Check the threads waiting for GC. */
102 for (td = TAILQ_FIRST(&_thread_gc_list); td != NULL; td = td_next) {
103 td_next = TAILQ_NEXT(td, gcle);
104 if (td->terminated == 0) {
105 /* make sure we are not still in userland */
106 continue;
107 }
108 _thr_stack_free(&td->attr);
109 if (((td->tlflags & TLFLAGS_DETACHED) != 0) &&
110 (td->refcount == 0)) {
111 THR_GCLIST_REMOVE(td);
112 /*
113 * The thread has detached and is no longer
114 * referenced. It is safe to remove all
115 * remnants of the thread.
116 */
117 THR_LIST_REMOVE(td);
118 TAILQ_INSERT_HEAD(&worklist, td, gcle);
119 }
120 }
121 THREAD_LIST_UNLOCK(curthread);
122
123 while ((td = TAILQ_FIRST(&worklist)) != NULL) {
124 TAILQ_REMOVE(&worklist, td, gcle);
125 /*
126 * XXX we don't free initial thread, because there might
127 * have some code referencing initial thread.
128 */
129 if (td == _thr_initial) {
130 DBG_MSG("Initial thread won't be freed\n");
131 continue;
132 }
133
71b3fa15
DX
134 _thr_free(curthread, td);
135 }
136}
137
138struct pthread *
139_thr_alloc(struct pthread *curthread)
140{
141 struct pthread *thread = NULL;
9e2ee207 142 struct tls_tcb *tcb;
71b3fa15
DX
143
144 if (curthread != NULL) {
145 if (GC_NEEDED())
146 _thr_gc(curthread);
147 if (free_thread_count > 0) {
148 THR_LOCK_ACQUIRE(curthread, &free_thread_lock);
149 if ((thread = TAILQ_FIRST(&free_threadq)) != NULL) {
150 TAILQ_REMOVE(&free_threadq, thread, tle);
71b3fa15
DX
151 free_thread_count--;
152 }
153 THR_LOCK_RELEASE(curthread, &free_thread_lock);
154 }
155 }
b1085cd3
DX
156 if (thread == NULL) {
157 thread = malloc(sizeof(struct pthread));
158 if (thread == NULL)
159 return (NULL);
160 }
161 if (curthread != NULL) {
162 THR_LOCK_ACQUIRE(curthread, &tcb_lock);
163 tcb = _tcb_ctor(thread, 0 /* not initial tls */);
164 THR_LOCK_RELEASE(curthread, &tcb_lock);
165 } else {
166 tcb = _tcb_ctor(thread, 1 /* initial tls */);
71b3fa15 167 }
b1085cd3 168 if (tcb != NULL) {
71b3fa15
DX
169 memset(thread, 0, sizeof(*thread));
170 thread->tcb = tcb;
b1085cd3
DX
171 } else {
172 thr_destroy(curthread, thread);
173 thread = NULL;
71b3fa15
DX
174 }
175 return (thread);
176}
177
178void
179_thr_free(struct pthread *curthread, struct pthread *thread)
180{
181 DBG_MSG("Freeing thread %p\n", thread);
182 if (thread->name) {
183 free(thread->name);
184 thread->name = NULL;
185 }
b1085cd3
DX
186 /*
187 * Always free tcb, as we only know it is part of RTLD TLS
188 * block, but don't know its detail and can not assume how
189 * it works, so better to avoid caching it here.
190 */
191 if (curthread != NULL) {
192 THR_LOCK_ACQUIRE(curthread, &tcb_lock);
193 _tcb_dtor(thread->tcb);
194 THR_LOCK_RELEASE(curthread, &tcb_lock);
195 } else {
196 _tcb_dtor(thread->tcb);
197 }
198 thread->tcb = NULL;
71b3fa15
DX
199 if ((curthread == NULL) || (free_thread_count >= MAX_CACHED_THREADS)) {
200 thr_destroy(curthread, thread);
201 } else {
b1085cd3
DX
202 /*
203 * Add the thread to the free thread list, this also avoids
204 * pthread id is reused too quickly, may help some buggy apps.
205 */
71b3fa15
DX
206 THR_LOCK_ACQUIRE(curthread, &free_thread_lock);
207 TAILQ_INSERT_TAIL(&free_threadq, thread, tle);
208 free_thread_count++;
209 THR_LOCK_RELEASE(curthread, &free_thread_lock);
210 }
211}
212
213static void
b1085cd3 214thr_destroy(struct pthread *curthread __unused, struct pthread *thread)
71b3fa15 215{
71b3fa15
DX
216 free(thread);
217}
218
219/*
220 * Add an active thread:
221 *
222 * o Assign the thread a unique id (which GDB uses to track
223 * threads.
224 * o Add the thread to the list of all threads and increment
225 * number of active threads.
226 */
227void
228_thr_link(struct pthread *curthread, struct pthread *thread)
229{
230 THREAD_LIST_LOCK(curthread);
231 /*
232 * Initialize the unique id (which GDB uses to track
233 * threads), add the thread to the list of all threads,
234 * and
235 */
236 thread->uniqueid = next_uniqueid++;
237 THR_LIST_ADD(thread);
71b3fa15
DX
238 _thread_active_threads++;
239 THREAD_LIST_UNLOCK(curthread);
240}
241
242/*
243 * Remove an active thread.
244 */
245void
246_thr_unlink(struct pthread *curthread, struct pthread *thread)
247{
248 THREAD_LIST_LOCK(curthread);
249 THR_LIST_REMOVE(thread);
250 _thread_active_threads--;
251 THREAD_LIST_UNLOCK(curthread);
252}
253
254void
255_thr_hash_add(struct pthread *thread)
256{
257 struct thread_hash_head *head;
258
259 head = &thr_hashtable[THREAD_HASH(thread)];
260 LIST_INSERT_HEAD(head, thread, hle);
261}
262
263void
264_thr_hash_remove(struct pthread *thread)
265{
266 LIST_REMOVE(thread, hle);
267}
268
269struct pthread *
270_thr_hash_find(struct pthread *thread)
271{
272 struct pthread *td;
273 struct thread_hash_head *head;
274
275 head = &thr_hashtable[THREAD_HASH(thread)];
276 LIST_FOREACH(td, head, hle) {
277 if (td == thread)
278 return (thread);
279 }
280 return (NULL);
281}
282
283/*
284 * Find a thread in the linked list of active threads and add a reference
285 * to it. Threads with positive reference counts will not be deallocated
286 * until all references are released.
287 */
288int
289_thr_ref_add(struct pthread *curthread, struct pthread *thread,
290 int include_dead)
291{
292 int ret;
293
294 if (thread == NULL)
295 /* Invalid thread: */
296 return (EINVAL);
297
298 THREAD_LIST_LOCK(curthread);
299 if ((ret = _thr_find_thread(curthread, thread, include_dead)) == 0) {
300 thread->refcount++;
301 }
302 THREAD_LIST_UNLOCK(curthread);
303
304 /* Return zero if the thread exists: */
305 return (ret);
306}
307
308void
309_thr_ref_delete(struct pthread *curthread, struct pthread *thread)
c9ad2203
DX
310{
311 THREAD_LIST_LOCK(curthread);
312 _thr_ref_delete_unlocked(curthread, thread);
313 THREAD_LIST_UNLOCK(curthread);
314}
315
316void
317_thr_ref_delete_unlocked(struct pthread *curthread, struct pthread *thread)
71b3fa15
DX
318{
319 if (thread != NULL) {
71b3fa15 320 thread->refcount--;
c9ad2203
DX
321 if ((thread->refcount == 0) && thread->state == PS_DEAD &&
322 (thread->tlflags & TLFLAGS_DETACHED) != 0)
71b3fa15 323 THR_GCLIST_ADD(thread);
71b3fa15
DX
324 }
325}
326
327int
328_thr_find_thread(struct pthread *curthread, struct pthread *thread,
329 int include_dead)
330{
331 struct pthread *pthread;
332
333 if (thread == NULL)
334 /* Invalid thread: */
335 return (EINVAL);
336
337 pthread = _thr_hash_find(thread);
338 if (pthread) {
339 if (include_dead == 0 && pthread->state == PS_DEAD) {
340 pthread = NULL;
341 }
342 }
343
344 /* Return zero if the thread exists: */
345 return ((pthread != NULL) ? 0 : ESRCH);
346}