summaryrefslogtreecommitdiffstats
path: root/lib/libpthread
diff options
context:
space:
mode:
authorjasone <jasone@FreeBSD.org>1999-07-05 00:35:19 +0000
committerjasone <jasone@FreeBSD.org>1999-07-05 00:35:19 +0000
commit6f85900affc95409ce87ebfce319b6f0a911a95c (patch)
tree55bd23172185419206e51b736aeb4824ee2c7666 /lib/libpthread
parenta84623740cbcddc83d3ae0e49ad184b469302d78 (diff)
downloadFreeBSD-src-6f85900affc95409ce87ebfce319b6f0a911a95c.zip
FreeBSD-src-6f85900affc95409ce87ebfce319b6f0a911a95c.tar.gz
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.
Diffstat (limited to 'lib/libpthread')
-rw-r--r--lib/libpthread/Makefile7
-rw-r--r--lib/libpthread/thread/thr_create.c58
-rw-r--r--lib/libpthread/thread/thr_gc.c32
-rw-r--r--lib/libpthread/thread/thr_init.c43
-rw-r--r--lib/libpthread/thread/thr_private.h46
5 files changed, 164 insertions, 22 deletions
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 <errno.h>
#include <stdlib.h>
@@ -37,6 +37,10 @@
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
+#ifdef _PTHREAD_GSTACK
+#include <sys/types.h>
+#include <sys/mman.h>
+#endif
#ifdef _THREAD_SAFE
#include <machine/reg.h>
#include <pthread.h>
@@ -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 <time.h>
#include <unistd.h>
#include <sys/types.h>
+#ifdef _PTHREAD_GSTACK
+#include <sys/types.h>
+#include <sys/mman.h>
+#endif
#include <pthread.h>
#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 <sys/sysctl.h>
#include <sys/time.h>
#include <sys/ttycom.h>
+#ifdef _PTHREAD_GSTACK
+#include <sys/types.h>
+#include <sys/mman.h>
+#endif
#ifdef _THREAD_SAFE
#include <machine/reg.h>
#include <pthread.h>
@@ -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
OpenPOWER on IntegriCloud