From 6f85900affc95409ce87ebfce319b6f0a911a95c Mon Sep 17 00:00:00 2001 From: jasone Date: Mon, 5 Jul 1999 00:35:19 +0000 Subject: Use growable stacks for thread stacks that are the default stack size. Cache discarded default thread stacks for use in subsequent thread creations. Create a red zone at the end of each stack (including the initial thread stack), with the hope of causing a segfault if a stack overflows. To activate these modifications, add -D_PTHREAD_GSTACK to CFLAGS in src/lib/libc_r/Makefile. Since the modifications depend on the VM_STACK kernel option, I'm not sure how to safely use growable stacks by default. Testing, as well as algorithmic and stylistic comments are welcome. --- lib/libpthread/Makefile | 7 +++-- lib/libpthread/thread/thr_create.c | 58 ++++++++++++++++++++++++++++++++++++- lib/libpthread/thread/thr_gc.c | 32 ++++++++++++++++++-- lib/libpthread/thread/thr_init.c | 43 +++++++++++++++++---------- lib/libpthread/thread/thr_private.h | 46 +++++++++++++++++++++++++++-- 5 files changed, 164 insertions(+), 22 deletions(-) (limited to 'lib/libpthread') diff --git a/lib/libpthread/Makefile b/lib/libpthread/Makefile index d79dcd7..ae767be 100644 --- a/lib/libpthread/Makefile +++ b/lib/libpthread/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.14 1998/12/10 20:27:52 jb Exp $ +# $Id: Makefile,v 1.15 1999/06/20 08:32:37 jb Exp $ # # All library objects contain rcsid strings by default; they may be # excluded as a space-saving measure. To produce a library that does @@ -13,7 +13,10 @@ CFLAGS+=-DPTHREAD_KERNEL -D_THREAD_SAFE -I${.CURDIR}/uthread # Uncomment this if you want libc_r to contain debug information for # thread locking. -#CFLAGS+=-D_LOCK_DEBUG +CFLAGS+=-D_LOCK_DEBUG + +# Uncomment this if you want libc_r to use growable stacks. +CFLAGS+= -D_PTHREAD_GSTACK AINC= -I${.CURDIR}/../libc/${MACHINE_ARCH} -I${.CURDIR}/uthread PRECIOUSLIB= yes diff --git a/lib/libpthread/thread/thr_create.c b/lib/libpthread/thread/thr_create.c index f5e0b63..1b12c7b 100644 --- a/lib/libpthread/thread/thr_create.c +++ b/lib/libpthread/thread/thr_create.c @@ -29,7 +29,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id$ + * $Id: uthread_create.c,v 1.13 1999/06/20 08:28:14 jb Exp $ */ #include #include @@ -37,6 +37,10 @@ #include #include #include +#ifdef _PTHREAD_GSTACK +#include +#include +#endif #ifdef _THREAD_SAFE #include #include @@ -77,12 +81,64 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr, /* Check if a stack was specified in the thread attributes: */ if ((stack = pattr->stackaddr_attr) != NULL) { } +#ifdef _PTHREAD_GSTACK + /* Allocate memory for a default-size stack: */ + else if (pattr->stacksize_attr == PTHREAD_STACK_DEFAULT) { + struct stack * spare_stack; + + /* Allocate or re-use a default-size stack. */ + + /* Use the garbage collector mutex for synchronization + * of the spare stack list. + * + * XXX This may not be ideal. */ + if (pthread_mutex_lock(&_gc_mutex) != 0) + PANIC("Cannot lock gc mutex"); + + if (NULL != (spare_stack = SLIST_FIRST(&_stackq))) { + /* Use the spare stack. */ + SLIST_REMOVE_HEAD(&_stackq, qe); + stack = sizeof(struct stack) + (void *) spare_stack - PTHREAD_STACK_DEFAULT; + } else { + /* Allocate a new stack. */ + stack = _next_stack + PTHREAD_STACK_GUARD; + /* Even if stack allocation fails, we don't want to try to use this location again, so unconditionally + * decrement _next_stack. Under normal operating conditions, the most likely reason for an mmap() + * error is a stack overflow of the adjacent thread stack. */ + _next_stack -= (PTHREAD_STACK_DEFAULT + PTHREAD_STACK_GUARD); + + /* Red zone: */ + if (MAP_FAILED == mmap(_next_stack, PTHREAD_STACK_GUARD, 0, MAP_ANON, -1, 0)) { + ret = EAGAIN; + free(new_thread); + } + /* Stack: */ + else if (MAP_FAILED == mmap(stack, PTHREAD_STACK_DEFAULT, PROT_READ | PROT_WRITE, MAP_STACK, -1, 0)) { + ret = EAGAIN; + munmap(_next_stack, PTHREAD_STACK_GUARD); + free(new_thread); + } + } + + /* Unlock the garbage collector mutex. */ + if (pthread_mutex_unlock(&_gc_mutex) != 0) + PANIC("Cannot unlock gc mutex"); + } + /* The user wants a stack of a particular size. Lets hope they really know what they want, and simply malloc the + * stack. */ + else if ((stack = (void *) malloc(pattr->stacksize_attr)) == NULL) { + /* Insufficient memory to create a thread: */ + ret = EAGAIN; + free(new_thread); + } +#else /* Allocate memory for the stack: */ else if ((stack = (void *) malloc(pattr->stacksize_attr)) == NULL) { /* Insufficient memory to create a thread: */ ret = EAGAIN; free(new_thread); } +#endif /* Check for errors: */ if (ret != 0) { } else { diff --git a/lib/libpthread/thread/thr_gc.c b/lib/libpthread/thread/thr_gc.c index e29e29b..a8e05ea 100644 --- a/lib/libpthread/thread/thr_gc.c +++ b/lib/libpthread/thread/thr_gc.c @@ -29,7 +29,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: uthread_gc.c,v 1.3 1999/03/23 05:07:55 jb Exp $ + * $Id: uthread_gc.c,v 1.4 1999/06/20 08:28:25 jb Exp $ * * Garbage collector thread. Frees memory allocated for dead threads. * @@ -38,6 +38,10 @@ #include #include #include +#ifdef _PTHREAD_GSTACK +#include +#include +#endif #include #include "pthread_private.h" @@ -132,11 +136,23 @@ _thread_gc(pthread_addr_t arg) */ if (pthread->attr.stackaddr_attr == NULL && pthread->stack != NULL) { +#ifdef _PTHREAD_GSTACK + if (pthread->attr.stacksize_attr == PTHREAD_STACK_DEFAULT) { + /* Default-size stack. Cache it: */ + struct stack * spare_stack = (pthread->stack + PTHREAD_STACK_DEFAULT + - sizeof(struct stack)); + SLIST_INSERT_HEAD(&_stackq, spare_stack, qe); + } else { + /* Non-standard stack size. free() it outside the locks: */ + p_stack = pthread->stack; + } +#else /* * Point to the stack that must * be freed outside the locks: */ p_stack = pthread->stack; +#endif } /* @@ -156,12 +172,24 @@ _thread_gc(pthread_addr_t arg) */ if (pthread->attr.stackaddr_attr == NULL && pthread->stack != NULL) { +#ifdef _PTHREAD_GSTACK + if (pthread->attr.stacksize_attr == PTHREAD_STACK_DEFAULT) { + /* Default-size stack. Cache it: */ + struct stack * spare_stack = (pthread->stack + PTHREAD_STACK_DEFAULT + - sizeof(struct stack)); + SLIST_INSERT_HEAD(&_stackq, spare_stack, qe); + } else { + /* Non-standard stack size. free() it outside the locks: */ + p_stack = pthread->stack; + } +#else /* * Point to the stack that must * be freed outside the locks: */ p_stack = pthread->stack; - +#endif + /* * NULL the stack pointer now * that the memory has been freed: diff --git a/lib/libpthread/thread/thr_init.c b/lib/libpthread/thread/thr_init.c index 8b907d7..9528e00 100644 --- a/lib/libpthread/thread/thr_init.c +++ b/lib/libpthread/thread/thr_init.c @@ -29,7 +29,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: uthread_init.c,v 1.11 1999/06/20 08:28:28 jb Exp $ + * $Id: uthread_init.c,v 1.12 1999/06/23 15:01:21 dt Exp $ */ /* Allocate space for global thread variables here: */ @@ -45,6 +45,10 @@ #include #include #include +#ifdef _PTHREAD_GSTACK +#include +#include +#endif #ifdef _THREAD_SAFE #include #include @@ -57,8 +61,8 @@ extern void __set_dynamic_handler_allocator(dynamic_handler_allocator); static pthread_key_t except_head_key; typedef struct { - void **__dynamic_handler_chain; - void *top_elt[2]; + void **__dynamic_handler_chain; + void *top_elt[2]; } except_struct; static void ***dynamic_allocator_handler_fn() @@ -103,26 +107,26 @@ _thread_init(void) * Setup a new session for this process which is * assumed to be running as root. */ - if (setsid() == -1) + if (setsid() == -1) PANIC("Can't set session ID"); - if (revoke(_PATH_CONSOLE) != 0) + if (revoke(_PATH_CONSOLE) != 0) PANIC("Can't revoke console"); - if ((fd = _thread_sys_open(_PATH_CONSOLE, O_RDWR)) < 0) + if ((fd = _thread_sys_open(_PATH_CONSOLE, O_RDWR)) < 0) PANIC("Can't open console"); - if (setlogin("root") == -1) + if (setlogin("root") == -1) PANIC("Can't set login to root"); - if (_thread_sys_ioctl(fd,TIOCSCTTY, (char *) NULL) == -1) + if (_thread_sys_ioctl(fd,TIOCSCTTY, (char *) NULL) == -1) PANIC("Can't set controlling terminal"); - if (_thread_sys_dup2(fd,0) == -1 || - _thread_sys_dup2(fd,1) == -1 || - _thread_sys_dup2(fd,2) == -1) + if (_thread_sys_dup2(fd,0) == -1 || + _thread_sys_dup2(fd,1) == -1 || + _thread_sys_dup2(fd,2) == -1) PANIC("Can't dup2"); } /* Get the standard I/O flags before messing with them : */ for (i = 0; i < 3; i++) if ((_pthread_stdio_flags[i] = - _thread_sys_fcntl(i,F_GETFL, NULL)) == -1) + _thread_sys_fcntl(i,F_GETFL, NULL)) == -1) PANIC("Cannot get stdio flags"); /* @@ -178,6 +182,15 @@ _thread_init(void) /* Initialize the scheduling switch hook routine: */ _sched_switch_hook = NULL; +#ifdef _PTHREAD_GSTACK + /* Initialize the thread stack cache: */ + SLIST_INIT(&_stackq); + + /* Create the red zone for the main stack. */ + if (MAP_FAILED == mmap((void *) PTHREAD_STACK_TOP - PTHREAD_STACK_INITIAL, PTHREAD_STACK_GUARD, 0, MAP_ANON, -1, 0)) { + PANIC("Cannot allocate red zone for initial thread"); + } +#endif /* * Write a magic value to the thread structure * to help identify valid ones: @@ -228,7 +241,7 @@ _thread_init(void) /* Get the signal handler details: */ else if (_thread_sys_sigaction(i, NULL, - &_thread_sigact[i - 1]) != 0) { + &_thread_sigact[i - 1]) != 0) { /* * Abort this process if signal * initialisation fails: @@ -296,7 +309,7 @@ _thread_init(void) (_thread_fd_table_init(1) != 0) || (_thread_fd_table_init(2) != 0)) { PANIC("Cannot initialize stdio file descriptor " - "table entries"); + "table entries"); } } } @@ -304,7 +317,7 @@ _thread_init(void) #ifdef GCC_2_8_MADE_THREAD_AWARE /* Create the thread-specific data for the exception linked list. */ if(pthread_key_create(&except_head_key, NULL) != 0) - PANIC("Failed to create thread specific execption head"); + PANIC("Failed to create thread specific execption head"); /* Setup the gcc exception handler per thread. */ __set_dynamic_handler_allocator( dynamic_allocator_handler_fn ); diff --git a/lib/libpthread/thread/thr_private.h b/lib/libpthread/thread/thr_private.h index 036ea80..1da2151 100644 --- a/lib/libpthread/thread/thr_private.h +++ b/lib/libpthread/thread/thr_private.h @@ -31,7 +31,7 @@ * * Private thread definitions for the uthread kernel. * - * $Id$ + * $Id: pthread_private.h,v 1.20 1999/06/20 08:28:08 jb Exp $ */ #ifndef _PTHREAD_PRIVATE_H @@ -335,6 +335,24 @@ struct pthread_attr { * Miscellaneous definitions. */ #define PTHREAD_STACK_DEFAULT 65536 +#ifdef _PTHREAD_GSTACK +/* Size of red zone at the end of each stack. */ +#define PTHREAD_STACK_GUARD 4096 +/* Maximum size of initial thread's stack. This perhaps deserves to be larger + * than the stacks of other threads, since legacy applications are likely to run + * almost entirely on this stack. */ +#define PTHREAD_STACK_INITIAL 0x100000 +/* Address immediately beyond the beginning of the initial thread stack. */ +#if defined(__FreeBSD__) +# if defined(__alpha__) +# define PTHREAD_STACK_TOP 0x160022000 +# else +# define PTHREAD_STACK_TOP 0xbfbde000 +# endif +#else +# error "Don't recognize this operating system!" +#endif +#endif #define PTHREAD_DEFAULT_PRIORITY 64 #define PTHREAD_MAX_PRIORITY 126 #define PTHREAD_MIN_PRIORITY 0 @@ -655,6 +673,13 @@ struct pthread { int lineno; /* Source line number. */ }; +#ifdef _PTHREAD_GSTACK +/* Spare thread stack. */ +struct stack { + SLIST_ENTRY(stack) qe; /* Queue entry for this stack. */ +}; +#endif + /* * Global variables for the uthread kernel. */ @@ -864,6 +889,24 @@ SCLASS pthread_switch_routine_t _sched_switch_hook #endif ; +#ifdef _PTHREAD_GSTACK +/* Spare stack queue. Stacks of default size are cached in order to reduce + * thread creation time. Spare stacks are used in LIFO order to increase cache + * locality. */ +SCLASS SLIST_HEAD(, stack) _stackq; + +/* Base address of next unallocated default-size stack. Stacks are allocated + * contiguously, starting below the beginning of the main stack. When a new + * stack is created, a guard page is created just above it in order to (usually) + * detect attempts by the adjacent stack to trounce the next thread stack. */ +SCLASS void * _next_stack +#ifdef GLOBAL_PTHREAD_PRIVATE +/* main stack top - main stack size - stack size - (red zone + main stack red zone) */ += (void *) PTHREAD_STACK_TOP - PTHREAD_STACK_INITIAL - PTHREAD_STACK_DEFAULT - (2 * PTHREAD_STACK_GUARD) +#endif +; +#endif + /* Used for _PTHREADS_INVARIANTS checking. */ SCLASS int _thread_kern_new_state #ifdef GLOBAL_PTHREAD_PRIVATE @@ -871,7 +914,6 @@ SCLASS int _thread_kern_new_state #endif ; - /* Undefine the storage class specifier: */ #undef SCLASS -- cgit v1.1