xref: /dflybsd-src/lib/libthread_xu/thread/thr_list.c (revision cf8046a92768d53e67d2533fb51b137d5506248d)
171b3fa15SDavid Xu /*
271b3fa15SDavid Xu  * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
371b3fa15SDavid Xu  * Copyright (C) 2003 Daniel M. Eischen <deischen@freebsd.org>
471b3fa15SDavid Xu  * All rights reserved.
571b3fa15SDavid Xu  *
671b3fa15SDavid Xu  * Redistribution and use in source and binary forms, with or without
771b3fa15SDavid Xu  * modification, are permitted provided that the following conditions
871b3fa15SDavid Xu  * are met:
971b3fa15SDavid Xu  * 1. Redistributions of source code must retain the above copyright
1071b3fa15SDavid Xu  *    notice unmodified, this list of conditions, and the following
1171b3fa15SDavid Xu  *    disclaimer.
1271b3fa15SDavid Xu  * 2. Redistributions in binary form must reproduce the above copyright
1371b3fa15SDavid Xu  *    notice, this list of conditions and the following disclaimer in the
1471b3fa15SDavid Xu  *    documentation and/or other materials provided with the distribution.
1571b3fa15SDavid Xu  *
1671b3fa15SDavid Xu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1771b3fa15SDavid Xu  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1871b3fa15SDavid Xu  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1971b3fa15SDavid Xu  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2071b3fa15SDavid Xu  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2171b3fa15SDavid Xu  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2271b3fa15SDavid Xu  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2371b3fa15SDavid Xu  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2471b3fa15SDavid Xu  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2571b3fa15SDavid Xu  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2671b3fa15SDavid Xu  */
2771b3fa15SDavid Xu 
2871b3fa15SDavid Xu #include <sys/types.h>
2971b3fa15SDavid Xu #include <sys/queue.h>
3071b3fa15SDavid Xu 
3171b3fa15SDavid Xu #include <stdlib.h>
3271b3fa15SDavid Xu #include <string.h>
3371b3fa15SDavid Xu #include <pthread.h>
3471b3fa15SDavid Xu 
3571b3fa15SDavid Xu #include "libc_private.h"
366be5672cSzrj #include "thr_private.h"
3771b3fa15SDavid Xu 
3866bf1134SDavid Xu /* #define DEBUG_THREAD_LIST */
3971b3fa15SDavid Xu #ifdef DEBUG_THREAD_LIST
4071b3fa15SDavid Xu #define DBG_MSG		stdout_debug
4171b3fa15SDavid Xu #else
4271b3fa15SDavid Xu #define DBG_MSG(x...)
4371b3fa15SDavid Xu #endif
4471b3fa15SDavid Xu 
45e8382b15SDavid Xu /* List of all threads */
46e8382b15SDavid Xu struct thread_head _thread_list = TAILQ_HEAD_INITIALIZER(_thread_list);
47e8382b15SDavid Xu 
48e8382b15SDavid Xu /* List of threads needing GC */
49e8382b15SDavid Xu struct thread_head _thread_gc_list = TAILQ_HEAD_INITIALIZER(_thread_gc_list);
50e8382b15SDavid Xu 
51e8382b15SDavid Xu /* Number of active threads */
52e8382b15SDavid Xu int	_thread_active_threads = 1;
53e8382b15SDavid Xu 
54e8382b15SDavid Xu /* Garbage thread count. */
55e8382b15SDavid Xu int	_thr_gc_count;
56e8382b15SDavid Xu 
57e8382b15SDavid Xu umtx_t	_thr_list_lock;
58e8382b15SDavid Xu 
5971b3fa15SDavid Xu /*
6071b3fa15SDavid Xu  * Define a high water mark for the maximum number of threads that
6171b3fa15SDavid Xu  * will be cached.  Once this level is reached, any extra threads
6271b3fa15SDavid Xu  * will be free()'d.
6371b3fa15SDavid Xu  */
6471b3fa15SDavid Xu #define	MAX_CACHED_THREADS	100
6571b3fa15SDavid Xu 
6671b3fa15SDavid Xu /*
6771b3fa15SDavid Xu  * We've got to keep track of everything that is allocated, not only
6871b3fa15SDavid Xu  * to have a speedy free list, but also so they can be deallocated
6971b3fa15SDavid Xu  * after a fork().
7071b3fa15SDavid Xu  */
71*cf8046a9Szrj static TAILQ_HEAD(, __pthread_s) free_threadq;
7271b3fa15SDavid Xu static umtx_t			free_thread_lock;
7371b3fa15SDavid Xu static umtx_t			tcb_lock;
7471b3fa15SDavid Xu static int			free_thread_count = 0;
7571b3fa15SDavid Xu static int			inited = 0;
7671b3fa15SDavid Xu static u_int64_t		next_uniqueid = 1;
7771b3fa15SDavid Xu 
78*cf8046a9Szrj LIST_HEAD(thread_hash_head, __pthread_s);
7971b3fa15SDavid Xu #define HASH_QUEUES	128
8071b3fa15SDavid Xu static struct thread_hash_head	thr_hashtable[HASH_QUEUES];
8171b3fa15SDavid Xu #define	THREAD_HASH(thrd)	(((unsigned long)thrd >> 12) % HASH_QUEUES)
8271b3fa15SDavid Xu 
83940be950Szrj static void thr_destroy(pthread_t curthread, pthread_t thread);
8471b3fa15SDavid Xu 
8571b3fa15SDavid Xu void
_thr_list_init(void)8671b3fa15SDavid Xu _thr_list_init(void)
8771b3fa15SDavid Xu {
8871b3fa15SDavid Xu 	int i;
8971b3fa15SDavid Xu 
90e8382b15SDavid Xu 	_thr_gc_count = 0;
9171b3fa15SDavid Xu 	_thr_umtx_init(&_thr_list_lock);
9271b3fa15SDavid Xu 	TAILQ_INIT(&_thread_list);
9371b3fa15SDavid Xu 	TAILQ_INIT(&free_threadq);
9471b3fa15SDavid Xu 	_thr_umtx_init(&free_thread_lock);
9571b3fa15SDavid Xu 	_thr_umtx_init(&tcb_lock);
9671b3fa15SDavid Xu 	if (inited) {
9771b3fa15SDavid Xu 		for (i = 0; i < HASH_QUEUES; ++i)
9871b3fa15SDavid Xu 			LIST_INIT(&thr_hashtable[i]);
9971b3fa15SDavid Xu 	}
10071b3fa15SDavid Xu 	inited = 1;
10171b3fa15SDavid Xu }
10271b3fa15SDavid Xu 
10371b3fa15SDavid Xu void
_thr_gc(pthread_t curthread)104940be950Szrj _thr_gc(pthread_t curthread)
10571b3fa15SDavid Xu {
106*cf8046a9Szrj 	pthread_t td, td_next;
107*cf8046a9Szrj 	TAILQ_HEAD(, __pthread_s) worklist;
10871b3fa15SDavid Xu 
10971b3fa15SDavid Xu 	TAILQ_INIT(&worklist);
11071b3fa15SDavid Xu 	THREAD_LIST_LOCK(curthread);
11171b3fa15SDavid Xu 
11271b3fa15SDavid Xu 	/* Check the threads waiting for GC. */
11371b3fa15SDavid Xu 	for (td = TAILQ_FIRST(&_thread_gc_list); td != NULL; td = td_next) {
11471b3fa15SDavid Xu 		td_next = TAILQ_NEXT(td, gcle);
11571b3fa15SDavid Xu 		if (td->terminated == 0) {
11671b3fa15SDavid Xu 			/* make sure we are not still in userland */
11771b3fa15SDavid Xu 			continue;
11871b3fa15SDavid Xu 		}
11971b3fa15SDavid Xu 		_thr_stack_free(&td->attr);
12071b3fa15SDavid Xu 		if (((td->tlflags & TLFLAGS_DETACHED) != 0) &&
12171b3fa15SDavid Xu 		    (td->refcount == 0)) {
12271b3fa15SDavid Xu 			THR_GCLIST_REMOVE(td);
12371b3fa15SDavid Xu 			/*
12471b3fa15SDavid Xu 			 * The thread has detached and is no longer
12571b3fa15SDavid Xu 			 * referenced.  It is safe to remove all
12671b3fa15SDavid Xu 			 * remnants of the thread.
12771b3fa15SDavid Xu 			 */
12871b3fa15SDavid Xu 			THR_LIST_REMOVE(td);
12971b3fa15SDavid Xu 			TAILQ_INSERT_HEAD(&worklist, td, gcle);
13071b3fa15SDavid Xu 		}
13171b3fa15SDavid Xu 	}
13271b3fa15SDavid Xu 	THREAD_LIST_UNLOCK(curthread);
13371b3fa15SDavid Xu 
13471b3fa15SDavid Xu 	while ((td = TAILQ_FIRST(&worklist)) != NULL) {
13571b3fa15SDavid Xu 		TAILQ_REMOVE(&worklist, td, gcle);
13671b3fa15SDavid Xu 		/*
13771b3fa15SDavid Xu 		 * XXX we don't free initial thread, because there might
13871b3fa15SDavid Xu 		 * have some code referencing initial thread.
13971b3fa15SDavid Xu 		 */
14071b3fa15SDavid Xu 		if (td == _thr_initial) {
14171b3fa15SDavid Xu 			DBG_MSG("Initial thread won't be freed\n");
14271b3fa15SDavid Xu 			continue;
14371b3fa15SDavid Xu 		}
14471b3fa15SDavid Xu 
14571b3fa15SDavid Xu 		_thr_free(curthread, td);
14671b3fa15SDavid Xu 	}
14771b3fa15SDavid Xu }
14871b3fa15SDavid Xu 
149940be950Szrj pthread_t
_thr_alloc(pthread_t curthread)150940be950Szrj _thr_alloc(pthread_t curthread)
15171b3fa15SDavid Xu {
152940be950Szrj 	pthread_t	thread = NULL;
1539e2ee207SJoerg Sonnenberger 	struct tls_tcb	*tcb;
15471b3fa15SDavid Xu 
15571b3fa15SDavid Xu 	if (curthread != NULL) {
15671b3fa15SDavid Xu 		if (GC_NEEDED())
15771b3fa15SDavid Xu 			_thr_gc(curthread);
15871b3fa15SDavid Xu 		if (free_thread_count > 0) {
15971b3fa15SDavid Xu 			THR_LOCK_ACQUIRE(curthread, &free_thread_lock);
16071b3fa15SDavid Xu 			if ((thread = TAILQ_FIRST(&free_threadq)) != NULL) {
16171b3fa15SDavid Xu 				TAILQ_REMOVE(&free_threadq, thread, tle);
16271b3fa15SDavid Xu 				free_thread_count--;
16371b3fa15SDavid Xu 			}
16471b3fa15SDavid Xu 			THR_LOCK_RELEASE(curthread, &free_thread_lock);
16571b3fa15SDavid Xu 		}
16671b3fa15SDavid Xu 	}
167b1085cd3SDavid Xu 	if (thread == NULL) {
168*cf8046a9Szrj 		thread = __malloc(sizeof(struct __pthread_s));
169b1085cd3SDavid Xu 		if (thread == NULL)
170b1085cd3SDavid Xu 			return (NULL);
171b1085cd3SDavid Xu 	}
172b1085cd3SDavid Xu 	if (curthread != NULL) {
17371b3fa15SDavid Xu 		THR_LOCK_ACQUIRE(curthread, &tcb_lock);
17471b3fa15SDavid Xu 		tcb = _tcb_ctor(thread, 0 /* not initial tls */);
17571b3fa15SDavid Xu 		THR_LOCK_RELEASE(curthread, &tcb_lock);
17671b3fa15SDavid Xu 	} else {
17771b3fa15SDavid Xu 		tcb = _tcb_ctor(thread, 1 /* initial tls */);
17871b3fa15SDavid Xu 	}
179b1085cd3SDavid Xu 	if (tcb != NULL) {
18071b3fa15SDavid Xu 		memset(thread, 0, sizeof(*thread));
18171b3fa15SDavid Xu 		thread->tcb = tcb;
182b1085cd3SDavid Xu 	} else {
183b1085cd3SDavid Xu 		thr_destroy(curthread, thread);
184b1085cd3SDavid Xu 		thread = NULL;
18571b3fa15SDavid Xu 	}
18671b3fa15SDavid Xu 	return (thread);
18771b3fa15SDavid Xu }
18871b3fa15SDavid Xu 
18971b3fa15SDavid Xu void
_thr_free(pthread_t curthread,pthread_t thread)190940be950Szrj _thr_free(pthread_t curthread, pthread_t thread)
19171b3fa15SDavid Xu {
19271b3fa15SDavid Xu 	DBG_MSG("Freeing thread %p\n", thread);
19371b3fa15SDavid Xu 	if (thread->name) {
194e7bf3f77SMatthew Dillon 		__free(thread->name);
19571b3fa15SDavid Xu 		thread->name = NULL;
19671b3fa15SDavid Xu 	}
197b1085cd3SDavid Xu 	/*
198b1085cd3SDavid Xu 	 * Always free tcb, as we only know it is part of RTLD TLS
199b1085cd3SDavid Xu 	 * block, but don't know its detail and can not assume how
200b1085cd3SDavid Xu 	 * it works, so better to avoid caching it here.
201b1085cd3SDavid Xu 	 */
202b1085cd3SDavid Xu 	if (curthread != NULL) {
203b1085cd3SDavid Xu 		THR_LOCK_ACQUIRE(curthread, &tcb_lock);
204b1085cd3SDavid Xu 		_tcb_dtor(thread->tcb);
205b1085cd3SDavid Xu 		THR_LOCK_RELEASE(curthread, &tcb_lock);
206b1085cd3SDavid Xu 	} else {
207b1085cd3SDavid Xu 		_tcb_dtor(thread->tcb);
208b1085cd3SDavid Xu 	}
209b1085cd3SDavid Xu 	thread->tcb = NULL;
21071b3fa15SDavid Xu 	if ((curthread == NULL) || (free_thread_count >= MAX_CACHED_THREADS)) {
21171b3fa15SDavid Xu 		thr_destroy(curthread, thread);
21271b3fa15SDavid Xu 	} else {
213b1085cd3SDavid Xu 		/*
214b1085cd3SDavid Xu 		 * Add the thread to the free thread list, this also avoids
215b1085cd3SDavid Xu 		 * pthread id is reused too quickly, may help some buggy apps.
216b1085cd3SDavid Xu 		 */
21771b3fa15SDavid Xu 		THR_LOCK_ACQUIRE(curthread, &free_thread_lock);
21871b3fa15SDavid Xu 		TAILQ_INSERT_TAIL(&free_threadq, thread, tle);
21971b3fa15SDavid Xu 		free_thread_count++;
22071b3fa15SDavid Xu 		THR_LOCK_RELEASE(curthread, &free_thread_lock);
22171b3fa15SDavid Xu 	}
22271b3fa15SDavid Xu }
22371b3fa15SDavid Xu 
22471b3fa15SDavid Xu static void
thr_destroy(pthread_t curthread __unused,pthread_t thread)225940be950Szrj thr_destroy(pthread_t curthread __unused, pthread_t thread)
22671b3fa15SDavid Xu {
227e7bf3f77SMatthew Dillon 	__free(thread);
22871b3fa15SDavid Xu }
22971b3fa15SDavid Xu 
23071b3fa15SDavid Xu /*
23171b3fa15SDavid Xu  * Add an active thread:
23271b3fa15SDavid Xu  *
23371b3fa15SDavid Xu  *   o Assign the thread a unique id (which GDB uses to track
23471b3fa15SDavid Xu  *     threads.
23571b3fa15SDavid Xu  *   o Add the thread to the list of all threads and increment
23671b3fa15SDavid Xu  *     number of active threads.
23771b3fa15SDavid Xu  */
23871b3fa15SDavid Xu void
_thr_link(pthread_t curthread,pthread_t thread)239940be950Szrj _thr_link(pthread_t curthread, pthread_t thread)
24071b3fa15SDavid Xu {
24171b3fa15SDavid Xu 	THREAD_LIST_LOCK(curthread);
24271b3fa15SDavid Xu 	/*
24371b3fa15SDavid Xu 	 * Initialize the unique id (which GDB uses to track
24471b3fa15SDavid Xu 	 * threads), add the thread to the list of all threads,
24571b3fa15SDavid Xu 	 * and
24671b3fa15SDavid Xu 	 */
24771b3fa15SDavid Xu 	thread->uniqueid = next_uniqueid++;
24871b3fa15SDavid Xu 	THR_LIST_ADD(thread);
24971b3fa15SDavid Xu 	_thread_active_threads++;
25071b3fa15SDavid Xu 	THREAD_LIST_UNLOCK(curthread);
25171b3fa15SDavid Xu }
25271b3fa15SDavid Xu 
25371b3fa15SDavid Xu /*
25471b3fa15SDavid Xu  * Remove an active thread.
25571b3fa15SDavid Xu  */
25671b3fa15SDavid Xu void
_thr_unlink(pthread_t curthread,pthread_t thread)257940be950Szrj _thr_unlink(pthread_t curthread, pthread_t thread)
25871b3fa15SDavid Xu {
25971b3fa15SDavid Xu 	THREAD_LIST_LOCK(curthread);
26071b3fa15SDavid Xu 	THR_LIST_REMOVE(thread);
26171b3fa15SDavid Xu 	_thread_active_threads--;
26271b3fa15SDavid Xu 	THREAD_LIST_UNLOCK(curthread);
26371b3fa15SDavid Xu }
26471b3fa15SDavid Xu 
26571b3fa15SDavid Xu void
_thr_hash_add(pthread_t thread)266940be950Szrj _thr_hash_add(pthread_t thread)
26771b3fa15SDavid Xu {
26871b3fa15SDavid Xu 	struct thread_hash_head *head;
26971b3fa15SDavid Xu 
27071b3fa15SDavid Xu 	head = &thr_hashtable[THREAD_HASH(thread)];
27171b3fa15SDavid Xu 	LIST_INSERT_HEAD(head, thread, hle);
27271b3fa15SDavid Xu }
27371b3fa15SDavid Xu 
27471b3fa15SDavid Xu void
_thr_hash_remove(pthread_t thread)275940be950Szrj _thr_hash_remove(pthread_t thread)
27671b3fa15SDavid Xu {
27771b3fa15SDavid Xu 	LIST_REMOVE(thread, hle);
27871b3fa15SDavid Xu }
27971b3fa15SDavid Xu 
280940be950Szrj pthread_t
_thr_hash_find(pthread_t thread)281940be950Szrj _thr_hash_find(pthread_t thread)
28271b3fa15SDavid Xu {
283940be950Szrj 	pthread_t td;
28471b3fa15SDavid Xu 	struct thread_hash_head *head;
28571b3fa15SDavid Xu 
28671b3fa15SDavid Xu 	head = &thr_hashtable[THREAD_HASH(thread)];
28771b3fa15SDavid Xu 	LIST_FOREACH(td, head, hle) {
28871b3fa15SDavid Xu 		if (td == thread)
28971b3fa15SDavid Xu 			return (thread);
29071b3fa15SDavid Xu 	}
29171b3fa15SDavid Xu 	return (NULL);
29271b3fa15SDavid Xu }
29371b3fa15SDavid Xu 
29471b3fa15SDavid Xu /*
29571b3fa15SDavid Xu  * Find a thread in the linked list of active threads and add a reference
29671b3fa15SDavid Xu  * to it.  Threads with positive reference counts will not be deallocated
29771b3fa15SDavid Xu  * until all references are released.
29871b3fa15SDavid Xu  */
29971b3fa15SDavid Xu int
_thr_ref_add(pthread_t curthread,pthread_t thread,int include_dead)300940be950Szrj _thr_ref_add(pthread_t curthread, pthread_t thread,
30171b3fa15SDavid Xu     int include_dead)
30271b3fa15SDavid Xu {
30371b3fa15SDavid Xu 	int ret;
30471b3fa15SDavid Xu 
30571b3fa15SDavid Xu 	if (thread == NULL)
30671b3fa15SDavid Xu 		/* Invalid thread: */
30771b3fa15SDavid Xu 		return (EINVAL);
30871b3fa15SDavid Xu 
30971b3fa15SDavid Xu 	THREAD_LIST_LOCK(curthread);
31071b3fa15SDavid Xu 	if ((ret = _thr_find_thread(curthread, thread, include_dead)) == 0) {
31171b3fa15SDavid Xu 		thread->refcount++;
31271b3fa15SDavid Xu 	}
31371b3fa15SDavid Xu 	THREAD_LIST_UNLOCK(curthread);
31471b3fa15SDavid Xu 
31571b3fa15SDavid Xu 	/* Return zero if the thread exists: */
31671b3fa15SDavid Xu 	return (ret);
31771b3fa15SDavid Xu }
31871b3fa15SDavid Xu 
31971b3fa15SDavid Xu void
_thr_ref_delete(pthread_t curthread,pthread_t thread)320940be950Szrj _thr_ref_delete(pthread_t curthread, pthread_t thread)
32171b3fa15SDavid Xu {
32271b3fa15SDavid Xu 	THREAD_LIST_LOCK(curthread);
323c9ad2203SDavid Xu 	_thr_ref_delete_unlocked(curthread, thread);
32471b3fa15SDavid Xu 	THREAD_LIST_UNLOCK(curthread);
32571b3fa15SDavid Xu }
326c9ad2203SDavid Xu 
327c9ad2203SDavid Xu void
_thr_ref_delete_unlocked(pthread_t curthread __unused,pthread_t thread)328940be950Szrj _thr_ref_delete_unlocked(pthread_t curthread __unused, pthread_t thread)
329c9ad2203SDavid Xu {
330c9ad2203SDavid Xu 	if (thread != NULL) {
331c9ad2203SDavid Xu 		thread->refcount--;
332c9ad2203SDavid Xu 		if ((thread->refcount == 0) && thread->state == PS_DEAD &&
333c9ad2203SDavid Xu 		    (thread->tlflags & TLFLAGS_DETACHED) != 0)
334c9ad2203SDavid Xu 			THR_GCLIST_ADD(thread);
335c9ad2203SDavid Xu 	}
33671b3fa15SDavid Xu }
33771b3fa15SDavid Xu 
33871b3fa15SDavid Xu int
_thr_find_thread(pthread_t curthread __unused,pthread_t thread,int include_dead)339940be950Szrj _thr_find_thread(pthread_t curthread __unused, pthread_t thread,
34071b3fa15SDavid Xu     int include_dead)
34171b3fa15SDavid Xu {
342940be950Szrj 	pthread_t pthread;
34371b3fa15SDavid Xu 
34471b3fa15SDavid Xu 	if (thread == NULL)
34571b3fa15SDavid Xu 		return (EINVAL);
34671b3fa15SDavid Xu 
34771b3fa15SDavid Xu 	pthread = _thr_hash_find(thread);
34871b3fa15SDavid Xu 	if (pthread) {
34971b3fa15SDavid Xu 		if (include_dead == 0 && pthread->state == PS_DEAD) {
35071b3fa15SDavid Xu 			pthread = NULL;
35171b3fa15SDavid Xu 		}
35271b3fa15SDavid Xu 	}
35371b3fa15SDavid Xu 
35471b3fa15SDavid Xu 	/* Return zero if the thread exists: */
35571b3fa15SDavid Xu 	return ((pthread != NULL) ? 0 : ESRCH);
35671b3fa15SDavid Xu }
357