diff --git a/lib/libc_r/uthread/Makefile.inc b/lib/libc_r/uthread/Makefile.inc index 0c5c5822d714..004982a8b6bc 100644 --- a/lib/libc_r/uthread/Makefile.inc +++ b/lib/libc_r/uthread/Makefile.inc @@ -1,4 +1,4 @@ -# $Id: Makefile.inc,v 1.14 1998/09/07 19:01:43 alex Exp $ +# $Id: Makefile.inc,v 1.15 1998/09/12 22:03:20 dt Exp $ # uthread sources .PATH: ${.CURDIR}/uthread @@ -40,6 +40,7 @@ SRCS+= \ uthread_fstat.c \ uthread_fstatfs.c \ uthread_fsync.c \ + uthread_gc.c \ uthread_getdirentries.c \ uthread_getpeername.c \ uthread_getprio.c \ diff --git a/lib/libc_r/uthread/uthread_create.c b/lib/libc_r/uthread/uthread_create.c index 7603dd3a30c9..c7ddeb2a59c0 100644 --- a/lib/libc_r/uthread/uthread_create.c +++ b/lib/libc_r/uthread/uthread_create.c @@ -46,9 +46,11 @@ int pthread_create(pthread_t * thread, const pthread_attr_t * attr, void *(*start_routine) (void *), void *arg) { + int f_gc = 0; int i; int ret = 0; int status; + pthread_t gc_thread; pthread_t new_thread; pthread_attr_t pattr; void *stack; @@ -185,6 +187,12 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr, /* Lock the thread list: */ _lock_thread_list(); + /* + * Check if the garbage collector thread + * needs to be started. + */ + f_gc = (_thread_link_list == _thread_initial); + /* Add the thread to the linked list of all threads: */ new_thread->nxt = _thread_link_list; _thread_link_list = new_thread; @@ -197,6 +205,14 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr, /* Schedule the new user thread: */ _thread_kern_sched(NULL); + + /* + * Start a garbage collector thread + * if necessary. + */ + if (f_gc && pthread_create(&gc_thread,NULL, + _thread_gc,NULL) != 0) + PANIC("Can't create gc thread"); } } diff --git a/lib/libc_r/uthread/uthread_exit.c b/lib/libc_r/uthread/uthread_exit.c index 0083f2b01694..a5fc4001e414 100644 --- a/lib/libc_r/uthread/uthread_exit.c +++ b/lib/libc_r/uthread/uthread_exit.c @@ -133,56 +133,29 @@ pthread_exit(void *status) PTHREAD_NEW_STATE(pthread,PS_RUNNING); } - /* Lock the thread list: */ - _lock_thread_list(); - - /* Check if the running thread is at the head of the linked list: */ - if (_thread_link_list == _thread_run) { - /* There is no previous thread: */ - _thread_link_list = _thread_run->nxt; - } else { - /* Point to the first thread in the list: */ - pthread = _thread_link_list; - - /* - * Enter a loop to find the thread in the linked list before - * the running thread: - */ - while (pthread != NULL && pthread->nxt != _thread_run) { - /* Point to the next thread: */ - pthread = pthread->nxt; - } - - /* Check that a previous thread was found: */ - if (pthread != NULL) { - /* - * Point the previous thread to the one after the - * running thread: - */ - pthread->nxt = _thread_run->nxt; - } - } - - /* Unlock the thread list: */ - _unlock_thread_list(); - - /* Lock the dead thread list: */ - _lock_dead_thread_list(); - /* - * This thread will never run again. Add it to the list of dead - * threads: + * Lock the garbage collector mutex to ensure that the garbage + * collector is not using the dead thread list. */ - _thread_run->nxt = _thread_dead; + if (pthread_mutex_lock(&_gc_mutex) != 0) + PANIC("Cannot lock gc mutex"); + + /* Add this thread to the list of dead threads. */ + _thread_run->nxt_dead = _thread_dead; _thread_dead = _thread_run; - /* Unlock the dead thread list: */ - _unlock_dead_thread_list(); - /* - * The running thread is no longer in the thread link list so it will - * now die: + * Signal the garbage collector thread that there is something + * to clean up. */ + if (pthread_cond_signal(&_gc_cond) != 0) + PANIC("Cannot signal gc cond"); + + /* Unlock the garbage collector mutex: */ + if (pthread_mutex_unlock(&_gc_mutex) != 0) + PANIC("Cannot lock gc mutex"); + + /* This this thread will never be re-scheduled. */ _thread_kern_sched_state(PS_DEAD, __FILE__, __LINE__); /* This point should not be reached. */ diff --git a/lib/libc_r/uthread/uthread_find_thread.c b/lib/libc_r/uthread/uthread_find_thread.c index 99e302306d2a..e4a59a0d83f6 100644 --- a/lib/libc_r/uthread/uthread_find_thread.c +++ b/lib/libc_r/uthread/uthread_find_thread.c @@ -76,8 +76,12 @@ _find_dead_thread(pthread_t pthread) /* Invalid thread: */ return(EINVAL); - /* Lock the dead thread list: */ - _lock_dead_thread_list(); + /* + * Lock the garbage collector mutex to ensure that the garbage + * collector is not using the dead thread list. + */ + if (pthread_mutex_lock(&_gc_mutex) != 0) + PANIC("Cannot lock gc mutex"); /* Point to the first thread in the list: */ pthread1 = _thread_dead; @@ -85,11 +89,12 @@ _find_dead_thread(pthread_t pthread) /* Search for the thread to join to: */ while (pthread1 != NULL && pthread1 != pthread) { /* Point to the next thread: */ - pthread1 = pthread1->nxt; + pthread1 = pthread1->nxt_dead; } - /* Unlock the dead thread list: */ - _unlock_dead_thread_list(); + /* Unlock the garbage collector mutex: */ + if (pthread_mutex_unlock(&_gc_mutex) != 0) + PANIC("Cannot lock gc mutex"); /* Return zero if the thread exists: */ return ((pthread1 != NULL) ? 0:ESRCH); diff --git a/lib/libc_r/uthread/uthread_gc.c b/lib/libc_r/uthread/uthread_gc.c new file mode 100644 index 000000000000..faf1b5d13b77 --- /dev/null +++ b/lib/libc_r/uthread/uthread_gc.c @@ -0,0 +1,272 @@ +/* + * Copyright (c) 1998 John Birrell + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + * + * Garbage collector thread. Frees memory allocated for dead threads. + * + */ +#include +#include +#include +#include +#include +#include "pthread_private.h" + +pthread_addr_t +_thread_gc(pthread_addr_t arg) +{ + int f_debug; + int f_done = 0; + int ret; + pthread_t pthread; + pthread_t pthread_cln; + pthread_t pthread_nxt; + pthread_t pthread_prv; + struct timespec abstime; + void *p_stack; + + /* Set a debug flag based on an environment variable. */ + f_debug = (getenv("LIBC_R_DEBUG") == NULL); + + /* Set the name of this thread. */ + pthread_set_name_np(_thread_run,"GC"); + + while (!f_done) { + /* Check if debugging this application. */ + if (f_debug) + /* Dump thread info to file. */ + _thread_dump_info(); + + /* Lock the thread list: */ + _lock_thread_list(); + + /* Check if this is the last running thread: */ + if (_thread_link_list == _thread_run && + _thread_link_list->nxt == NULL) + /* + * This is the last thread, so it can exit + * now. + */ + f_done = 1; + + /* Unlock the thread list: */ + _unlock_thread_list(); + + /* + * Lock the garbage collector mutex which ensures that + * this thread sees another thread exit: + */ + if (pthread_mutex_lock(&_gc_mutex) != 0) + PANIC("Cannot lock gc mutex"); + + /* No stack of thread structure to free yet: */ + p_stack = NULL; + pthread_cln = NULL; + + /* Point to the first dead thread (if there are any): */ + pthread = _thread_dead; + + /* There is no previous dead thread: */ + pthread_prv = NULL; + + /* + * Enter a loop to search for the first dead thread that + * has memory to free. + */ + while (p_stack == NULL && pthread_cln == NULL && + pthread != NULL) { + /* Save a pointer to the next thread: */ + pthread_nxt = pthread->nxt_dead; + + /* Check if the initial thread: */ + if (pthread == _thread_initial) + /* Don't destroy the initial thread. */ + pthread_prv = pthread; + + /* + * Check if this thread has detached: + */ + else if ((pthread->attr.flags & + PTHREAD_DETACHED) != 0) { + /* + * Check if there is no previous dead + * thread: + */ + if (pthread_prv == NULL) + /* + * The dead thread is at the head + * of the list: + */ + _thread_dead = pthread_nxt; + else + /* + * The dead thread is not at the + * head of the list: + */ + pthread_prv->nxt_dead = + pthread->nxt_dead; + + /* + * Check if the stack was not specified by + * the caller to pthread_create and has not + * been destroyed yet: + */ + if (pthread->attr.stackaddr_attr == NULL && + pthread->stack != NULL) { + /* + * Point to the stack that must + * be freed outside the locks: + */ + p_stack = pthread->stack; + } + + /* + * Point to the thread structure that must + * be freed outside the locks: + */ + pthread_cln = pthread; + } else { + /* + * This thread has not detached, so do + * not destroy it: + */ + pthread_prv = pthread; + + /* + * Check if the stack was not specified by + * the caller to pthread_create and has not + * been destroyed yet: + */ + if (pthread->attr.stackaddr_attr == NULL && + pthread->stack != NULL) { + /* + * Point to the stack that must + * be freed outside the locks: + */ + p_stack = pthread->stack; + + /* + * NULL the stack pointer now + * that the memory has been freed: + */ + pthread->stack = NULL; + } + } + + /* Point to the next thread: */ + pthread = pthread_nxt; + } + + /* + * Check if this is not the last thread and there is no + * memory to free this time around. + */ + if (!f_done && p_stack == NULL && pthread_cln == NULL) { + /* Get the current time. */ + if (clock_gettime(CLOCK_REALTIME,&abstime) != 0) + PANIC("gc cannot get time"); + + /* + * Do a backup poll in 10 seconds if no threads + * die before then. + */ + abstime.tv_sec += 10; + + /* + * Wait for a signal from a dying thread or a + * timeout (for a backup poll). + */ + if ((ret = pthread_cond_timedwait(&_gc_cond, + &_gc_mutex, &abstime)) != 0 && ret != ETIMEDOUT) + PANIC("gc cannot wait for a signal"); + } + + /* Unlock the garbage collector mutex: */ + if (pthread_mutex_unlock(&_gc_mutex) != 0) + PANIC("Cannot unlock gc mutex"); + + /* + * If there is memory to free, do it now. The call to + * free() might block, so this must be done outside the + * locks. + */ + if (p_stack != NULL) + free(p_stack); + if (pthread_cln != NULL) { + /* Lock the thread list: */ + _lock_thread_list(); + + /* + * Check if the thread is at the head of the + * linked list. + */ + if (_thread_link_list == pthread_cln) + /* There is no previous thread: */ + _thread_link_list = pthread_cln->nxt; + else { + /* Point to the first thread in the list: */ + pthread = _thread_link_list; + + /* + * Enter a loop to find the thread in the + * linked list before the thread that is + * about to be freed. + */ + while (pthread != NULL && + pthread->nxt != pthread_cln) + /* Point to the next thread: */ + pthread = pthread->nxt; + + /* Check that a previous thread was found: */ + if (pthread != NULL) { + /* + * Point the previous thread to + * the one after the thread being + * freed: + */ + pthread->nxt = pthread_cln->nxt; + } + } + + /* Unlock the thread list: */ + _unlock_thread_list(); + + /* + * Free the memory allocated for the thread + * structure. + */ + free(pthread_cln); + } + + } + return (NULL); +} diff --git a/lib/libc_r/uthread/uthread_init.c b/lib/libc_r/uthread/uthread_init.c index 69b0fef563ae..50f3bef8973a 100644 --- a/lib/libc_r/uthread/uthread_init.c +++ b/lib/libc_r/uthread/uthread_init.c @@ -251,6 +251,11 @@ _thread_init(void) __set_dynamic_handler_allocator( dynamic_allocator_handler_fn ); #endif /* GCC_2_8_MADE_THREAD_AWARE */ + /* Initialise the garbage collector mutex and condition variable. */ + if (pthread_mutex_init(&_gc_mutex,NULL) != 0 || + pthread_cond_init(&_gc_cond,NULL) != 0) + PANIC("Failed to initialise garbage collector mutex or condvar"); + return; } diff --git a/lib/libc_r/uthread/uthread_kern.c b/lib/libc_r/uthread/uthread_kern.c index ce17cf85343c..da125cd3e645 100644 --- a/lib/libc_r/uthread/uthread_kern.c +++ b/lib/libc_r/uthread/uthread_kern.c @@ -29,7 +29,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: uthread_kern.c,v 1.11 1998/04/30 21:50:29 jb Exp $ + * $Id: uthread_kern.c,v 1.12 1998/09/07 21:55:01 alex Exp $ * */ #include @@ -62,8 +62,6 @@ _thread_kern_sched(struct sigcontext * scp) int prio = -1; pthread_t pthread; pthread_t pthread_h = NULL; - pthread_t pthread_nxt = NULL; - pthread_t pthread_prv = NULL; pthread_t pthread_s = NULL; struct itimerval itimer; struct timespec ts; @@ -113,85 +111,9 @@ __asm__("fnsave %0": :"m"(*fdata)); */ _dispatch_signals(); return; - } else { + } else /* Flag the jump buffer was the last state saved: */ _thread_run->sig_saved = 0; - } - - /* Point to the first dead thread (if there are any): */ - pthread = _thread_dead; - - /* There is no previous dead thread: */ - pthread_prv = NULL; - - /* Enter a loop to cleanup after dead threads: */ - while (pthread != NULL) { - /* Save a pointer to the next thread: */ - pthread_nxt = pthread->nxt; - - /* Check if this thread is one which is running: */ - if (pthread == _thread_run || pthread == _thread_initial) { - /* - * Don't destroy the running thread or the initial - * thread. - */ - pthread_prv = pthread; - } - /* - * Check if this thread has detached: - */ - else if ((pthread->attr.flags & PTHREAD_DETACHED) != 0) { - /* Check if there is no previous dead thread: */ - if (pthread_prv == NULL) { - /* - * The dead thread is at the head of the - * list: - */ - _thread_dead = pthread_nxt; - } else { - /* - * The dead thread is not at the head of the - * list: - */ - pthread_prv->nxt = pthread->nxt; - } - - /* - * Check if the stack was not specified by the caller - * to pthread_create and has not been destroyed yet: - */ - if (pthread->attr.stackaddr_attr == NULL && pthread->stack != NULL) { - /* Free the stack of the dead thread: */ - free(pthread->stack); - } - /* Free the memory allocated to the thread structure: */ - free(pthread); - } else { - /* - * This thread has not detached, so do not destroy - * it: - */ - pthread_prv = pthread; - - /* - * Check if the stack was not specified by the caller - * to pthread_create and has not been destroyed yet: - */ - if (pthread->attr.stackaddr_attr == NULL && pthread->stack != NULL) { - /* Free the stack of the dead thread: */ - free(pthread->stack); - - /* - * NULL the stack pointer now that the memory - * has been freed: - */ - pthread->stack = NULL; - } - } - - /* Point to the next thread: */ - pthread = pthread_nxt; - } /* * Enter a the scheduling loop that finds the next thread that is diff --git a/lib/libkse/thread/Makefile.inc b/lib/libkse/thread/Makefile.inc index 0c5c5822d714..004982a8b6bc 100644 --- a/lib/libkse/thread/Makefile.inc +++ b/lib/libkse/thread/Makefile.inc @@ -1,4 +1,4 @@ -# $Id: Makefile.inc,v 1.14 1998/09/07 19:01:43 alex Exp $ +# $Id: Makefile.inc,v 1.15 1998/09/12 22:03:20 dt Exp $ # uthread sources .PATH: ${.CURDIR}/uthread @@ -40,6 +40,7 @@ SRCS+= \ uthread_fstat.c \ uthread_fstatfs.c \ uthread_fsync.c \ + uthread_gc.c \ uthread_getdirentries.c \ uthread_getpeername.c \ uthread_getprio.c \ diff --git a/lib/libkse/thread/thr_create.c b/lib/libkse/thread/thr_create.c index 7603dd3a30c9..c7ddeb2a59c0 100644 --- a/lib/libkse/thread/thr_create.c +++ b/lib/libkse/thread/thr_create.c @@ -46,9 +46,11 @@ int pthread_create(pthread_t * thread, const pthread_attr_t * attr, void *(*start_routine) (void *), void *arg) { + int f_gc = 0; int i; int ret = 0; int status; + pthread_t gc_thread; pthread_t new_thread; pthread_attr_t pattr; void *stack; @@ -185,6 +187,12 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr, /* Lock the thread list: */ _lock_thread_list(); + /* + * Check if the garbage collector thread + * needs to be started. + */ + f_gc = (_thread_link_list == _thread_initial); + /* Add the thread to the linked list of all threads: */ new_thread->nxt = _thread_link_list; _thread_link_list = new_thread; @@ -197,6 +205,14 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr, /* Schedule the new user thread: */ _thread_kern_sched(NULL); + + /* + * Start a garbage collector thread + * if necessary. + */ + if (f_gc && pthread_create(&gc_thread,NULL, + _thread_gc,NULL) != 0) + PANIC("Can't create gc thread"); } } diff --git a/lib/libkse/thread/thr_exit.c b/lib/libkse/thread/thr_exit.c index 0083f2b01694..a5fc4001e414 100644 --- a/lib/libkse/thread/thr_exit.c +++ b/lib/libkse/thread/thr_exit.c @@ -133,56 +133,29 @@ pthread_exit(void *status) PTHREAD_NEW_STATE(pthread,PS_RUNNING); } - /* Lock the thread list: */ - _lock_thread_list(); - - /* Check if the running thread is at the head of the linked list: */ - if (_thread_link_list == _thread_run) { - /* There is no previous thread: */ - _thread_link_list = _thread_run->nxt; - } else { - /* Point to the first thread in the list: */ - pthread = _thread_link_list; - - /* - * Enter a loop to find the thread in the linked list before - * the running thread: - */ - while (pthread != NULL && pthread->nxt != _thread_run) { - /* Point to the next thread: */ - pthread = pthread->nxt; - } - - /* Check that a previous thread was found: */ - if (pthread != NULL) { - /* - * Point the previous thread to the one after the - * running thread: - */ - pthread->nxt = _thread_run->nxt; - } - } - - /* Unlock the thread list: */ - _unlock_thread_list(); - - /* Lock the dead thread list: */ - _lock_dead_thread_list(); - /* - * This thread will never run again. Add it to the list of dead - * threads: + * Lock the garbage collector mutex to ensure that the garbage + * collector is not using the dead thread list. */ - _thread_run->nxt = _thread_dead; + if (pthread_mutex_lock(&_gc_mutex) != 0) + PANIC("Cannot lock gc mutex"); + + /* Add this thread to the list of dead threads. */ + _thread_run->nxt_dead = _thread_dead; _thread_dead = _thread_run; - /* Unlock the dead thread list: */ - _unlock_dead_thread_list(); - /* - * The running thread is no longer in the thread link list so it will - * now die: + * Signal the garbage collector thread that there is something + * to clean up. */ + if (pthread_cond_signal(&_gc_cond) != 0) + PANIC("Cannot signal gc cond"); + + /* Unlock the garbage collector mutex: */ + if (pthread_mutex_unlock(&_gc_mutex) != 0) + PANIC("Cannot lock gc mutex"); + + /* This this thread will never be re-scheduled. */ _thread_kern_sched_state(PS_DEAD, __FILE__, __LINE__); /* This point should not be reached. */ diff --git a/lib/libkse/thread/thr_find_thread.c b/lib/libkse/thread/thr_find_thread.c index 99e302306d2a..e4a59a0d83f6 100644 --- a/lib/libkse/thread/thr_find_thread.c +++ b/lib/libkse/thread/thr_find_thread.c @@ -76,8 +76,12 @@ _find_dead_thread(pthread_t pthread) /* Invalid thread: */ return(EINVAL); - /* Lock the dead thread list: */ - _lock_dead_thread_list(); + /* + * Lock the garbage collector mutex to ensure that the garbage + * collector is not using the dead thread list. + */ + if (pthread_mutex_lock(&_gc_mutex) != 0) + PANIC("Cannot lock gc mutex"); /* Point to the first thread in the list: */ pthread1 = _thread_dead; @@ -85,11 +89,12 @@ _find_dead_thread(pthread_t pthread) /* Search for the thread to join to: */ while (pthread1 != NULL && pthread1 != pthread) { /* Point to the next thread: */ - pthread1 = pthread1->nxt; + pthread1 = pthread1->nxt_dead; } - /* Unlock the dead thread list: */ - _unlock_dead_thread_list(); + /* Unlock the garbage collector mutex: */ + if (pthread_mutex_unlock(&_gc_mutex) != 0) + PANIC("Cannot lock gc mutex"); /* Return zero if the thread exists: */ return ((pthread1 != NULL) ? 0:ESRCH); diff --git a/lib/libkse/thread/thr_init.c b/lib/libkse/thread/thr_init.c index 69b0fef563ae..50f3bef8973a 100644 --- a/lib/libkse/thread/thr_init.c +++ b/lib/libkse/thread/thr_init.c @@ -251,6 +251,11 @@ _thread_init(void) __set_dynamic_handler_allocator( dynamic_allocator_handler_fn ); #endif /* GCC_2_8_MADE_THREAD_AWARE */ + /* Initialise the garbage collector mutex and condition variable. */ + if (pthread_mutex_init(&_gc_mutex,NULL) != 0 || + pthread_cond_init(&_gc_cond,NULL) != 0) + PANIC("Failed to initialise garbage collector mutex or condvar"); + return; } diff --git a/lib/libkse/thread/thr_kern.c b/lib/libkse/thread/thr_kern.c index ce17cf85343c..da125cd3e645 100644 --- a/lib/libkse/thread/thr_kern.c +++ b/lib/libkse/thread/thr_kern.c @@ -29,7 +29,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: uthread_kern.c,v 1.11 1998/04/30 21:50:29 jb Exp $ + * $Id: uthread_kern.c,v 1.12 1998/09/07 21:55:01 alex Exp $ * */ #include @@ -62,8 +62,6 @@ _thread_kern_sched(struct sigcontext * scp) int prio = -1; pthread_t pthread; pthread_t pthread_h = NULL; - pthread_t pthread_nxt = NULL; - pthread_t pthread_prv = NULL; pthread_t pthread_s = NULL; struct itimerval itimer; struct timespec ts; @@ -113,85 +111,9 @@ __asm__("fnsave %0": :"m"(*fdata)); */ _dispatch_signals(); return; - } else { + } else /* Flag the jump buffer was the last state saved: */ _thread_run->sig_saved = 0; - } - - /* Point to the first dead thread (if there are any): */ - pthread = _thread_dead; - - /* There is no previous dead thread: */ - pthread_prv = NULL; - - /* Enter a loop to cleanup after dead threads: */ - while (pthread != NULL) { - /* Save a pointer to the next thread: */ - pthread_nxt = pthread->nxt; - - /* Check if this thread is one which is running: */ - if (pthread == _thread_run || pthread == _thread_initial) { - /* - * Don't destroy the running thread or the initial - * thread. - */ - pthread_prv = pthread; - } - /* - * Check if this thread has detached: - */ - else if ((pthread->attr.flags & PTHREAD_DETACHED) != 0) { - /* Check if there is no previous dead thread: */ - if (pthread_prv == NULL) { - /* - * The dead thread is at the head of the - * list: - */ - _thread_dead = pthread_nxt; - } else { - /* - * The dead thread is not at the head of the - * list: - */ - pthread_prv->nxt = pthread->nxt; - } - - /* - * Check if the stack was not specified by the caller - * to pthread_create and has not been destroyed yet: - */ - if (pthread->attr.stackaddr_attr == NULL && pthread->stack != NULL) { - /* Free the stack of the dead thread: */ - free(pthread->stack); - } - /* Free the memory allocated to the thread structure: */ - free(pthread); - } else { - /* - * This thread has not detached, so do not destroy - * it: - */ - pthread_prv = pthread; - - /* - * Check if the stack was not specified by the caller - * to pthread_create and has not been destroyed yet: - */ - if (pthread->attr.stackaddr_attr == NULL && pthread->stack != NULL) { - /* Free the stack of the dead thread: */ - free(pthread->stack); - - /* - * NULL the stack pointer now that the memory - * has been freed: - */ - pthread->stack = NULL; - } - } - - /* Point to the next thread: */ - pthread = pthread_nxt; - } /* * Enter a the scheduling loop that finds the next thread that is diff --git a/lib/libpthread/thread/Makefile.inc b/lib/libpthread/thread/Makefile.inc index 0c5c5822d714..004982a8b6bc 100644 --- a/lib/libpthread/thread/Makefile.inc +++ b/lib/libpthread/thread/Makefile.inc @@ -1,4 +1,4 @@ -# $Id: Makefile.inc,v 1.14 1998/09/07 19:01:43 alex Exp $ +# $Id: Makefile.inc,v 1.15 1998/09/12 22:03:20 dt Exp $ # uthread sources .PATH: ${.CURDIR}/uthread @@ -40,6 +40,7 @@ SRCS+= \ uthread_fstat.c \ uthread_fstatfs.c \ uthread_fsync.c \ + uthread_gc.c \ uthread_getdirentries.c \ uthread_getpeername.c \ uthread_getprio.c \ diff --git a/lib/libpthread/thread/thr_create.c b/lib/libpthread/thread/thr_create.c index 7603dd3a30c9..c7ddeb2a59c0 100644 --- a/lib/libpthread/thread/thr_create.c +++ b/lib/libpthread/thread/thr_create.c @@ -46,9 +46,11 @@ int pthread_create(pthread_t * thread, const pthread_attr_t * attr, void *(*start_routine) (void *), void *arg) { + int f_gc = 0; int i; int ret = 0; int status; + pthread_t gc_thread; pthread_t new_thread; pthread_attr_t pattr; void *stack; @@ -185,6 +187,12 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr, /* Lock the thread list: */ _lock_thread_list(); + /* + * Check if the garbage collector thread + * needs to be started. + */ + f_gc = (_thread_link_list == _thread_initial); + /* Add the thread to the linked list of all threads: */ new_thread->nxt = _thread_link_list; _thread_link_list = new_thread; @@ -197,6 +205,14 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr, /* Schedule the new user thread: */ _thread_kern_sched(NULL); + + /* + * Start a garbage collector thread + * if necessary. + */ + if (f_gc && pthread_create(&gc_thread,NULL, + _thread_gc,NULL) != 0) + PANIC("Can't create gc thread"); } } diff --git a/lib/libpthread/thread/thr_exit.c b/lib/libpthread/thread/thr_exit.c index 0083f2b01694..a5fc4001e414 100644 --- a/lib/libpthread/thread/thr_exit.c +++ b/lib/libpthread/thread/thr_exit.c @@ -133,56 +133,29 @@ pthread_exit(void *status) PTHREAD_NEW_STATE(pthread,PS_RUNNING); } - /* Lock the thread list: */ - _lock_thread_list(); - - /* Check if the running thread is at the head of the linked list: */ - if (_thread_link_list == _thread_run) { - /* There is no previous thread: */ - _thread_link_list = _thread_run->nxt; - } else { - /* Point to the first thread in the list: */ - pthread = _thread_link_list; - - /* - * Enter a loop to find the thread in the linked list before - * the running thread: - */ - while (pthread != NULL && pthread->nxt != _thread_run) { - /* Point to the next thread: */ - pthread = pthread->nxt; - } - - /* Check that a previous thread was found: */ - if (pthread != NULL) { - /* - * Point the previous thread to the one after the - * running thread: - */ - pthread->nxt = _thread_run->nxt; - } - } - - /* Unlock the thread list: */ - _unlock_thread_list(); - - /* Lock the dead thread list: */ - _lock_dead_thread_list(); - /* - * This thread will never run again. Add it to the list of dead - * threads: + * Lock the garbage collector mutex to ensure that the garbage + * collector is not using the dead thread list. */ - _thread_run->nxt = _thread_dead; + if (pthread_mutex_lock(&_gc_mutex) != 0) + PANIC("Cannot lock gc mutex"); + + /* Add this thread to the list of dead threads. */ + _thread_run->nxt_dead = _thread_dead; _thread_dead = _thread_run; - /* Unlock the dead thread list: */ - _unlock_dead_thread_list(); - /* - * The running thread is no longer in the thread link list so it will - * now die: + * Signal the garbage collector thread that there is something + * to clean up. */ + if (pthread_cond_signal(&_gc_cond) != 0) + PANIC("Cannot signal gc cond"); + + /* Unlock the garbage collector mutex: */ + if (pthread_mutex_unlock(&_gc_mutex) != 0) + PANIC("Cannot lock gc mutex"); + + /* This this thread will never be re-scheduled. */ _thread_kern_sched_state(PS_DEAD, __FILE__, __LINE__); /* This point should not be reached. */ diff --git a/lib/libpthread/thread/thr_find_thread.c b/lib/libpthread/thread/thr_find_thread.c index 99e302306d2a..e4a59a0d83f6 100644 --- a/lib/libpthread/thread/thr_find_thread.c +++ b/lib/libpthread/thread/thr_find_thread.c @@ -76,8 +76,12 @@ _find_dead_thread(pthread_t pthread) /* Invalid thread: */ return(EINVAL); - /* Lock the dead thread list: */ - _lock_dead_thread_list(); + /* + * Lock the garbage collector mutex to ensure that the garbage + * collector is not using the dead thread list. + */ + if (pthread_mutex_lock(&_gc_mutex) != 0) + PANIC("Cannot lock gc mutex"); /* Point to the first thread in the list: */ pthread1 = _thread_dead; @@ -85,11 +89,12 @@ _find_dead_thread(pthread_t pthread) /* Search for the thread to join to: */ while (pthread1 != NULL && pthread1 != pthread) { /* Point to the next thread: */ - pthread1 = pthread1->nxt; + pthread1 = pthread1->nxt_dead; } - /* Unlock the dead thread list: */ - _unlock_dead_thread_list(); + /* Unlock the garbage collector mutex: */ + if (pthread_mutex_unlock(&_gc_mutex) != 0) + PANIC("Cannot lock gc mutex"); /* Return zero if the thread exists: */ return ((pthread1 != NULL) ? 0:ESRCH); diff --git a/lib/libpthread/thread/thr_gc.c b/lib/libpthread/thread/thr_gc.c new file mode 100644 index 000000000000..faf1b5d13b77 --- /dev/null +++ b/lib/libpthread/thread/thr_gc.c @@ -0,0 +1,272 @@ +/* + * Copyright (c) 1998 John Birrell + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + * + * Garbage collector thread. Frees memory allocated for dead threads. + * + */ +#include +#include +#include +#include +#include +#include "pthread_private.h" + +pthread_addr_t +_thread_gc(pthread_addr_t arg) +{ + int f_debug; + int f_done = 0; + int ret; + pthread_t pthread; + pthread_t pthread_cln; + pthread_t pthread_nxt; + pthread_t pthread_prv; + struct timespec abstime; + void *p_stack; + + /* Set a debug flag based on an environment variable. */ + f_debug = (getenv("LIBC_R_DEBUG") == NULL); + + /* Set the name of this thread. */ + pthread_set_name_np(_thread_run,"GC"); + + while (!f_done) { + /* Check if debugging this application. */ + if (f_debug) + /* Dump thread info to file. */ + _thread_dump_info(); + + /* Lock the thread list: */ + _lock_thread_list(); + + /* Check if this is the last running thread: */ + if (_thread_link_list == _thread_run && + _thread_link_list->nxt == NULL) + /* + * This is the last thread, so it can exit + * now. + */ + f_done = 1; + + /* Unlock the thread list: */ + _unlock_thread_list(); + + /* + * Lock the garbage collector mutex which ensures that + * this thread sees another thread exit: + */ + if (pthread_mutex_lock(&_gc_mutex) != 0) + PANIC("Cannot lock gc mutex"); + + /* No stack of thread structure to free yet: */ + p_stack = NULL; + pthread_cln = NULL; + + /* Point to the first dead thread (if there are any): */ + pthread = _thread_dead; + + /* There is no previous dead thread: */ + pthread_prv = NULL; + + /* + * Enter a loop to search for the first dead thread that + * has memory to free. + */ + while (p_stack == NULL && pthread_cln == NULL && + pthread != NULL) { + /* Save a pointer to the next thread: */ + pthread_nxt = pthread->nxt_dead; + + /* Check if the initial thread: */ + if (pthread == _thread_initial) + /* Don't destroy the initial thread. */ + pthread_prv = pthread; + + /* + * Check if this thread has detached: + */ + else if ((pthread->attr.flags & + PTHREAD_DETACHED) != 0) { + /* + * Check if there is no previous dead + * thread: + */ + if (pthread_prv == NULL) + /* + * The dead thread is at the head + * of the list: + */ + _thread_dead = pthread_nxt; + else + /* + * The dead thread is not at the + * head of the list: + */ + pthread_prv->nxt_dead = + pthread->nxt_dead; + + /* + * Check if the stack was not specified by + * the caller to pthread_create and has not + * been destroyed yet: + */ + if (pthread->attr.stackaddr_attr == NULL && + pthread->stack != NULL) { + /* + * Point to the stack that must + * be freed outside the locks: + */ + p_stack = pthread->stack; + } + + /* + * Point to the thread structure that must + * be freed outside the locks: + */ + pthread_cln = pthread; + } else { + /* + * This thread has not detached, so do + * not destroy it: + */ + pthread_prv = pthread; + + /* + * Check if the stack was not specified by + * the caller to pthread_create and has not + * been destroyed yet: + */ + if (pthread->attr.stackaddr_attr == NULL && + pthread->stack != NULL) { + /* + * Point to the stack that must + * be freed outside the locks: + */ + p_stack = pthread->stack; + + /* + * NULL the stack pointer now + * that the memory has been freed: + */ + pthread->stack = NULL; + } + } + + /* Point to the next thread: */ + pthread = pthread_nxt; + } + + /* + * Check if this is not the last thread and there is no + * memory to free this time around. + */ + if (!f_done && p_stack == NULL && pthread_cln == NULL) { + /* Get the current time. */ + if (clock_gettime(CLOCK_REALTIME,&abstime) != 0) + PANIC("gc cannot get time"); + + /* + * Do a backup poll in 10 seconds if no threads + * die before then. + */ + abstime.tv_sec += 10; + + /* + * Wait for a signal from a dying thread or a + * timeout (for a backup poll). + */ + if ((ret = pthread_cond_timedwait(&_gc_cond, + &_gc_mutex, &abstime)) != 0 && ret != ETIMEDOUT) + PANIC("gc cannot wait for a signal"); + } + + /* Unlock the garbage collector mutex: */ + if (pthread_mutex_unlock(&_gc_mutex) != 0) + PANIC("Cannot unlock gc mutex"); + + /* + * If there is memory to free, do it now. The call to + * free() might block, so this must be done outside the + * locks. + */ + if (p_stack != NULL) + free(p_stack); + if (pthread_cln != NULL) { + /* Lock the thread list: */ + _lock_thread_list(); + + /* + * Check if the thread is at the head of the + * linked list. + */ + if (_thread_link_list == pthread_cln) + /* There is no previous thread: */ + _thread_link_list = pthread_cln->nxt; + else { + /* Point to the first thread in the list: */ + pthread = _thread_link_list; + + /* + * Enter a loop to find the thread in the + * linked list before the thread that is + * about to be freed. + */ + while (pthread != NULL && + pthread->nxt != pthread_cln) + /* Point to the next thread: */ + pthread = pthread->nxt; + + /* Check that a previous thread was found: */ + if (pthread != NULL) { + /* + * Point the previous thread to + * the one after the thread being + * freed: + */ + pthread->nxt = pthread_cln->nxt; + } + } + + /* Unlock the thread list: */ + _unlock_thread_list(); + + /* + * Free the memory allocated for the thread + * structure. + */ + free(pthread_cln); + } + + } + return (NULL); +} diff --git a/lib/libpthread/thread/thr_init.c b/lib/libpthread/thread/thr_init.c index 69b0fef563ae..50f3bef8973a 100644 --- a/lib/libpthread/thread/thr_init.c +++ b/lib/libpthread/thread/thr_init.c @@ -251,6 +251,11 @@ _thread_init(void) __set_dynamic_handler_allocator( dynamic_allocator_handler_fn ); #endif /* GCC_2_8_MADE_THREAD_AWARE */ + /* Initialise the garbage collector mutex and condition variable. */ + if (pthread_mutex_init(&_gc_mutex,NULL) != 0 || + pthread_cond_init(&_gc_cond,NULL) != 0) + PANIC("Failed to initialise garbage collector mutex or condvar"); + return; } diff --git a/lib/libpthread/thread/thr_kern.c b/lib/libpthread/thread/thr_kern.c index ce17cf85343c..da125cd3e645 100644 --- a/lib/libpthread/thread/thr_kern.c +++ b/lib/libpthread/thread/thr_kern.c @@ -29,7 +29,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: uthread_kern.c,v 1.11 1998/04/30 21:50:29 jb Exp $ + * $Id: uthread_kern.c,v 1.12 1998/09/07 21:55:01 alex Exp $ * */ #include @@ -62,8 +62,6 @@ _thread_kern_sched(struct sigcontext * scp) int prio = -1; pthread_t pthread; pthread_t pthread_h = NULL; - pthread_t pthread_nxt = NULL; - pthread_t pthread_prv = NULL; pthread_t pthread_s = NULL; struct itimerval itimer; struct timespec ts; @@ -113,85 +111,9 @@ __asm__("fnsave %0": :"m"(*fdata)); */ _dispatch_signals(); return; - } else { + } else /* Flag the jump buffer was the last state saved: */ _thread_run->sig_saved = 0; - } - - /* Point to the first dead thread (if there are any): */ - pthread = _thread_dead; - - /* There is no previous dead thread: */ - pthread_prv = NULL; - - /* Enter a loop to cleanup after dead threads: */ - while (pthread != NULL) { - /* Save a pointer to the next thread: */ - pthread_nxt = pthread->nxt; - - /* Check if this thread is one which is running: */ - if (pthread == _thread_run || pthread == _thread_initial) { - /* - * Don't destroy the running thread or the initial - * thread. - */ - pthread_prv = pthread; - } - /* - * Check if this thread has detached: - */ - else if ((pthread->attr.flags & PTHREAD_DETACHED) != 0) { - /* Check if there is no previous dead thread: */ - if (pthread_prv == NULL) { - /* - * The dead thread is at the head of the - * list: - */ - _thread_dead = pthread_nxt; - } else { - /* - * The dead thread is not at the head of the - * list: - */ - pthread_prv->nxt = pthread->nxt; - } - - /* - * Check if the stack was not specified by the caller - * to pthread_create and has not been destroyed yet: - */ - if (pthread->attr.stackaddr_attr == NULL && pthread->stack != NULL) { - /* Free the stack of the dead thread: */ - free(pthread->stack); - } - /* Free the memory allocated to the thread structure: */ - free(pthread); - } else { - /* - * This thread has not detached, so do not destroy - * it: - */ - pthread_prv = pthread; - - /* - * Check if the stack was not specified by the caller - * to pthread_create and has not been destroyed yet: - */ - if (pthread->attr.stackaddr_attr == NULL && pthread->stack != NULL) { - /* Free the stack of the dead thread: */ - free(pthread->stack); - - /* - * NULL the stack pointer now that the memory - * has been freed: - */ - pthread->stack = NULL; - } - } - - /* Point to the next thread: */ - pthread = pthread_nxt; - } /* * Enter a the scheduling loop that finds the next thread that is