Implement pthread_attr_[gs]etguardsize(). Non-default-size stacks used to
be malloc()ed, but they are now allocated using mmap(), just as the default-size stacks are. A separate cache of stacks is kept for non-default-size stacks. Collaboration with: deischen
This commit is contained in:
parent
50ea040994
commit
aa33517e94
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=80021
@ -192,12 +192,15 @@ __BEGIN_DECLS
|
||||
int pthread_attr_destroy __P((pthread_attr_t *));
|
||||
int pthread_attr_getstacksize __P((const pthread_attr_t *,
|
||||
size_t *));
|
||||
int pthread_attr_getguardsize __P((const pthread_attr_t *,
|
||||
size_t *));
|
||||
int pthread_attr_getstackaddr __P((const pthread_attr_t *,
|
||||
void **));
|
||||
int pthread_attr_getdetachstate __P((const pthread_attr_t *,
|
||||
int *));
|
||||
int pthread_attr_init __P((pthread_attr_t *));
|
||||
int pthread_attr_setstacksize __P((pthread_attr_t *, size_t));
|
||||
int pthread_attr_setguardsize __P((pthread_attr_t *, size_t));
|
||||
int pthread_attr_setstackaddr __P((pthread_attr_t *, void *));
|
||||
int pthread_attr_setdetachstate __P((pthread_attr_t *, int));
|
||||
void pthread_cleanup_pop __P((int));
|
||||
|
@ -56,6 +56,7 @@ MAN+= pthread_attr.3 \
|
||||
MLINKS+= \
|
||||
pthread_attr.3 pthread_attr_destroy.3 \
|
||||
pthread_attr.3 pthread_attr_getdetachstate.3 \
|
||||
pthread_attr.3 pthread_attr_getguardsize.3 \
|
||||
pthread_attr.3 pthread_attr_getinheritsched.3 \
|
||||
pthread_attr.3 pthread_attr_getschedparam.3 \
|
||||
pthread_attr.3 pthread_attr_getschedpolicy.3 \
|
||||
@ -64,6 +65,7 @@ MLINKS+= \
|
||||
pthread_attr.3 pthread_attr_getstacksize.3 \
|
||||
pthread_attr.3 pthread_attr_init.3 \
|
||||
pthread_attr.3 pthread_attr_setdetachstate.3 \
|
||||
pthread_attr.3 pthread_attr_setguardsize.3 \
|
||||
pthread_attr.3 pthread_attr_setinheritsched.3 \
|
||||
pthread_attr.3 pthread_attr_setschedparam.3 \
|
||||
pthread_attr.3 pthread_attr_setschedpolicy.3 \
|
||||
|
@ -34,6 +34,8 @@
|
||||
.Nm pthread_attr_destroy ,
|
||||
.Nm pthread_attr_setstacksize ,
|
||||
.Nm pthread_attr_getstacksize ,
|
||||
.Nm pthread_attr_setguardsize ,
|
||||
.Nm pthread_attr_getguardsize ,
|
||||
.Nm pthread_attr_setstackaddr ,
|
||||
.Nm pthread_attr_getstackaddr ,
|
||||
.Nm pthread_attr_setdetachstate ,
|
||||
@ -60,6 +62,10 @@
|
||||
.Ft int
|
||||
.Fn pthread_attr_getstacksize "const pthread_attr_t *attr" "size_t *stacksize"
|
||||
.Ft int
|
||||
.Fn pthread_attr_setguardsize "pthread_attr_t *attr" "size_t guardsize"
|
||||
.Ft int
|
||||
.Fn pthread_attr_getguardsize "const pthread_attr_t *attr" "size_t *guardsize"
|
||||
.Ft int
|
||||
.Fn pthread_attr_setstackaddr "pthread_attr_t *attr" "void *stackaddr"
|
||||
.Ft int
|
||||
.Fn pthread_attr_getstackaddr "const pthread_attr_t *attr" "void **stackaddr"
|
||||
|
@ -12,10 +12,10 @@ CTESTS := hello_d.c hello_s.c join_leak_d.c mutex_d.c sem_d.c sigsuspend_d.c \
|
||||
|
||||
# C programs that are used internally by the tests. The build system merely
|
||||
# compiles these.
|
||||
BTESTS := hello_b.c
|
||||
BTESTS := guard_b.c hello_b.c
|
||||
|
||||
# Tests written in perl.
|
||||
PTESTS := propagate_s.pl
|
||||
PTESTS := guard_s.pl propagate_s.pl
|
||||
|
||||
# Munge the file lists to their final executable names (strip the .c).
|
||||
CTESTS := $(CTESTS:R)
|
||||
|
150
lib/libc_r/test/guard_b.c
Normal file
150
lib/libc_r/test/guard_b.c
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Jason Evans <jasone@freebsd.org>.
|
||||
* 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(s), this list of conditions and the following disclaimer
|
||||
* unmodified other than the allowable addition of one or more
|
||||
* copyright notices.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice(s), this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
* Test thread stack guard functionality.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define FRAME_SIZE 1024
|
||||
#define FRAME_OVERHEAD 40
|
||||
|
||||
struct args
|
||||
{
|
||||
void *top; /* Top of thread's initial stack frame. */
|
||||
int cur; /* Recursion depth. */
|
||||
int max; /* Maximum recursion depth. */
|
||||
};
|
||||
|
||||
void *
|
||||
recurse(void *args)
|
||||
{
|
||||
int top;
|
||||
struct args *parms = (struct args *)args;
|
||||
char filler[FRAME_SIZE - FRAME_OVERHEAD];
|
||||
|
||||
/* Touch the memory in this stack frame. */
|
||||
top = 0xa5;
|
||||
memset(filler, 0xa5, sizeof(filler));
|
||||
|
||||
if (parms->top == NULL) {
|
||||
/* Initial stack frame. */
|
||||
parms->top = (void*)⊤
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure frame size is what we expect. Getting this right involves
|
||||
* hand tweaking, so just print a warning rather than aborting.
|
||||
*/
|
||||
if (parms->top - (void *)&top != FRAME_SIZE * parms->cur) {
|
||||
fprintf(stderr, "Stack size (%d) != expected (%d), frame %d\n",
|
||||
parms->top - (void *)&top, FRAME_SIZE * parms->cur,
|
||||
parms->cur);
|
||||
}
|
||||
|
||||
parms->cur++;
|
||||
if (parms->cur < parms->max)
|
||||
recurse(args);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
size_t def_stacksize, def_guardsize;
|
||||
size_t stacksize, guardsize;
|
||||
pthread_t thread;
|
||||
pthread_attr_t attr;
|
||||
struct args args;
|
||||
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "Usage: guard_b <stacksize> <guardsize>\n");
|
||||
exit(1);
|
||||
}
|
||||
fprintf(stderr, "Test begin\n");
|
||||
|
||||
stacksize = strtoul(argv[1], NULL, 10);
|
||||
guardsize = strtoul(argv[2], NULL, 10);
|
||||
|
||||
assert(pthread_attr_init(&attr) == 0);
|
||||
/*
|
||||
* Exercise the attribute APIs more thoroughly than is strictly
|
||||
* necessary for the meat of this test program.
|
||||
*/
|
||||
assert(pthread_attr_getstacksize(&attr, &def_stacksize) == 0);
|
||||
assert(pthread_attr_getguardsize(&attr, &def_guardsize) == 0);
|
||||
if (def_stacksize != stacksize) {
|
||||
assert(pthread_attr_setstacksize(&attr, stacksize) == 0);
|
||||
assert(pthread_attr_getstacksize(&attr, &def_stacksize) == 0);
|
||||
assert(def_stacksize == stacksize);
|
||||
}
|
||||
if (def_guardsize != guardsize) {
|
||||
assert(pthread_attr_setguardsize(&attr, guardsize) == 0);
|
||||
assert(pthread_attr_getguardsize(&attr, &def_guardsize) == 0);
|
||||
assert(def_guardsize >= guardsize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a thread that will come just short of overflowing the thread
|
||||
* stack. We need to leave a bit of breathing room in case the thread
|
||||
* is context switched, and we also have to take care not to call any
|
||||
* functions in the deepest stack frame.
|
||||
*/
|
||||
args.top = NULL;
|
||||
args.cur = 0;
|
||||
args.max = (stacksize / FRAME_SIZE) - 1;
|
||||
fprintf(stderr, "No overflow:\n");
|
||||
assert(pthread_create(&thread, &attr, recurse, &args) == 0);
|
||||
assert(pthread_join(thread, NULL) == 0);
|
||||
|
||||
/*
|
||||
* Create a thread that will barely of overflow the thread stack. This
|
||||
* should cause a segfault.
|
||||
*/
|
||||
args.top = NULL;
|
||||
args.cur = 0;
|
||||
args.max = (stacksize / FRAME_SIZE) + 1;
|
||||
fprintf(stderr, "Overflow:\n");
|
||||
assert(pthread_create(&thread, &attr, recurse, &args) == 0);
|
||||
assert(pthread_join(thread, NULL) == 0);
|
||||
|
||||
/* Not reached. */
|
||||
fprintf(stderr, "Unexpected success\n");
|
||||
abort();
|
||||
|
||||
return 0;
|
||||
}
|
3
lib/libc_r/test/guard_b.exp
Normal file
3
lib/libc_r/test/guard_b.exp
Normal file
@ -0,0 +1,3 @@
|
||||
Test begin
|
||||
No overflow:
|
||||
Overflow:
|
69
lib/libc_r/test/guard_s.pl
Executable file
69
lib/libc_r/test/guard_s.pl
Executable file
@ -0,0 +1,69 @@
|
||||
#!/usr/bin/perl -w
|
||||
#
|
||||
# Copyright (C) 2001 Jason Evans <jasone@freebsd.org>.
|
||||
# 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(s), this list of conditions and the following disclaimer
|
||||
# unmodified other than the allowable addition of one or more
|
||||
# copyright notices.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice(s), this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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.
|
||||
#
|
||||
# $FreeBSD$
|
||||
#
|
||||
# Test thread stack guard functionality. The C test program needs to be driven
|
||||
# by this script because it segfaults when the stack guard is hit.
|
||||
#
|
||||
|
||||
print "1..30\n";
|
||||
|
||||
$i = 0;
|
||||
# Iterates 10 times.
|
||||
for ($stacksize = 65536; $stacksize < 131072; $stacksize += 7168)
|
||||
{
|
||||
# Iterates 3 times (1024, 4096, 7168).
|
||||
for ($guardsize = 1024; $guardsize < 8192; $guardsize += 3072)
|
||||
{
|
||||
$i++;
|
||||
|
||||
print "stacksize: $stacksize, guardsize: $guardsize\n";
|
||||
|
||||
`./guard_b $stacksize $guardsize >guard_b.out 2>&1`;
|
||||
|
||||
if (! -f "./guard_b.out")
|
||||
{
|
||||
print "not ok $i\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
`diff guard_b.exp guard_b.out >guard_b.diff 2>&1`;
|
||||
if ($?)
|
||||
{
|
||||
# diff returns non-zero if there is a difference.
|
||||
print "not ok $i\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
print "ok $i\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ SRCS+= \
|
||||
uthread_attr_destroy.c \
|
||||
uthread_attr_init.c \
|
||||
uthread_attr_getdetachstate.c \
|
||||
uthread_attr_getguardsize.c \
|
||||
uthread_attr_getinheritsched.c \
|
||||
uthread_attr_getschedparam.c \
|
||||
uthread_attr_getschedpolicy.c \
|
||||
@ -21,6 +22,7 @@ SRCS+= \
|
||||
uthread_attr_getstacksize.c \
|
||||
uthread_attr_setcreatesuspend_np.c \
|
||||
uthread_attr_setdetachstate.c \
|
||||
uthread_attr_setguardsize.c \
|
||||
uthread_attr_setinheritsched.c \
|
||||
uthread_attr_setschedparam.c \
|
||||
uthread_attr_setschedpolicy.c \
|
||||
@ -124,6 +126,7 @@ SRCS+= \
|
||||
uthread_socketpair.c \
|
||||
uthread_spec.c \
|
||||
uthread_spinlock.c \
|
||||
uthread_stack.c \
|
||||
uthread_suspend_np.c \
|
||||
uthread_switch_np.c \
|
||||
uthread_system.c \
|
||||
|
@ -389,6 +389,7 @@ struct pthread_attr {
|
||||
void (*cleanup_attr) ();
|
||||
void *stackaddr_attr;
|
||||
size_t stacksize_attr;
|
||||
size_t guardsize_attr;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -414,13 +415,13 @@ enum pthread_susp {
|
||||
*/
|
||||
#define PTHREAD_STACK_DEFAULT 65536
|
||||
/*
|
||||
* Size of red zone at the end of each stack. In actuality, this "red zone" is
|
||||
* merely an unmapped region, except in the case of the initial stack. Since
|
||||
* mmap() makes it possible to specify the maximum growth of a MAP_STACK region,
|
||||
* an unmapped gap between thread stacks achieves the same effect as explicitly
|
||||
* mapped red zones.
|
||||
* Size of default red zone at the end of each stack. In actuality, this "red
|
||||
* zone" is merely an unmapped region, except in the case of the initial stack.
|
||||
* Since mmap() makes it possible to specify the maximum growth of a MAP_STACK
|
||||
* region, an unmapped gap between thread stacks achieves the same effect as
|
||||
* explicitly mapped red zones.
|
||||
*/
|
||||
#define PTHREAD_STACK_GUARD PAGE_SIZE
|
||||
#define PTHREAD_GUARD_DEFAULT PAGE_SIZE
|
||||
|
||||
/*
|
||||
* Maximum size of initial thread's stack. This perhaps deserves to be larger
|
||||
@ -875,11 +876,6 @@ struct pthread {
|
||||
int lineno; /* Source line number. */
|
||||
};
|
||||
|
||||
/* Spare thread stack. */
|
||||
struct stack {
|
||||
SLIST_ENTRY(stack) qe; /* Queue entry for this stack. */
|
||||
};
|
||||
|
||||
/*
|
||||
* Global variables for the uthread kernel.
|
||||
*/
|
||||
@ -992,8 +988,9 @@ SCLASS struct pthread *_thread_initial
|
||||
/* Default thread attributes: */
|
||||
SCLASS struct pthread_attr pthread_attr_default
|
||||
#ifdef GLOBAL_PTHREAD_PRIVATE
|
||||
= { SCHED_RR, 0, TIMESLICE_USEC, PTHREAD_DEFAULT_PRIORITY, PTHREAD_CREATE_RUNNING,
|
||||
PTHREAD_CREATE_JOINABLE, NULL, NULL, NULL, PTHREAD_STACK_DEFAULT };
|
||||
= { SCHED_RR, 0, TIMESLICE_USEC, PTHREAD_DEFAULT_PRIORITY,
|
||||
PTHREAD_CREATE_RUNNING, PTHREAD_CREATE_JOINABLE, NULL, NULL, NULL,
|
||||
PTHREAD_STACK_DEFAULT, PTHREAD_GUARD_DEFAULT };
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
@ -1141,31 +1138,6 @@ SCLASS pthread_switch_routine_t _sched_switch_hook
|
||||
#endif
|
||||
;
|
||||
|
||||
/*
|
||||
* 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, red zone}. Stacks are
|
||||
* allocated contiguously, starting below the bottom of the main stack. When a
|
||||
* new stack is created, a red zone is created (actually, the red zone is simply
|
||||
* left unmapped) below the bottom of the stack, such that the stack will not be
|
||||
* able to grow all the way to the top of the next stack. This isn't
|
||||
* fool-proof. It is possible for a stack to grow by a large amount, such that
|
||||
* it grows into the next stack, and as long as the memory within the red zone
|
||||
* is never accessed, nothing will prevent one thread stack from trouncing all
|
||||
* over the next.
|
||||
*/
|
||||
SCLASS void * _next_stack
|
||||
#ifdef GLOBAL_PTHREAD_PRIVATE
|
||||
/* main stack top - main stack size - stack size - (red zone + main stack red zone) */
|
||||
= (void *) USRSTACK - PTHREAD_STACK_INITIAL - PTHREAD_STACK_DEFAULT - (2 * PTHREAD_STACK_GUARD)
|
||||
#endif
|
||||
;
|
||||
|
||||
/*
|
||||
* Declare the kernel scheduler jump buffer and stack:
|
||||
*/
|
||||
@ -1210,6 +1182,8 @@ void _fd_lock_backout(pthread_t);
|
||||
int _find_thread(pthread_t);
|
||||
struct pthread *_get_curthread(void);
|
||||
void _set_curthread(struct pthread *);
|
||||
void *_thread_stack_alloc(size_t, size_t);
|
||||
void _thread_stack_free(void *, size_t, size_t);
|
||||
int _thread_create(pthread_t *,const pthread_attr_t *,void *(*start_routine)(void *),void *,pthread_t);
|
||||
int _thread_fd_lock(int, int, struct timespec *);
|
||||
int _thread_fd_lock_debug(int, int, struct timespec *,char *fname,int lineno);
|
||||
|
52
lib/libc_r/uthread/uthread_attr_getguardsize.c
Normal file
52
lib/libc_r/uthread/uthread_attr_getguardsize.c
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Jason Evans <jasone@freebsd.org>.
|
||||
* 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(s), this list of conditions and the following disclaimer
|
||||
* unmodified other than the allowable addition of one or more
|
||||
* copyright notices.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice(s), this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
__weak_reference(_pthread_attr_getguardsize, pthread_attr_getguardsize);
|
||||
|
||||
int
|
||||
_pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Check for invalid arguments: */
|
||||
if (attr == NULL || *attr == NULL || guardsize == NULL)
|
||||
ret = EINVAL;
|
||||
else {
|
||||
/* Return the guard size: */
|
||||
*guardsize = (*attr)->guardsize_attr;
|
||||
ret = 0;
|
||||
}
|
||||
return(ret);
|
||||
}
|
57
lib/libc_r/uthread/uthread_attr_setguardsize.c
Normal file
57
lib/libc_r/uthread/uthread_attr_setguardsize.c
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Jason Evans <jasone@freebsd.org>.
|
||||
* 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(s), this list of conditions and the following disclaimer
|
||||
* unmodified other than the allowable addition of one or more
|
||||
* copyright notices.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice(s), this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
__weak_reference(_pthread_attr_setguardsize, pthread_attr_setguardsize);
|
||||
|
||||
int
|
||||
_pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Check for invalid arguments. */
|
||||
if (attr == NULL || *attr == NULL)
|
||||
ret = EINVAL;
|
||||
else {
|
||||
/* Round guardsize up to the nearest multiple of PAGE_SIZE. */
|
||||
if (guardsize % PAGE_SIZE != 0)
|
||||
guardsize = ((guardsize / PAGE_SIZE) + 1) * PAGE_SIZE;
|
||||
|
||||
/* Save the stack size. */
|
||||
(*attr)->guardsize_attr = guardsize;
|
||||
ret = 0;
|
||||
}
|
||||
return(ret);
|
||||
}
|
@ -38,8 +38,6 @@
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/mman.h>
|
||||
#include <machine/reg.h>
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
@ -99,68 +97,15 @@ _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) {
|
||||
}
|
||||
/* 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.
|
||||
*/
|
||||
if (pthread_mutex_lock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot lock gc mutex");
|
||||
|
||||
if ((spare_stack = SLIST_FIRST(&_stackq)) != NULL) {
|
||||
/* Use the spare stack. */
|
||||
SLIST_REMOVE_HEAD(&_stackq, qe);
|
||||
|
||||
/* Unlock the garbage collector mutex. */
|
||||
if (pthread_mutex_unlock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot unlock gc mutex");
|
||||
|
||||
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);
|
||||
|
||||
/* Unlock the garbage collector mutex. */
|
||||
if (pthread_mutex_unlock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot unlock gc mutex");
|
||||
|
||||
/* Stack: */
|
||||
if (mmap(stack, PTHREAD_STACK_DEFAULT,
|
||||
PROT_READ | PROT_WRITE, MAP_STACK,
|
||||
-1, 0) == MAP_FAILED) {
|
||||
ret = EAGAIN;
|
||||
free(new_thread);
|
||||
}
|
||||
/* Allocate a stack: */
|
||||
else {
|
||||
stack = _thread_stack_alloc(pattr->stacksize_attr,
|
||||
pattr->guardsize_attr);
|
||||
if (stack == NULL) {
|
||||
ret = EAGAIN;
|
||||
free(new_thread);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/* Check for errors: */
|
||||
if (ret != 0) {
|
||||
|
@ -31,6 +31,7 @@
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#include <sys/param.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
@ -220,28 +221,16 @@ _fork(void)
|
||||
static void
|
||||
free_thread_resources(struct pthread *thread)
|
||||
{
|
||||
struct stack *spare_stack;
|
||||
|
||||
/* Check to see if the threads library allocated the stack. */
|
||||
if ((thread->attr.stackaddr_attr == NULL) && (thread->stack != NULL)) {
|
||||
if (thread->attr.stacksize_attr != PTHREAD_STACK_DEFAULT) {
|
||||
/*
|
||||
* The threads library malloc()'d the stack;
|
||||
* just free() it.
|
||||
*/
|
||||
free(thread->stack);
|
||||
} else {
|
||||
/*
|
||||
* This stack was allocated from the main threads
|
||||
* stack; cache it for future use. Since this is
|
||||
* being called from fork, we are currently single
|
||||
* threaded so there is no need to protect the
|
||||
* queue insertion.
|
||||
*/
|
||||
spare_stack = (thread->stack + PTHREAD_STACK_DEFAULT -
|
||||
sizeof(struct stack));
|
||||
SLIST_INSERT_HEAD(&_stackq, spare_stack, qe);
|
||||
}
|
||||
/*
|
||||
* Since this is being called from fork, we are currently single
|
||||
* threaded so there is no need to protect the call to
|
||||
* _thread_stack_free() with _gc_mutex.
|
||||
*/
|
||||
_thread_stack_free(thread->stack, thread->attr.stacksize_attr,
|
||||
thread->attr.guardsize_attr);
|
||||
}
|
||||
|
||||
if (thread->specific_data != NULL)
|
||||
|
@ -34,13 +34,12 @@
|
||||
* Garbage collector thread. Frees memory allocated for dead threads.
|
||||
*
|
||||
*/
|
||||
#include <sys/param.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
@ -123,39 +122,20 @@ _thread_gc(pthread_addr_t arg)
|
||||
* Check if this thread has detached:
|
||||
*/
|
||||
else if ((pthread->attr.flags &
|
||||
PTHREAD_DETACHED) != 0) {
|
||||
PTHREAD_DETACHED) != 0) {
|
||||
/* Remove this thread from the dead list: */
|
||||
TAILQ_REMOVE(&_dead_list, pthread, dle);
|
||||
|
||||
/*
|
||||
* Check if the stack was not specified by
|
||||
* the caller to pthread_create and has not
|
||||
* the caller to pthread_create() and has not
|
||||
* been destroyed yet:
|
||||
*/
|
||||
if (pthread->attr.stackaddr_attr == NULL &&
|
||||
pthread->stack != NULL) {
|
||||
if (pthread->attr.stacksize_attr
|
||||
== PTHREAD_STACK_DEFAULT) {
|
||||
/*
|
||||
* Default-size stack. Cache
|
||||
* it:
|
||||
*/
|
||||
struct stack *spare_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;
|
||||
}
|
||||
_thread_stack_free(pthread->stack,
|
||||
pthread->attr.stacksize_attr,
|
||||
pthread->attr.guardsize_attr);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -170,37 +150,18 @@ _thread_gc(pthread_addr_t arg)
|
||||
* not destroy it.
|
||||
*
|
||||
* Check if the stack was not specified by
|
||||
* the caller to pthread_create and has not
|
||||
* the caller to pthread_create() and has not
|
||||
* been destroyed yet:
|
||||
*/
|
||||
if (pthread->attr.stackaddr_attr == NULL &&
|
||||
pthread->stack != NULL) {
|
||||
if (pthread->attr.stacksize_attr
|
||||
== PTHREAD_STACK_DEFAULT) {
|
||||
/*
|
||||
* Default-size stack. Cache
|
||||
* it:
|
||||
*/
|
||||
struct stack *spare_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;
|
||||
}
|
||||
_thread_stack_free(pthread->stack,
|
||||
pthread->attr.stacksize_attr,
|
||||
pthread->attr.guardsize_attr);
|
||||
|
||||
/*
|
||||
* NULL the stack pointer now
|
||||
* that the memory has been freed:
|
||||
* NULL the stack pointer now that the
|
||||
* memory has been freed:
|
||||
*/
|
||||
pthread->stack = NULL;
|
||||
}
|
||||
|
@ -268,9 +268,6 @@ _thread_init(void)
|
||||
memcpy((void *) &_thread_initial->attr, &pthread_attr_default,
|
||||
sizeof(struct pthread_attr));
|
||||
|
||||
/* Initialize the thread stack cache: */
|
||||
SLIST_INIT(&_stackq);
|
||||
|
||||
/*
|
||||
* Create a red zone below the main stack. All other stacks are
|
||||
* constrained to a maximum size by the paramters passed to
|
||||
@ -279,7 +276,7 @@ _thread_init(void)
|
||||
* thread stack that is just beyond.
|
||||
*/
|
||||
if (mmap((void *) USRSTACK - PTHREAD_STACK_INITIAL -
|
||||
PTHREAD_STACK_GUARD, PTHREAD_STACK_GUARD, 0, MAP_ANON,
|
||||
PTHREAD_GUARD_DEFAULT, PTHREAD_GUARD_DEFAULT, 0, MAP_ANON,
|
||||
-1, 0) == MAP_FAILED)
|
||||
PANIC("Cannot allocate red zone for initial thread");
|
||||
|
||||
|
235
lib/libc_r/uthread/uthread_stack.c
Normal file
235
lib/libc_r/uthread/uthread_stack.c
Normal file
@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Copyright (c) 2001 Daniel Eischen <deischen@freebsd.org>
|
||||
* Copyright (c) 2000-2001 Jason Evans <jasone@freebsd.org>
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/user.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
/* Spare thread stack. */
|
||||
struct stack {
|
||||
LIST_ENTRY(stack) qe; /* Stack queue linkage. */
|
||||
size_t stacksize; /* Stack size (rounded up). */
|
||||
size_t guardsize; /* Guard size. */
|
||||
void *stackaddr; /* Stack address. */
|
||||
};
|
||||
|
||||
/*
|
||||
* Default sized (stack and guard) spare stack queue. Stacks are cached to
|
||||
* avoid additional complexity managing mmap()ed stack regions. Spare stacks
|
||||
* are used in LIFO order to increase cache locality.
|
||||
*/
|
||||
static LIST_HEAD(, stack) _dstackq = LIST_HEAD_INITIALIZER(_dstackq);
|
||||
|
||||
/*
|
||||
* Miscellaneous sized (non-default stack and/or guard) spare stack queue.
|
||||
* Stacks are cached to avoid additional complexity managing mmap()ed stack
|
||||
* regions. This list is unordered, since ordering on both stack size and guard
|
||||
* size would be more trouble than it's worth. Stacks are allocated from this
|
||||
* cache on a first size match basis.
|
||||
*/
|
||||
static LIST_HEAD(, stack) _mstackq = LIST_HEAD_INITIALIZER(_mstackq);
|
||||
|
||||
/**
|
||||
* Base address of the last stack allocated (including its red zone, if there is
|
||||
* one). Stacks are allocated contiguously, starting beyond the top of the main
|
||||
* stack. When a new stack is created, a red zone is typically created
|
||||
* (actually, the red zone is simply left unmapped) above the top of the stack,
|
||||
* such that the stack will not be able to grow all the way to the bottom of the
|
||||
* next stack. This isn't fool-proof. It is possible for a stack to grow by a
|
||||
* large amount, such that it grows into the next stack, and as long as the
|
||||
* memory within the red zone is never accessed, nothing will prevent one thread
|
||||
* stack from trouncing all over the next.
|
||||
*
|
||||
* low memory
|
||||
* . . . . . . . . . . . . . . . . . .
|
||||
* | |
|
||||
* | stack 3 | start of 3rd thread stack
|
||||
* +-----------------------------------+
|
||||
* | |
|
||||
* | Red Zone (guard page) | red zone for 2nd thread
|
||||
* | |
|
||||
* +-----------------------------------+
|
||||
* | stack 2 - PTHREAD_STACK_DEFAULT | top of 2nd thread stack
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | stack 2 |
|
||||
* +-----------------------------------+ <-- start of 2nd thread stack
|
||||
* | |
|
||||
* | Red Zone | red zone for 1st thread
|
||||
* | |
|
||||
* +-----------------------------------+
|
||||
* | stack 1 - PTHREAD_STACK_DEFAULT | top of 1st thread stack
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | stack 1 |
|
||||
* +-----------------------------------+ <-- start of 1st thread stack
|
||||
* | | (initial value of last_stack)
|
||||
* | Red Zone |
|
||||
* | | red zone for main thread
|
||||
* +-----------------------------------+
|
||||
* | USRSTACK - PTHREAD_STACK_INITIAL | top of main thread stack
|
||||
* | | ^
|
||||
* | | |
|
||||
* | | |
|
||||
* | | | stack growth
|
||||
* | |
|
||||
* +-----------------------------------+ <-- start of main thread stack
|
||||
* (USRSTACK)
|
||||
* high memory
|
||||
*
|
||||
*/
|
||||
static void * last_stack = (void *) USRSTACK - PTHREAD_STACK_INITIAL
|
||||
- PTHREAD_GUARD_DEFAULT;
|
||||
|
||||
void *
|
||||
_thread_stack_alloc(size_t stacksize, size_t guardsize)
|
||||
{
|
||||
void *stack = NULL;
|
||||
struct stack *spare_stack;
|
||||
size_t stack_size;
|
||||
|
||||
/*
|
||||
* Round up stack size to nearest multiple of PAGE_SIZE, so that mmap()
|
||||
* will work. If the stack size is not an even multiple, we end up
|
||||
* initializing things such that there is unused space above the
|
||||
* beginning of the stack, so the stack sits snugly against its guard.
|
||||
*/
|
||||
if (stacksize % PAGE_SIZE != 0)
|
||||
stack_size = ((stacksize / PAGE_SIZE) + 1) * PAGE_SIZE;
|
||||
else
|
||||
stack_size = stacksize;
|
||||
|
||||
/*
|
||||
* If the stack and guard sizes are default, try to allocate a stack
|
||||
* from the default-size stack cache:
|
||||
*/
|
||||
if (stack_size == PTHREAD_STACK_DEFAULT &&
|
||||
guardsize == PTHREAD_GUARD_DEFAULT) {
|
||||
/*
|
||||
* Use the garbage collector mutex for synchronization of the
|
||||
* spare stack list.
|
||||
*/
|
||||
if (pthread_mutex_lock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot lock gc mutex");
|
||||
|
||||
if ((spare_stack = LIST_FIRST(&_dstackq)) != NULL) {
|
||||
/* Use the spare stack. */
|
||||
LIST_REMOVE(spare_stack, qe);
|
||||
stack = spare_stack->stackaddr;
|
||||
}
|
||||
|
||||
/* Unlock the garbage collector mutex. */
|
||||
if (pthread_mutex_unlock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot unlock gc mutex");
|
||||
}
|
||||
/*
|
||||
* The user specified a non-default stack and/or guard size, so try to
|
||||
* allocate a stack from the non-default size stack cache, using the
|
||||
* rounded up stack size (stack_size) in the search:
|
||||
*/
|
||||
else {
|
||||
/*
|
||||
* Use the garbage collector mutex for synchronization of the
|
||||
* spare stack list.
|
||||
*/
|
||||
if (pthread_mutex_lock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot lock gc mutex");
|
||||
|
||||
LIST_FOREACH(spare_stack, &_mstackq, qe) {
|
||||
if (spare_stack->stacksize == stack_size &&
|
||||
spare_stack->guardsize == guardsize) {
|
||||
LIST_REMOVE(spare_stack, qe);
|
||||
stack = spare_stack->stackaddr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Unlock the garbage collector mutex. */
|
||||
if (pthread_mutex_unlock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot unlock gc mutex");
|
||||
}
|
||||
|
||||
/* Check if a stack was not allocated from a stack cache: */
|
||||
if (stack == NULL) {
|
||||
|
||||
/* Allocate a new stack. */
|
||||
|
||||
stack = last_stack - stack_size;
|
||||
|
||||
/*
|
||||
* Even if stack allocation fails, we don't want to try to use
|
||||
* this location again, so unconditionally decrement
|
||||
* last_stack. Under normal operating conditions, the most
|
||||
* likely reason for an mmap() error is a stack overflow of the
|
||||
* adjacent thread stack.
|
||||
*/
|
||||
last_stack -= (stack_size + guardsize);
|
||||
|
||||
/* Stack: */
|
||||
if (mmap(stack, stack_size, PROT_READ | PROT_WRITE, MAP_STACK,
|
||||
-1, 0) == MAP_FAILED)
|
||||
stack = NULL;
|
||||
}
|
||||
|
||||
return (stack);
|
||||
}
|
||||
|
||||
/* This function must be called with _gc_mutex held. */
|
||||
void
|
||||
_thread_stack_free(void *stack, size_t stacksize, size_t guardsize)
|
||||
{
|
||||
struct stack *spare_stack;
|
||||
|
||||
spare_stack = (stack + stacksize - sizeof(struct stack));
|
||||
/* Round stacksize up to nearest multiple of PAGE_SIZE. */
|
||||
if (stacksize % PAGE_SIZE != 0) {
|
||||
spare_stack->stacksize = ((stacksize / PAGE_SIZE) + 1) *
|
||||
PAGE_SIZE;
|
||||
} else
|
||||
spare_stack->stacksize = stacksize;
|
||||
spare_stack->guardsize = guardsize;
|
||||
spare_stack->stackaddr = stack;
|
||||
|
||||
if (spare_stack->stacksize == PTHREAD_STACK_DEFAULT &&
|
||||
spare_stack->guardsize == PTHREAD_GUARD_DEFAULT) {
|
||||
/* Default stack/guard size. */
|
||||
LIST_INSERT_HEAD(&_dstackq, spare_stack, qe);
|
||||
} else {
|
||||
/* Non-default stack/guard size. */
|
||||
LIST_INSERT_HEAD(&_mstackq, spare_stack, qe);
|
||||
}
|
||||
}
|
@ -12,10 +12,10 @@ CTESTS := hello_d.c hello_s.c join_leak_d.c mutex_d.c sem_d.c sigsuspend_d.c \
|
||||
|
||||
# C programs that are used internally by the tests. The build system merely
|
||||
# compiles these.
|
||||
BTESTS := hello_b.c
|
||||
BTESTS := guard_b.c hello_b.c
|
||||
|
||||
# Tests written in perl.
|
||||
PTESTS := propagate_s.pl
|
||||
PTESTS := guard_s.pl propagate_s.pl
|
||||
|
||||
# Munge the file lists to their final executable names (strip the .c).
|
||||
CTESTS := $(CTESTS:R)
|
||||
|
150
lib/libkse/test/guard_b.c
Normal file
150
lib/libkse/test/guard_b.c
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Jason Evans <jasone@freebsd.org>.
|
||||
* 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(s), this list of conditions and the following disclaimer
|
||||
* unmodified other than the allowable addition of one or more
|
||||
* copyright notices.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice(s), this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
* Test thread stack guard functionality.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define FRAME_SIZE 1024
|
||||
#define FRAME_OVERHEAD 40
|
||||
|
||||
struct args
|
||||
{
|
||||
void *top; /* Top of thread's initial stack frame. */
|
||||
int cur; /* Recursion depth. */
|
||||
int max; /* Maximum recursion depth. */
|
||||
};
|
||||
|
||||
void *
|
||||
recurse(void *args)
|
||||
{
|
||||
int top;
|
||||
struct args *parms = (struct args *)args;
|
||||
char filler[FRAME_SIZE - FRAME_OVERHEAD];
|
||||
|
||||
/* Touch the memory in this stack frame. */
|
||||
top = 0xa5;
|
||||
memset(filler, 0xa5, sizeof(filler));
|
||||
|
||||
if (parms->top == NULL) {
|
||||
/* Initial stack frame. */
|
||||
parms->top = (void*)⊤
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure frame size is what we expect. Getting this right involves
|
||||
* hand tweaking, so just print a warning rather than aborting.
|
||||
*/
|
||||
if (parms->top - (void *)&top != FRAME_SIZE * parms->cur) {
|
||||
fprintf(stderr, "Stack size (%d) != expected (%d), frame %d\n",
|
||||
parms->top - (void *)&top, FRAME_SIZE * parms->cur,
|
||||
parms->cur);
|
||||
}
|
||||
|
||||
parms->cur++;
|
||||
if (parms->cur < parms->max)
|
||||
recurse(args);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
size_t def_stacksize, def_guardsize;
|
||||
size_t stacksize, guardsize;
|
||||
pthread_t thread;
|
||||
pthread_attr_t attr;
|
||||
struct args args;
|
||||
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "Usage: guard_b <stacksize> <guardsize>\n");
|
||||
exit(1);
|
||||
}
|
||||
fprintf(stderr, "Test begin\n");
|
||||
|
||||
stacksize = strtoul(argv[1], NULL, 10);
|
||||
guardsize = strtoul(argv[2], NULL, 10);
|
||||
|
||||
assert(pthread_attr_init(&attr) == 0);
|
||||
/*
|
||||
* Exercise the attribute APIs more thoroughly than is strictly
|
||||
* necessary for the meat of this test program.
|
||||
*/
|
||||
assert(pthread_attr_getstacksize(&attr, &def_stacksize) == 0);
|
||||
assert(pthread_attr_getguardsize(&attr, &def_guardsize) == 0);
|
||||
if (def_stacksize != stacksize) {
|
||||
assert(pthread_attr_setstacksize(&attr, stacksize) == 0);
|
||||
assert(pthread_attr_getstacksize(&attr, &def_stacksize) == 0);
|
||||
assert(def_stacksize == stacksize);
|
||||
}
|
||||
if (def_guardsize != guardsize) {
|
||||
assert(pthread_attr_setguardsize(&attr, guardsize) == 0);
|
||||
assert(pthread_attr_getguardsize(&attr, &def_guardsize) == 0);
|
||||
assert(def_guardsize >= guardsize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a thread that will come just short of overflowing the thread
|
||||
* stack. We need to leave a bit of breathing room in case the thread
|
||||
* is context switched, and we also have to take care not to call any
|
||||
* functions in the deepest stack frame.
|
||||
*/
|
||||
args.top = NULL;
|
||||
args.cur = 0;
|
||||
args.max = (stacksize / FRAME_SIZE) - 1;
|
||||
fprintf(stderr, "No overflow:\n");
|
||||
assert(pthread_create(&thread, &attr, recurse, &args) == 0);
|
||||
assert(pthread_join(thread, NULL) == 0);
|
||||
|
||||
/*
|
||||
* Create a thread that will barely of overflow the thread stack. This
|
||||
* should cause a segfault.
|
||||
*/
|
||||
args.top = NULL;
|
||||
args.cur = 0;
|
||||
args.max = (stacksize / FRAME_SIZE) + 1;
|
||||
fprintf(stderr, "Overflow:\n");
|
||||
assert(pthread_create(&thread, &attr, recurse, &args) == 0);
|
||||
assert(pthread_join(thread, NULL) == 0);
|
||||
|
||||
/* Not reached. */
|
||||
fprintf(stderr, "Unexpected success\n");
|
||||
abort();
|
||||
|
||||
return 0;
|
||||
}
|
3
lib/libkse/test/guard_b.exp
Normal file
3
lib/libkse/test/guard_b.exp
Normal file
@ -0,0 +1,3 @@
|
||||
Test begin
|
||||
No overflow:
|
||||
Overflow:
|
69
lib/libkse/test/guard_s.pl
Normal file
69
lib/libkse/test/guard_s.pl
Normal file
@ -0,0 +1,69 @@
|
||||
#!/usr/bin/perl -w
|
||||
#
|
||||
# Copyright (C) 2001 Jason Evans <jasone@freebsd.org>.
|
||||
# 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(s), this list of conditions and the following disclaimer
|
||||
# unmodified other than the allowable addition of one or more
|
||||
# copyright notices.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice(s), this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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.
|
||||
#
|
||||
# $FreeBSD$
|
||||
#
|
||||
# Test thread stack guard functionality. The C test program needs to be driven
|
||||
# by this script because it segfaults when the stack guard is hit.
|
||||
#
|
||||
|
||||
print "1..30\n";
|
||||
|
||||
$i = 0;
|
||||
# Iterates 10 times.
|
||||
for ($stacksize = 65536; $stacksize < 131072; $stacksize += 7168)
|
||||
{
|
||||
# Iterates 3 times (1024, 4096, 7168).
|
||||
for ($guardsize = 1024; $guardsize < 8192; $guardsize += 3072)
|
||||
{
|
||||
$i++;
|
||||
|
||||
print "stacksize: $stacksize, guardsize: $guardsize\n";
|
||||
|
||||
`./guard_b $stacksize $guardsize >guard_b.out 2>&1`;
|
||||
|
||||
if (! -f "./guard_b.out")
|
||||
{
|
||||
print "not ok $i\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
`diff guard_b.exp guard_b.out >guard_b.diff 2>&1`;
|
||||
if ($?)
|
||||
{
|
||||
# diff returns non-zero if there is a difference.
|
||||
print "not ok $i\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
print "ok $i\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ SRCS+= \
|
||||
uthread_attr_destroy.c \
|
||||
uthread_attr_init.c \
|
||||
uthread_attr_getdetachstate.c \
|
||||
uthread_attr_getguardsize.c \
|
||||
uthread_attr_getinheritsched.c \
|
||||
uthread_attr_getschedparam.c \
|
||||
uthread_attr_getschedpolicy.c \
|
||||
@ -21,6 +22,7 @@ SRCS+= \
|
||||
uthread_attr_getstacksize.c \
|
||||
uthread_attr_setcreatesuspend_np.c \
|
||||
uthread_attr_setdetachstate.c \
|
||||
uthread_attr_setguardsize.c \
|
||||
uthread_attr_setinheritsched.c \
|
||||
uthread_attr_setschedparam.c \
|
||||
uthread_attr_setschedpolicy.c \
|
||||
@ -124,6 +126,7 @@ SRCS+= \
|
||||
uthread_socketpair.c \
|
||||
uthread_spec.c \
|
||||
uthread_spinlock.c \
|
||||
uthread_stack.c \
|
||||
uthread_suspend_np.c \
|
||||
uthread_switch_np.c \
|
||||
uthread_system.c \
|
||||
|
52
lib/libkse/thread/thr_attr_getguardsize.c
Normal file
52
lib/libkse/thread/thr_attr_getguardsize.c
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Jason Evans <jasone@freebsd.org>.
|
||||
* 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(s), this list of conditions and the following disclaimer
|
||||
* unmodified other than the allowable addition of one or more
|
||||
* copyright notices.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice(s), this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
__weak_reference(_pthread_attr_getguardsize, pthread_attr_getguardsize);
|
||||
|
||||
int
|
||||
_pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Check for invalid arguments: */
|
||||
if (attr == NULL || *attr == NULL || guardsize == NULL)
|
||||
ret = EINVAL;
|
||||
else {
|
||||
/* Return the guard size: */
|
||||
*guardsize = (*attr)->guardsize_attr;
|
||||
ret = 0;
|
||||
}
|
||||
return(ret);
|
||||
}
|
57
lib/libkse/thread/thr_attr_setguardsize.c
Normal file
57
lib/libkse/thread/thr_attr_setguardsize.c
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Jason Evans <jasone@freebsd.org>.
|
||||
* 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(s), this list of conditions and the following disclaimer
|
||||
* unmodified other than the allowable addition of one or more
|
||||
* copyright notices.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice(s), this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
__weak_reference(_pthread_attr_setguardsize, pthread_attr_setguardsize);
|
||||
|
||||
int
|
||||
_pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Check for invalid arguments. */
|
||||
if (attr == NULL || *attr == NULL)
|
||||
ret = EINVAL;
|
||||
else {
|
||||
/* Round guardsize up to the nearest multiple of PAGE_SIZE. */
|
||||
if (guardsize % PAGE_SIZE != 0)
|
||||
guardsize = ((guardsize / PAGE_SIZE) + 1) * PAGE_SIZE;
|
||||
|
||||
/* Save the stack size. */
|
||||
(*attr)->guardsize_attr = guardsize;
|
||||
ret = 0;
|
||||
}
|
||||
return(ret);
|
||||
}
|
@ -38,8 +38,6 @@
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/mman.h>
|
||||
#include <machine/reg.h>
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
@ -99,68 +97,15 @@ _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) {
|
||||
}
|
||||
/* 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.
|
||||
*/
|
||||
if (pthread_mutex_lock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot lock gc mutex");
|
||||
|
||||
if ((spare_stack = SLIST_FIRST(&_stackq)) != NULL) {
|
||||
/* Use the spare stack. */
|
||||
SLIST_REMOVE_HEAD(&_stackq, qe);
|
||||
|
||||
/* Unlock the garbage collector mutex. */
|
||||
if (pthread_mutex_unlock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot unlock gc mutex");
|
||||
|
||||
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);
|
||||
|
||||
/* Unlock the garbage collector mutex. */
|
||||
if (pthread_mutex_unlock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot unlock gc mutex");
|
||||
|
||||
/* Stack: */
|
||||
if (mmap(stack, PTHREAD_STACK_DEFAULT,
|
||||
PROT_READ | PROT_WRITE, MAP_STACK,
|
||||
-1, 0) == MAP_FAILED) {
|
||||
ret = EAGAIN;
|
||||
free(new_thread);
|
||||
}
|
||||
/* Allocate a stack: */
|
||||
else {
|
||||
stack = _thread_stack_alloc(pattr->stacksize_attr,
|
||||
pattr->guardsize_attr);
|
||||
if (stack == NULL) {
|
||||
ret = EAGAIN;
|
||||
free(new_thread);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/* Check for errors: */
|
||||
if (ret != 0) {
|
||||
|
@ -31,6 +31,7 @@
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#include <sys/param.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
@ -220,28 +221,16 @@ _fork(void)
|
||||
static void
|
||||
free_thread_resources(struct pthread *thread)
|
||||
{
|
||||
struct stack *spare_stack;
|
||||
|
||||
/* Check to see if the threads library allocated the stack. */
|
||||
if ((thread->attr.stackaddr_attr == NULL) && (thread->stack != NULL)) {
|
||||
if (thread->attr.stacksize_attr != PTHREAD_STACK_DEFAULT) {
|
||||
/*
|
||||
* The threads library malloc()'d the stack;
|
||||
* just free() it.
|
||||
*/
|
||||
free(thread->stack);
|
||||
} else {
|
||||
/*
|
||||
* This stack was allocated from the main threads
|
||||
* stack; cache it for future use. Since this is
|
||||
* being called from fork, we are currently single
|
||||
* threaded so there is no need to protect the
|
||||
* queue insertion.
|
||||
*/
|
||||
spare_stack = (thread->stack + PTHREAD_STACK_DEFAULT -
|
||||
sizeof(struct stack));
|
||||
SLIST_INSERT_HEAD(&_stackq, spare_stack, qe);
|
||||
}
|
||||
/*
|
||||
* Since this is being called from fork, we are currently single
|
||||
* threaded so there is no need to protect the call to
|
||||
* _thread_stack_free() with _gc_mutex.
|
||||
*/
|
||||
_thread_stack_free(thread->stack, thread->attr.stacksize_attr,
|
||||
thread->attr.guardsize_attr);
|
||||
}
|
||||
|
||||
if (thread->specific_data != NULL)
|
||||
|
@ -268,9 +268,6 @@ _thread_init(void)
|
||||
memcpy((void *) &_thread_initial->attr, &pthread_attr_default,
|
||||
sizeof(struct pthread_attr));
|
||||
|
||||
/* Initialize the thread stack cache: */
|
||||
SLIST_INIT(&_stackq);
|
||||
|
||||
/*
|
||||
* Create a red zone below the main stack. All other stacks are
|
||||
* constrained to a maximum size by the paramters passed to
|
||||
@ -279,7 +276,7 @@ _thread_init(void)
|
||||
* thread stack that is just beyond.
|
||||
*/
|
||||
if (mmap((void *) USRSTACK - PTHREAD_STACK_INITIAL -
|
||||
PTHREAD_STACK_GUARD, PTHREAD_STACK_GUARD, 0, MAP_ANON,
|
||||
PTHREAD_GUARD_DEFAULT, PTHREAD_GUARD_DEFAULT, 0, MAP_ANON,
|
||||
-1, 0) == MAP_FAILED)
|
||||
PANIC("Cannot allocate red zone for initial thread");
|
||||
|
||||
|
@ -389,6 +389,7 @@ struct pthread_attr {
|
||||
void (*cleanup_attr) ();
|
||||
void *stackaddr_attr;
|
||||
size_t stacksize_attr;
|
||||
size_t guardsize_attr;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -414,13 +415,13 @@ enum pthread_susp {
|
||||
*/
|
||||
#define PTHREAD_STACK_DEFAULT 65536
|
||||
/*
|
||||
* Size of red zone at the end of each stack. In actuality, this "red zone" is
|
||||
* merely an unmapped region, except in the case of the initial stack. Since
|
||||
* mmap() makes it possible to specify the maximum growth of a MAP_STACK region,
|
||||
* an unmapped gap between thread stacks achieves the same effect as explicitly
|
||||
* mapped red zones.
|
||||
* Size of default red zone at the end of each stack. In actuality, this "red
|
||||
* zone" is merely an unmapped region, except in the case of the initial stack.
|
||||
* Since mmap() makes it possible to specify the maximum growth of a MAP_STACK
|
||||
* region, an unmapped gap between thread stacks achieves the same effect as
|
||||
* explicitly mapped red zones.
|
||||
*/
|
||||
#define PTHREAD_STACK_GUARD PAGE_SIZE
|
||||
#define PTHREAD_GUARD_DEFAULT PAGE_SIZE
|
||||
|
||||
/*
|
||||
* Maximum size of initial thread's stack. This perhaps deserves to be larger
|
||||
@ -875,11 +876,6 @@ struct pthread {
|
||||
int lineno; /* Source line number. */
|
||||
};
|
||||
|
||||
/* Spare thread stack. */
|
||||
struct stack {
|
||||
SLIST_ENTRY(stack) qe; /* Queue entry for this stack. */
|
||||
};
|
||||
|
||||
/*
|
||||
* Global variables for the uthread kernel.
|
||||
*/
|
||||
@ -992,8 +988,9 @@ SCLASS struct pthread *_thread_initial
|
||||
/* Default thread attributes: */
|
||||
SCLASS struct pthread_attr pthread_attr_default
|
||||
#ifdef GLOBAL_PTHREAD_PRIVATE
|
||||
= { SCHED_RR, 0, TIMESLICE_USEC, PTHREAD_DEFAULT_PRIORITY, PTHREAD_CREATE_RUNNING,
|
||||
PTHREAD_CREATE_JOINABLE, NULL, NULL, NULL, PTHREAD_STACK_DEFAULT };
|
||||
= { SCHED_RR, 0, TIMESLICE_USEC, PTHREAD_DEFAULT_PRIORITY,
|
||||
PTHREAD_CREATE_RUNNING, PTHREAD_CREATE_JOINABLE, NULL, NULL, NULL,
|
||||
PTHREAD_STACK_DEFAULT, PTHREAD_GUARD_DEFAULT };
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
@ -1141,31 +1138,6 @@ SCLASS pthread_switch_routine_t _sched_switch_hook
|
||||
#endif
|
||||
;
|
||||
|
||||
/*
|
||||
* 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, red zone}. Stacks are
|
||||
* allocated contiguously, starting below the bottom of the main stack. When a
|
||||
* new stack is created, a red zone is created (actually, the red zone is simply
|
||||
* left unmapped) below the bottom of the stack, such that the stack will not be
|
||||
* able to grow all the way to the top of the next stack. This isn't
|
||||
* fool-proof. It is possible for a stack to grow by a large amount, such that
|
||||
* it grows into the next stack, and as long as the memory within the red zone
|
||||
* is never accessed, nothing will prevent one thread stack from trouncing all
|
||||
* over the next.
|
||||
*/
|
||||
SCLASS void * _next_stack
|
||||
#ifdef GLOBAL_PTHREAD_PRIVATE
|
||||
/* main stack top - main stack size - stack size - (red zone + main stack red zone) */
|
||||
= (void *) USRSTACK - PTHREAD_STACK_INITIAL - PTHREAD_STACK_DEFAULT - (2 * PTHREAD_STACK_GUARD)
|
||||
#endif
|
||||
;
|
||||
|
||||
/*
|
||||
* Declare the kernel scheduler jump buffer and stack:
|
||||
*/
|
||||
@ -1210,6 +1182,8 @@ void _fd_lock_backout(pthread_t);
|
||||
int _find_thread(pthread_t);
|
||||
struct pthread *_get_curthread(void);
|
||||
void _set_curthread(struct pthread *);
|
||||
void *_thread_stack_alloc(size_t, size_t);
|
||||
void _thread_stack_free(void *, size_t, size_t);
|
||||
int _thread_create(pthread_t *,const pthread_attr_t *,void *(*start_routine)(void *),void *,pthread_t);
|
||||
int _thread_fd_lock(int, int, struct timespec *);
|
||||
int _thread_fd_lock_debug(int, int, struct timespec *,char *fname,int lineno);
|
||||
|
235
lib/libkse/thread/thr_stack.c
Normal file
235
lib/libkse/thread/thr_stack.c
Normal file
@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Copyright (c) 2001 Daniel Eischen <deischen@freebsd.org>
|
||||
* Copyright (c) 2000-2001 Jason Evans <jasone@freebsd.org>
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/user.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
/* Spare thread stack. */
|
||||
struct stack {
|
||||
LIST_ENTRY(stack) qe; /* Stack queue linkage. */
|
||||
size_t stacksize; /* Stack size (rounded up). */
|
||||
size_t guardsize; /* Guard size. */
|
||||
void *stackaddr; /* Stack address. */
|
||||
};
|
||||
|
||||
/*
|
||||
* Default sized (stack and guard) spare stack queue. Stacks are cached to
|
||||
* avoid additional complexity managing mmap()ed stack regions. Spare stacks
|
||||
* are used in LIFO order to increase cache locality.
|
||||
*/
|
||||
static LIST_HEAD(, stack) _dstackq = LIST_HEAD_INITIALIZER(_dstackq);
|
||||
|
||||
/*
|
||||
* Miscellaneous sized (non-default stack and/or guard) spare stack queue.
|
||||
* Stacks are cached to avoid additional complexity managing mmap()ed stack
|
||||
* regions. This list is unordered, since ordering on both stack size and guard
|
||||
* size would be more trouble than it's worth. Stacks are allocated from this
|
||||
* cache on a first size match basis.
|
||||
*/
|
||||
static LIST_HEAD(, stack) _mstackq = LIST_HEAD_INITIALIZER(_mstackq);
|
||||
|
||||
/**
|
||||
* Base address of the last stack allocated (including its red zone, if there is
|
||||
* one). Stacks are allocated contiguously, starting beyond the top of the main
|
||||
* stack. When a new stack is created, a red zone is typically created
|
||||
* (actually, the red zone is simply left unmapped) above the top of the stack,
|
||||
* such that the stack will not be able to grow all the way to the bottom of the
|
||||
* next stack. This isn't fool-proof. It is possible for a stack to grow by a
|
||||
* large amount, such that it grows into the next stack, and as long as the
|
||||
* memory within the red zone is never accessed, nothing will prevent one thread
|
||||
* stack from trouncing all over the next.
|
||||
*
|
||||
* low memory
|
||||
* . . . . . . . . . . . . . . . . . .
|
||||
* | |
|
||||
* | stack 3 | start of 3rd thread stack
|
||||
* +-----------------------------------+
|
||||
* | |
|
||||
* | Red Zone (guard page) | red zone for 2nd thread
|
||||
* | |
|
||||
* +-----------------------------------+
|
||||
* | stack 2 - PTHREAD_STACK_DEFAULT | top of 2nd thread stack
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | stack 2 |
|
||||
* +-----------------------------------+ <-- start of 2nd thread stack
|
||||
* | |
|
||||
* | Red Zone | red zone for 1st thread
|
||||
* | |
|
||||
* +-----------------------------------+
|
||||
* | stack 1 - PTHREAD_STACK_DEFAULT | top of 1st thread stack
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | stack 1 |
|
||||
* +-----------------------------------+ <-- start of 1st thread stack
|
||||
* | | (initial value of last_stack)
|
||||
* | Red Zone |
|
||||
* | | red zone for main thread
|
||||
* +-----------------------------------+
|
||||
* | USRSTACK - PTHREAD_STACK_INITIAL | top of main thread stack
|
||||
* | | ^
|
||||
* | | |
|
||||
* | | |
|
||||
* | | | stack growth
|
||||
* | |
|
||||
* +-----------------------------------+ <-- start of main thread stack
|
||||
* (USRSTACK)
|
||||
* high memory
|
||||
*
|
||||
*/
|
||||
static void * last_stack = (void *) USRSTACK - PTHREAD_STACK_INITIAL
|
||||
- PTHREAD_GUARD_DEFAULT;
|
||||
|
||||
void *
|
||||
_thread_stack_alloc(size_t stacksize, size_t guardsize)
|
||||
{
|
||||
void *stack = NULL;
|
||||
struct stack *spare_stack;
|
||||
size_t stack_size;
|
||||
|
||||
/*
|
||||
* Round up stack size to nearest multiple of PAGE_SIZE, so that mmap()
|
||||
* will work. If the stack size is not an even multiple, we end up
|
||||
* initializing things such that there is unused space above the
|
||||
* beginning of the stack, so the stack sits snugly against its guard.
|
||||
*/
|
||||
if (stacksize % PAGE_SIZE != 0)
|
||||
stack_size = ((stacksize / PAGE_SIZE) + 1) * PAGE_SIZE;
|
||||
else
|
||||
stack_size = stacksize;
|
||||
|
||||
/*
|
||||
* If the stack and guard sizes are default, try to allocate a stack
|
||||
* from the default-size stack cache:
|
||||
*/
|
||||
if (stack_size == PTHREAD_STACK_DEFAULT &&
|
||||
guardsize == PTHREAD_GUARD_DEFAULT) {
|
||||
/*
|
||||
* Use the garbage collector mutex for synchronization of the
|
||||
* spare stack list.
|
||||
*/
|
||||
if (pthread_mutex_lock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot lock gc mutex");
|
||||
|
||||
if ((spare_stack = LIST_FIRST(&_dstackq)) != NULL) {
|
||||
/* Use the spare stack. */
|
||||
LIST_REMOVE(spare_stack, qe);
|
||||
stack = spare_stack->stackaddr;
|
||||
}
|
||||
|
||||
/* Unlock the garbage collector mutex. */
|
||||
if (pthread_mutex_unlock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot unlock gc mutex");
|
||||
}
|
||||
/*
|
||||
* The user specified a non-default stack and/or guard size, so try to
|
||||
* allocate a stack from the non-default size stack cache, using the
|
||||
* rounded up stack size (stack_size) in the search:
|
||||
*/
|
||||
else {
|
||||
/*
|
||||
* Use the garbage collector mutex for synchronization of the
|
||||
* spare stack list.
|
||||
*/
|
||||
if (pthread_mutex_lock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot lock gc mutex");
|
||||
|
||||
LIST_FOREACH(spare_stack, &_mstackq, qe) {
|
||||
if (spare_stack->stacksize == stack_size &&
|
||||
spare_stack->guardsize == guardsize) {
|
||||
LIST_REMOVE(spare_stack, qe);
|
||||
stack = spare_stack->stackaddr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Unlock the garbage collector mutex. */
|
||||
if (pthread_mutex_unlock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot unlock gc mutex");
|
||||
}
|
||||
|
||||
/* Check if a stack was not allocated from a stack cache: */
|
||||
if (stack == NULL) {
|
||||
|
||||
/* Allocate a new stack. */
|
||||
|
||||
stack = last_stack - stack_size;
|
||||
|
||||
/*
|
||||
* Even if stack allocation fails, we don't want to try to use
|
||||
* this location again, so unconditionally decrement
|
||||
* last_stack. Under normal operating conditions, the most
|
||||
* likely reason for an mmap() error is a stack overflow of the
|
||||
* adjacent thread stack.
|
||||
*/
|
||||
last_stack -= (stack_size + guardsize);
|
||||
|
||||
/* Stack: */
|
||||
if (mmap(stack, stack_size, PROT_READ | PROT_WRITE, MAP_STACK,
|
||||
-1, 0) == MAP_FAILED)
|
||||
stack = NULL;
|
||||
}
|
||||
|
||||
return (stack);
|
||||
}
|
||||
|
||||
/* This function must be called with _gc_mutex held. */
|
||||
void
|
||||
_thread_stack_free(void *stack, size_t stacksize, size_t guardsize)
|
||||
{
|
||||
struct stack *spare_stack;
|
||||
|
||||
spare_stack = (stack + stacksize - sizeof(struct stack));
|
||||
/* Round stacksize up to nearest multiple of PAGE_SIZE. */
|
||||
if (stacksize % PAGE_SIZE != 0) {
|
||||
spare_stack->stacksize = ((stacksize / PAGE_SIZE) + 1) *
|
||||
PAGE_SIZE;
|
||||
} else
|
||||
spare_stack->stacksize = stacksize;
|
||||
spare_stack->guardsize = guardsize;
|
||||
spare_stack->stackaddr = stack;
|
||||
|
||||
if (spare_stack->stacksize == PTHREAD_STACK_DEFAULT &&
|
||||
spare_stack->guardsize == PTHREAD_GUARD_DEFAULT) {
|
||||
/* Default stack/guard size. */
|
||||
LIST_INSERT_HEAD(&_dstackq, spare_stack, qe);
|
||||
} else {
|
||||
/* Non-default stack/guard size. */
|
||||
LIST_INSERT_HEAD(&_mstackq, spare_stack, qe);
|
||||
}
|
||||
}
|
@ -56,6 +56,7 @@ MAN+= pthread_attr.3 \
|
||||
MLINKS+= \
|
||||
pthread_attr.3 pthread_attr_destroy.3 \
|
||||
pthread_attr.3 pthread_attr_getdetachstate.3 \
|
||||
pthread_attr.3 pthread_attr_getguardsize.3 \
|
||||
pthread_attr.3 pthread_attr_getinheritsched.3 \
|
||||
pthread_attr.3 pthread_attr_getschedparam.3 \
|
||||
pthread_attr.3 pthread_attr_getschedpolicy.3 \
|
||||
@ -64,6 +65,7 @@ MLINKS+= \
|
||||
pthread_attr.3 pthread_attr_getstacksize.3 \
|
||||
pthread_attr.3 pthread_attr_init.3 \
|
||||
pthread_attr.3 pthread_attr_setdetachstate.3 \
|
||||
pthread_attr.3 pthread_attr_setguardsize.3 \
|
||||
pthread_attr.3 pthread_attr_setinheritsched.3 \
|
||||
pthread_attr.3 pthread_attr_setschedparam.3 \
|
||||
pthread_attr.3 pthread_attr_setschedpolicy.3 \
|
||||
|
@ -34,6 +34,8 @@
|
||||
.Nm pthread_attr_destroy ,
|
||||
.Nm pthread_attr_setstacksize ,
|
||||
.Nm pthread_attr_getstacksize ,
|
||||
.Nm pthread_attr_setguardsize ,
|
||||
.Nm pthread_attr_getguardsize ,
|
||||
.Nm pthread_attr_setstackaddr ,
|
||||
.Nm pthread_attr_getstackaddr ,
|
||||
.Nm pthread_attr_setdetachstate ,
|
||||
@ -60,6 +62,10 @@
|
||||
.Ft int
|
||||
.Fn pthread_attr_getstacksize "const pthread_attr_t *attr" "size_t *stacksize"
|
||||
.Ft int
|
||||
.Fn pthread_attr_setguardsize "pthread_attr_t *attr" "size_t guardsize"
|
||||
.Ft int
|
||||
.Fn pthread_attr_getguardsize "const pthread_attr_t *attr" "size_t *guardsize"
|
||||
.Ft int
|
||||
.Fn pthread_attr_setstackaddr "pthread_attr_t *attr" "void *stackaddr"
|
||||
.Ft int
|
||||
.Fn pthread_attr_getstackaddr "const pthread_attr_t *attr" "void **stackaddr"
|
||||
|
@ -12,10 +12,10 @@ CTESTS := hello_d.c hello_s.c join_leak_d.c mutex_d.c sem_d.c sigsuspend_d.c \
|
||||
|
||||
# C programs that are used internally by the tests. The build system merely
|
||||
# compiles these.
|
||||
BTESTS := hello_b.c
|
||||
BTESTS := guard_b.c hello_b.c
|
||||
|
||||
# Tests written in perl.
|
||||
PTESTS := propagate_s.pl
|
||||
PTESTS := guard_s.pl propagate_s.pl
|
||||
|
||||
# Munge the file lists to their final executable names (strip the .c).
|
||||
CTESTS := $(CTESTS:R)
|
||||
|
150
lib/libpthread/test/guard_b.c
Normal file
150
lib/libpthread/test/guard_b.c
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Jason Evans <jasone@freebsd.org>.
|
||||
* 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(s), this list of conditions and the following disclaimer
|
||||
* unmodified other than the allowable addition of one or more
|
||||
* copyright notices.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice(s), this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
* Test thread stack guard functionality.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define FRAME_SIZE 1024
|
||||
#define FRAME_OVERHEAD 40
|
||||
|
||||
struct args
|
||||
{
|
||||
void *top; /* Top of thread's initial stack frame. */
|
||||
int cur; /* Recursion depth. */
|
||||
int max; /* Maximum recursion depth. */
|
||||
};
|
||||
|
||||
void *
|
||||
recurse(void *args)
|
||||
{
|
||||
int top;
|
||||
struct args *parms = (struct args *)args;
|
||||
char filler[FRAME_SIZE - FRAME_OVERHEAD];
|
||||
|
||||
/* Touch the memory in this stack frame. */
|
||||
top = 0xa5;
|
||||
memset(filler, 0xa5, sizeof(filler));
|
||||
|
||||
if (parms->top == NULL) {
|
||||
/* Initial stack frame. */
|
||||
parms->top = (void*)⊤
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure frame size is what we expect. Getting this right involves
|
||||
* hand tweaking, so just print a warning rather than aborting.
|
||||
*/
|
||||
if (parms->top - (void *)&top != FRAME_SIZE * parms->cur) {
|
||||
fprintf(stderr, "Stack size (%d) != expected (%d), frame %d\n",
|
||||
parms->top - (void *)&top, FRAME_SIZE * parms->cur,
|
||||
parms->cur);
|
||||
}
|
||||
|
||||
parms->cur++;
|
||||
if (parms->cur < parms->max)
|
||||
recurse(args);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
size_t def_stacksize, def_guardsize;
|
||||
size_t stacksize, guardsize;
|
||||
pthread_t thread;
|
||||
pthread_attr_t attr;
|
||||
struct args args;
|
||||
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "Usage: guard_b <stacksize> <guardsize>\n");
|
||||
exit(1);
|
||||
}
|
||||
fprintf(stderr, "Test begin\n");
|
||||
|
||||
stacksize = strtoul(argv[1], NULL, 10);
|
||||
guardsize = strtoul(argv[2], NULL, 10);
|
||||
|
||||
assert(pthread_attr_init(&attr) == 0);
|
||||
/*
|
||||
* Exercise the attribute APIs more thoroughly than is strictly
|
||||
* necessary for the meat of this test program.
|
||||
*/
|
||||
assert(pthread_attr_getstacksize(&attr, &def_stacksize) == 0);
|
||||
assert(pthread_attr_getguardsize(&attr, &def_guardsize) == 0);
|
||||
if (def_stacksize != stacksize) {
|
||||
assert(pthread_attr_setstacksize(&attr, stacksize) == 0);
|
||||
assert(pthread_attr_getstacksize(&attr, &def_stacksize) == 0);
|
||||
assert(def_stacksize == stacksize);
|
||||
}
|
||||
if (def_guardsize != guardsize) {
|
||||
assert(pthread_attr_setguardsize(&attr, guardsize) == 0);
|
||||
assert(pthread_attr_getguardsize(&attr, &def_guardsize) == 0);
|
||||
assert(def_guardsize >= guardsize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a thread that will come just short of overflowing the thread
|
||||
* stack. We need to leave a bit of breathing room in case the thread
|
||||
* is context switched, and we also have to take care not to call any
|
||||
* functions in the deepest stack frame.
|
||||
*/
|
||||
args.top = NULL;
|
||||
args.cur = 0;
|
||||
args.max = (stacksize / FRAME_SIZE) - 1;
|
||||
fprintf(stderr, "No overflow:\n");
|
||||
assert(pthread_create(&thread, &attr, recurse, &args) == 0);
|
||||
assert(pthread_join(thread, NULL) == 0);
|
||||
|
||||
/*
|
||||
* Create a thread that will barely of overflow the thread stack. This
|
||||
* should cause a segfault.
|
||||
*/
|
||||
args.top = NULL;
|
||||
args.cur = 0;
|
||||
args.max = (stacksize / FRAME_SIZE) + 1;
|
||||
fprintf(stderr, "Overflow:\n");
|
||||
assert(pthread_create(&thread, &attr, recurse, &args) == 0);
|
||||
assert(pthread_join(thread, NULL) == 0);
|
||||
|
||||
/* Not reached. */
|
||||
fprintf(stderr, "Unexpected success\n");
|
||||
abort();
|
||||
|
||||
return 0;
|
||||
}
|
3
lib/libpthread/test/guard_b.exp
Normal file
3
lib/libpthread/test/guard_b.exp
Normal file
@ -0,0 +1,3 @@
|
||||
Test begin
|
||||
No overflow:
|
||||
Overflow:
|
69
lib/libpthread/test/guard_s.pl
Executable file
69
lib/libpthread/test/guard_s.pl
Executable file
@ -0,0 +1,69 @@
|
||||
#!/usr/bin/perl -w
|
||||
#
|
||||
# Copyright (C) 2001 Jason Evans <jasone@freebsd.org>.
|
||||
# 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(s), this list of conditions and the following disclaimer
|
||||
# unmodified other than the allowable addition of one or more
|
||||
# copyright notices.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice(s), this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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.
|
||||
#
|
||||
# $FreeBSD$
|
||||
#
|
||||
# Test thread stack guard functionality. The C test program needs to be driven
|
||||
# by this script because it segfaults when the stack guard is hit.
|
||||
#
|
||||
|
||||
print "1..30\n";
|
||||
|
||||
$i = 0;
|
||||
# Iterates 10 times.
|
||||
for ($stacksize = 65536; $stacksize < 131072; $stacksize += 7168)
|
||||
{
|
||||
# Iterates 3 times (1024, 4096, 7168).
|
||||
for ($guardsize = 1024; $guardsize < 8192; $guardsize += 3072)
|
||||
{
|
||||
$i++;
|
||||
|
||||
print "stacksize: $stacksize, guardsize: $guardsize\n";
|
||||
|
||||
`./guard_b $stacksize $guardsize >guard_b.out 2>&1`;
|
||||
|
||||
if (! -f "./guard_b.out")
|
||||
{
|
||||
print "not ok $i\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
`diff guard_b.exp guard_b.out >guard_b.diff 2>&1`;
|
||||
if ($?)
|
||||
{
|
||||
# diff returns non-zero if there is a difference.
|
||||
print "not ok $i\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
print "ok $i\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ SRCS+= \
|
||||
uthread_attr_destroy.c \
|
||||
uthread_attr_init.c \
|
||||
uthread_attr_getdetachstate.c \
|
||||
uthread_attr_getguardsize.c \
|
||||
uthread_attr_getinheritsched.c \
|
||||
uthread_attr_getschedparam.c \
|
||||
uthread_attr_getschedpolicy.c \
|
||||
@ -21,6 +22,7 @@ SRCS+= \
|
||||
uthread_attr_getstacksize.c \
|
||||
uthread_attr_setcreatesuspend_np.c \
|
||||
uthread_attr_setdetachstate.c \
|
||||
uthread_attr_setguardsize.c \
|
||||
uthread_attr_setinheritsched.c \
|
||||
uthread_attr_setschedparam.c \
|
||||
uthread_attr_setschedpolicy.c \
|
||||
@ -124,6 +126,7 @@ SRCS+= \
|
||||
uthread_socketpair.c \
|
||||
uthread_spec.c \
|
||||
uthread_spinlock.c \
|
||||
uthread_stack.c \
|
||||
uthread_suspend_np.c \
|
||||
uthread_switch_np.c \
|
||||
uthread_system.c \
|
||||
|
52
lib/libpthread/thread/thr_attr_getguardsize.c
Normal file
52
lib/libpthread/thread/thr_attr_getguardsize.c
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Jason Evans <jasone@freebsd.org>.
|
||||
* 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(s), this list of conditions and the following disclaimer
|
||||
* unmodified other than the allowable addition of one or more
|
||||
* copyright notices.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice(s), this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
__weak_reference(_pthread_attr_getguardsize, pthread_attr_getguardsize);
|
||||
|
||||
int
|
||||
_pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Check for invalid arguments: */
|
||||
if (attr == NULL || *attr == NULL || guardsize == NULL)
|
||||
ret = EINVAL;
|
||||
else {
|
||||
/* Return the guard size: */
|
||||
*guardsize = (*attr)->guardsize_attr;
|
||||
ret = 0;
|
||||
}
|
||||
return(ret);
|
||||
}
|
57
lib/libpthread/thread/thr_attr_setguardsize.c
Normal file
57
lib/libpthread/thread/thr_attr_setguardsize.c
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Jason Evans <jasone@freebsd.org>.
|
||||
* 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(s), this list of conditions and the following disclaimer
|
||||
* unmodified other than the allowable addition of one or more
|
||||
* copyright notices.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice(s), this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``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 COPYRIGHT HOLDER(S) 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
__weak_reference(_pthread_attr_setguardsize, pthread_attr_setguardsize);
|
||||
|
||||
int
|
||||
_pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Check for invalid arguments. */
|
||||
if (attr == NULL || *attr == NULL)
|
||||
ret = EINVAL;
|
||||
else {
|
||||
/* Round guardsize up to the nearest multiple of PAGE_SIZE. */
|
||||
if (guardsize % PAGE_SIZE != 0)
|
||||
guardsize = ((guardsize / PAGE_SIZE) + 1) * PAGE_SIZE;
|
||||
|
||||
/* Save the stack size. */
|
||||
(*attr)->guardsize_attr = guardsize;
|
||||
ret = 0;
|
||||
}
|
||||
return(ret);
|
||||
}
|
@ -38,8 +38,6 @@
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/mman.h>
|
||||
#include <machine/reg.h>
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
@ -99,68 +97,15 @@ _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) {
|
||||
}
|
||||
/* 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.
|
||||
*/
|
||||
if (pthread_mutex_lock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot lock gc mutex");
|
||||
|
||||
if ((spare_stack = SLIST_FIRST(&_stackq)) != NULL) {
|
||||
/* Use the spare stack. */
|
||||
SLIST_REMOVE_HEAD(&_stackq, qe);
|
||||
|
||||
/* Unlock the garbage collector mutex. */
|
||||
if (pthread_mutex_unlock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot unlock gc mutex");
|
||||
|
||||
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);
|
||||
|
||||
/* Unlock the garbage collector mutex. */
|
||||
if (pthread_mutex_unlock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot unlock gc mutex");
|
||||
|
||||
/* Stack: */
|
||||
if (mmap(stack, PTHREAD_STACK_DEFAULT,
|
||||
PROT_READ | PROT_WRITE, MAP_STACK,
|
||||
-1, 0) == MAP_FAILED) {
|
||||
ret = EAGAIN;
|
||||
free(new_thread);
|
||||
}
|
||||
/* Allocate a stack: */
|
||||
else {
|
||||
stack = _thread_stack_alloc(pattr->stacksize_attr,
|
||||
pattr->guardsize_attr);
|
||||
if (stack == NULL) {
|
||||
ret = EAGAIN;
|
||||
free(new_thread);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/* Check for errors: */
|
||||
if (ret != 0) {
|
||||
|
@ -31,6 +31,7 @@
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#include <sys/param.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
@ -220,28 +221,16 @@ _fork(void)
|
||||
static void
|
||||
free_thread_resources(struct pthread *thread)
|
||||
{
|
||||
struct stack *spare_stack;
|
||||
|
||||
/* Check to see if the threads library allocated the stack. */
|
||||
if ((thread->attr.stackaddr_attr == NULL) && (thread->stack != NULL)) {
|
||||
if (thread->attr.stacksize_attr != PTHREAD_STACK_DEFAULT) {
|
||||
/*
|
||||
* The threads library malloc()'d the stack;
|
||||
* just free() it.
|
||||
*/
|
||||
free(thread->stack);
|
||||
} else {
|
||||
/*
|
||||
* This stack was allocated from the main threads
|
||||
* stack; cache it for future use. Since this is
|
||||
* being called from fork, we are currently single
|
||||
* threaded so there is no need to protect the
|
||||
* queue insertion.
|
||||
*/
|
||||
spare_stack = (thread->stack + PTHREAD_STACK_DEFAULT -
|
||||
sizeof(struct stack));
|
||||
SLIST_INSERT_HEAD(&_stackq, spare_stack, qe);
|
||||
}
|
||||
/*
|
||||
* Since this is being called from fork, we are currently single
|
||||
* threaded so there is no need to protect the call to
|
||||
* _thread_stack_free() with _gc_mutex.
|
||||
*/
|
||||
_thread_stack_free(thread->stack, thread->attr.stacksize_attr,
|
||||
thread->attr.guardsize_attr);
|
||||
}
|
||||
|
||||
if (thread->specific_data != NULL)
|
||||
|
@ -34,13 +34,12 @@
|
||||
* Garbage collector thread. Frees memory allocated for dead threads.
|
||||
*
|
||||
*/
|
||||
#include <sys/param.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
@ -123,39 +122,20 @@ _thread_gc(pthread_addr_t arg)
|
||||
* Check if this thread has detached:
|
||||
*/
|
||||
else if ((pthread->attr.flags &
|
||||
PTHREAD_DETACHED) != 0) {
|
||||
PTHREAD_DETACHED) != 0) {
|
||||
/* Remove this thread from the dead list: */
|
||||
TAILQ_REMOVE(&_dead_list, pthread, dle);
|
||||
|
||||
/*
|
||||
* Check if the stack was not specified by
|
||||
* the caller to pthread_create and has not
|
||||
* the caller to pthread_create() and has not
|
||||
* been destroyed yet:
|
||||
*/
|
||||
if (pthread->attr.stackaddr_attr == NULL &&
|
||||
pthread->stack != NULL) {
|
||||
if (pthread->attr.stacksize_attr
|
||||
== PTHREAD_STACK_DEFAULT) {
|
||||
/*
|
||||
* Default-size stack. Cache
|
||||
* it:
|
||||
*/
|
||||
struct stack *spare_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;
|
||||
}
|
||||
_thread_stack_free(pthread->stack,
|
||||
pthread->attr.stacksize_attr,
|
||||
pthread->attr.guardsize_attr);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -170,37 +150,18 @@ _thread_gc(pthread_addr_t arg)
|
||||
* not destroy it.
|
||||
*
|
||||
* Check if the stack was not specified by
|
||||
* the caller to pthread_create and has not
|
||||
* the caller to pthread_create() and has not
|
||||
* been destroyed yet:
|
||||
*/
|
||||
if (pthread->attr.stackaddr_attr == NULL &&
|
||||
pthread->stack != NULL) {
|
||||
if (pthread->attr.stacksize_attr
|
||||
== PTHREAD_STACK_DEFAULT) {
|
||||
/*
|
||||
* Default-size stack. Cache
|
||||
* it:
|
||||
*/
|
||||
struct stack *spare_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;
|
||||
}
|
||||
_thread_stack_free(pthread->stack,
|
||||
pthread->attr.stacksize_attr,
|
||||
pthread->attr.guardsize_attr);
|
||||
|
||||
/*
|
||||
* NULL the stack pointer now
|
||||
* that the memory has been freed:
|
||||
* NULL the stack pointer now that the
|
||||
* memory has been freed:
|
||||
*/
|
||||
pthread->stack = NULL;
|
||||
}
|
||||
|
@ -268,9 +268,6 @@ _thread_init(void)
|
||||
memcpy((void *) &_thread_initial->attr, &pthread_attr_default,
|
||||
sizeof(struct pthread_attr));
|
||||
|
||||
/* Initialize the thread stack cache: */
|
||||
SLIST_INIT(&_stackq);
|
||||
|
||||
/*
|
||||
* Create a red zone below the main stack. All other stacks are
|
||||
* constrained to a maximum size by the paramters passed to
|
||||
@ -279,7 +276,7 @@ _thread_init(void)
|
||||
* thread stack that is just beyond.
|
||||
*/
|
||||
if (mmap((void *) USRSTACK - PTHREAD_STACK_INITIAL -
|
||||
PTHREAD_STACK_GUARD, PTHREAD_STACK_GUARD, 0, MAP_ANON,
|
||||
PTHREAD_GUARD_DEFAULT, PTHREAD_GUARD_DEFAULT, 0, MAP_ANON,
|
||||
-1, 0) == MAP_FAILED)
|
||||
PANIC("Cannot allocate red zone for initial thread");
|
||||
|
||||
|
@ -389,6 +389,7 @@ struct pthread_attr {
|
||||
void (*cleanup_attr) ();
|
||||
void *stackaddr_attr;
|
||||
size_t stacksize_attr;
|
||||
size_t guardsize_attr;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -414,13 +415,13 @@ enum pthread_susp {
|
||||
*/
|
||||
#define PTHREAD_STACK_DEFAULT 65536
|
||||
/*
|
||||
* Size of red zone at the end of each stack. In actuality, this "red zone" is
|
||||
* merely an unmapped region, except in the case of the initial stack. Since
|
||||
* mmap() makes it possible to specify the maximum growth of a MAP_STACK region,
|
||||
* an unmapped gap between thread stacks achieves the same effect as explicitly
|
||||
* mapped red zones.
|
||||
* Size of default red zone at the end of each stack. In actuality, this "red
|
||||
* zone" is merely an unmapped region, except in the case of the initial stack.
|
||||
* Since mmap() makes it possible to specify the maximum growth of a MAP_STACK
|
||||
* region, an unmapped gap between thread stacks achieves the same effect as
|
||||
* explicitly mapped red zones.
|
||||
*/
|
||||
#define PTHREAD_STACK_GUARD PAGE_SIZE
|
||||
#define PTHREAD_GUARD_DEFAULT PAGE_SIZE
|
||||
|
||||
/*
|
||||
* Maximum size of initial thread's stack. This perhaps deserves to be larger
|
||||
@ -875,11 +876,6 @@ struct pthread {
|
||||
int lineno; /* Source line number. */
|
||||
};
|
||||
|
||||
/* Spare thread stack. */
|
||||
struct stack {
|
||||
SLIST_ENTRY(stack) qe; /* Queue entry for this stack. */
|
||||
};
|
||||
|
||||
/*
|
||||
* Global variables for the uthread kernel.
|
||||
*/
|
||||
@ -992,8 +988,9 @@ SCLASS struct pthread *_thread_initial
|
||||
/* Default thread attributes: */
|
||||
SCLASS struct pthread_attr pthread_attr_default
|
||||
#ifdef GLOBAL_PTHREAD_PRIVATE
|
||||
= { SCHED_RR, 0, TIMESLICE_USEC, PTHREAD_DEFAULT_PRIORITY, PTHREAD_CREATE_RUNNING,
|
||||
PTHREAD_CREATE_JOINABLE, NULL, NULL, NULL, PTHREAD_STACK_DEFAULT };
|
||||
= { SCHED_RR, 0, TIMESLICE_USEC, PTHREAD_DEFAULT_PRIORITY,
|
||||
PTHREAD_CREATE_RUNNING, PTHREAD_CREATE_JOINABLE, NULL, NULL, NULL,
|
||||
PTHREAD_STACK_DEFAULT, PTHREAD_GUARD_DEFAULT };
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
@ -1141,31 +1138,6 @@ SCLASS pthread_switch_routine_t _sched_switch_hook
|
||||
#endif
|
||||
;
|
||||
|
||||
/*
|
||||
* 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, red zone}. Stacks are
|
||||
* allocated contiguously, starting below the bottom of the main stack. When a
|
||||
* new stack is created, a red zone is created (actually, the red zone is simply
|
||||
* left unmapped) below the bottom of the stack, such that the stack will not be
|
||||
* able to grow all the way to the top of the next stack. This isn't
|
||||
* fool-proof. It is possible for a stack to grow by a large amount, such that
|
||||
* it grows into the next stack, and as long as the memory within the red zone
|
||||
* is never accessed, nothing will prevent one thread stack from trouncing all
|
||||
* over the next.
|
||||
*/
|
||||
SCLASS void * _next_stack
|
||||
#ifdef GLOBAL_PTHREAD_PRIVATE
|
||||
/* main stack top - main stack size - stack size - (red zone + main stack red zone) */
|
||||
= (void *) USRSTACK - PTHREAD_STACK_INITIAL - PTHREAD_STACK_DEFAULT - (2 * PTHREAD_STACK_GUARD)
|
||||
#endif
|
||||
;
|
||||
|
||||
/*
|
||||
* Declare the kernel scheduler jump buffer and stack:
|
||||
*/
|
||||
@ -1210,6 +1182,8 @@ void _fd_lock_backout(pthread_t);
|
||||
int _find_thread(pthread_t);
|
||||
struct pthread *_get_curthread(void);
|
||||
void _set_curthread(struct pthread *);
|
||||
void *_thread_stack_alloc(size_t, size_t);
|
||||
void _thread_stack_free(void *, size_t, size_t);
|
||||
int _thread_create(pthread_t *,const pthread_attr_t *,void *(*start_routine)(void *),void *,pthread_t);
|
||||
int _thread_fd_lock(int, int, struct timespec *);
|
||||
int _thread_fd_lock_debug(int, int, struct timespec *,char *fname,int lineno);
|
||||
|
235
lib/libpthread/thread/thr_stack.c
Normal file
235
lib/libpthread/thread/thr_stack.c
Normal file
@ -0,0 +1,235 @@
|
||||
/*
|
||||
* Copyright (c) 2001 Daniel Eischen <deischen@freebsd.org>
|
||||
* Copyright (c) 2000-2001 Jason Evans <jasone@freebsd.org>
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/user.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
/* Spare thread stack. */
|
||||
struct stack {
|
||||
LIST_ENTRY(stack) qe; /* Stack queue linkage. */
|
||||
size_t stacksize; /* Stack size (rounded up). */
|
||||
size_t guardsize; /* Guard size. */
|
||||
void *stackaddr; /* Stack address. */
|
||||
};
|
||||
|
||||
/*
|
||||
* Default sized (stack and guard) spare stack queue. Stacks are cached to
|
||||
* avoid additional complexity managing mmap()ed stack regions. Spare stacks
|
||||
* are used in LIFO order to increase cache locality.
|
||||
*/
|
||||
static LIST_HEAD(, stack) _dstackq = LIST_HEAD_INITIALIZER(_dstackq);
|
||||
|
||||
/*
|
||||
* Miscellaneous sized (non-default stack and/or guard) spare stack queue.
|
||||
* Stacks are cached to avoid additional complexity managing mmap()ed stack
|
||||
* regions. This list is unordered, since ordering on both stack size and guard
|
||||
* size would be more trouble than it's worth. Stacks are allocated from this
|
||||
* cache on a first size match basis.
|
||||
*/
|
||||
static LIST_HEAD(, stack) _mstackq = LIST_HEAD_INITIALIZER(_mstackq);
|
||||
|
||||
/**
|
||||
* Base address of the last stack allocated (including its red zone, if there is
|
||||
* one). Stacks are allocated contiguously, starting beyond the top of the main
|
||||
* stack. When a new stack is created, a red zone is typically created
|
||||
* (actually, the red zone is simply left unmapped) above the top of the stack,
|
||||
* such that the stack will not be able to grow all the way to the bottom of the
|
||||
* next stack. This isn't fool-proof. It is possible for a stack to grow by a
|
||||
* large amount, such that it grows into the next stack, and as long as the
|
||||
* memory within the red zone is never accessed, nothing will prevent one thread
|
||||
* stack from trouncing all over the next.
|
||||
*
|
||||
* low memory
|
||||
* . . . . . . . . . . . . . . . . . .
|
||||
* | |
|
||||
* | stack 3 | start of 3rd thread stack
|
||||
* +-----------------------------------+
|
||||
* | |
|
||||
* | Red Zone (guard page) | red zone for 2nd thread
|
||||
* | |
|
||||
* +-----------------------------------+
|
||||
* | stack 2 - PTHREAD_STACK_DEFAULT | top of 2nd thread stack
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | stack 2 |
|
||||
* +-----------------------------------+ <-- start of 2nd thread stack
|
||||
* | |
|
||||
* | Red Zone | red zone for 1st thread
|
||||
* | |
|
||||
* +-----------------------------------+
|
||||
* | stack 1 - PTHREAD_STACK_DEFAULT | top of 1st thread stack
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* | stack 1 |
|
||||
* +-----------------------------------+ <-- start of 1st thread stack
|
||||
* | | (initial value of last_stack)
|
||||
* | Red Zone |
|
||||
* | | red zone for main thread
|
||||
* +-----------------------------------+
|
||||
* | USRSTACK - PTHREAD_STACK_INITIAL | top of main thread stack
|
||||
* | | ^
|
||||
* | | |
|
||||
* | | |
|
||||
* | | | stack growth
|
||||
* | |
|
||||
* +-----------------------------------+ <-- start of main thread stack
|
||||
* (USRSTACK)
|
||||
* high memory
|
||||
*
|
||||
*/
|
||||
static void * last_stack = (void *) USRSTACK - PTHREAD_STACK_INITIAL
|
||||
- PTHREAD_GUARD_DEFAULT;
|
||||
|
||||
void *
|
||||
_thread_stack_alloc(size_t stacksize, size_t guardsize)
|
||||
{
|
||||
void *stack = NULL;
|
||||
struct stack *spare_stack;
|
||||
size_t stack_size;
|
||||
|
||||
/*
|
||||
* Round up stack size to nearest multiple of PAGE_SIZE, so that mmap()
|
||||
* will work. If the stack size is not an even multiple, we end up
|
||||
* initializing things such that there is unused space above the
|
||||
* beginning of the stack, so the stack sits snugly against its guard.
|
||||
*/
|
||||
if (stacksize % PAGE_SIZE != 0)
|
||||
stack_size = ((stacksize / PAGE_SIZE) + 1) * PAGE_SIZE;
|
||||
else
|
||||
stack_size = stacksize;
|
||||
|
||||
/*
|
||||
* If the stack and guard sizes are default, try to allocate a stack
|
||||
* from the default-size stack cache:
|
||||
*/
|
||||
if (stack_size == PTHREAD_STACK_DEFAULT &&
|
||||
guardsize == PTHREAD_GUARD_DEFAULT) {
|
||||
/*
|
||||
* Use the garbage collector mutex for synchronization of the
|
||||
* spare stack list.
|
||||
*/
|
||||
if (pthread_mutex_lock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot lock gc mutex");
|
||||
|
||||
if ((spare_stack = LIST_FIRST(&_dstackq)) != NULL) {
|
||||
/* Use the spare stack. */
|
||||
LIST_REMOVE(spare_stack, qe);
|
||||
stack = spare_stack->stackaddr;
|
||||
}
|
||||
|
||||
/* Unlock the garbage collector mutex. */
|
||||
if (pthread_mutex_unlock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot unlock gc mutex");
|
||||
}
|
||||
/*
|
||||
* The user specified a non-default stack and/or guard size, so try to
|
||||
* allocate a stack from the non-default size stack cache, using the
|
||||
* rounded up stack size (stack_size) in the search:
|
||||
*/
|
||||
else {
|
||||
/*
|
||||
* Use the garbage collector mutex for synchronization of the
|
||||
* spare stack list.
|
||||
*/
|
||||
if (pthread_mutex_lock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot lock gc mutex");
|
||||
|
||||
LIST_FOREACH(spare_stack, &_mstackq, qe) {
|
||||
if (spare_stack->stacksize == stack_size &&
|
||||
spare_stack->guardsize == guardsize) {
|
||||
LIST_REMOVE(spare_stack, qe);
|
||||
stack = spare_stack->stackaddr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Unlock the garbage collector mutex. */
|
||||
if (pthread_mutex_unlock(&_gc_mutex) != 0)
|
||||
PANIC("Cannot unlock gc mutex");
|
||||
}
|
||||
|
||||
/* Check if a stack was not allocated from a stack cache: */
|
||||
if (stack == NULL) {
|
||||
|
||||
/* Allocate a new stack. */
|
||||
|
||||
stack = last_stack - stack_size;
|
||||
|
||||
/*
|
||||
* Even if stack allocation fails, we don't want to try to use
|
||||
* this location again, so unconditionally decrement
|
||||
* last_stack. Under normal operating conditions, the most
|
||||
* likely reason for an mmap() error is a stack overflow of the
|
||||
* adjacent thread stack.
|
||||
*/
|
||||
last_stack -= (stack_size + guardsize);
|
||||
|
||||
/* Stack: */
|
||||
if (mmap(stack, stack_size, PROT_READ | PROT_WRITE, MAP_STACK,
|
||||
-1, 0) == MAP_FAILED)
|
||||
stack = NULL;
|
||||
}
|
||||
|
||||
return (stack);
|
||||
}
|
||||
|
||||
/* This function must be called with _gc_mutex held. */
|
||||
void
|
||||
_thread_stack_free(void *stack, size_t stacksize, size_t guardsize)
|
||||
{
|
||||
struct stack *spare_stack;
|
||||
|
||||
spare_stack = (stack + stacksize - sizeof(struct stack));
|
||||
/* Round stacksize up to nearest multiple of PAGE_SIZE. */
|
||||
if (stacksize % PAGE_SIZE != 0) {
|
||||
spare_stack->stacksize = ((stacksize / PAGE_SIZE) + 1) *
|
||||
PAGE_SIZE;
|
||||
} else
|
||||
spare_stack->stacksize = stacksize;
|
||||
spare_stack->guardsize = guardsize;
|
||||
spare_stack->stackaddr = stack;
|
||||
|
||||
if (spare_stack->stacksize == PTHREAD_STACK_DEFAULT &&
|
||||
spare_stack->guardsize == PTHREAD_GUARD_DEFAULT) {
|
||||
/* Default stack/guard size. */
|
||||
LIST_INSERT_HEAD(&_dstackq, spare_stack, qe);
|
||||
} else {
|
||||
/* Non-default stack/guard size. */
|
||||
LIST_INSERT_HEAD(&_mstackq, spare_stack, qe);
|
||||
}
|
||||
}
|
@ -34,6 +34,8 @@
|
||||
.Nm pthread_attr_destroy ,
|
||||
.Nm pthread_attr_setstacksize ,
|
||||
.Nm pthread_attr_getstacksize ,
|
||||
.Nm pthread_attr_setguardsize ,
|
||||
.Nm pthread_attr_getguardsize ,
|
||||
.Nm pthread_attr_setstackaddr ,
|
||||
.Nm pthread_attr_getstackaddr ,
|
||||
.Nm pthread_attr_setdetachstate ,
|
||||
@ -60,6 +62,10 @@
|
||||
.Ft int
|
||||
.Fn pthread_attr_getstacksize "const pthread_attr_t *attr" "size_t *stacksize"
|
||||
.Ft int
|
||||
.Fn pthread_attr_setguardsize "pthread_attr_t *attr" "size_t guardsize"
|
||||
.Ft int
|
||||
.Fn pthread_attr_getguardsize "const pthread_attr_t *attr" "size_t *guardsize"
|
||||
.Ft int
|
||||
.Fn pthread_attr_setstackaddr "pthread_attr_t *attr" "void *stackaddr"
|
||||
.Ft int
|
||||
.Fn pthread_attr_getstackaddr "const pthread_attr_t *attr" "void **stackaddr"
|
||||
|
Loading…
Reference in New Issue
Block a user