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
@ -192,12 +192,15 @@ __BEGIN_DECLS
|
|||||||
int pthread_attr_destroy __P((pthread_attr_t *));
|
int pthread_attr_destroy __P((pthread_attr_t *));
|
||||||
int pthread_attr_getstacksize __P((const pthread_attr_t *,
|
int pthread_attr_getstacksize __P((const pthread_attr_t *,
|
||||||
size_t *));
|
size_t *));
|
||||||
|
int pthread_attr_getguardsize __P((const pthread_attr_t *,
|
||||||
|
size_t *));
|
||||||
int pthread_attr_getstackaddr __P((const pthread_attr_t *,
|
int pthread_attr_getstackaddr __P((const pthread_attr_t *,
|
||||||
void **));
|
void **));
|
||||||
int pthread_attr_getdetachstate __P((const pthread_attr_t *,
|
int pthread_attr_getdetachstate __P((const pthread_attr_t *,
|
||||||
int *));
|
int *));
|
||||||
int pthread_attr_init __P((pthread_attr_t *));
|
int pthread_attr_init __P((pthread_attr_t *));
|
||||||
int pthread_attr_setstacksize __P((pthread_attr_t *, size_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_setstackaddr __P((pthread_attr_t *, void *));
|
||||||
int pthread_attr_setdetachstate __P((pthread_attr_t *, int));
|
int pthread_attr_setdetachstate __P((pthread_attr_t *, int));
|
||||||
void pthread_cleanup_pop __P((int));
|
void pthread_cleanup_pop __P((int));
|
||||||
|
@ -56,6 +56,7 @@ MAN+= pthread_attr.3 \
|
|||||||
MLINKS+= \
|
MLINKS+= \
|
||||||
pthread_attr.3 pthread_attr_destroy.3 \
|
pthread_attr.3 pthread_attr_destroy.3 \
|
||||||
pthread_attr.3 pthread_attr_getdetachstate.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_getinheritsched.3 \
|
||||||
pthread_attr.3 pthread_attr_getschedparam.3 \
|
pthread_attr.3 pthread_attr_getschedparam.3 \
|
||||||
pthread_attr.3 pthread_attr_getschedpolicy.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_getstacksize.3 \
|
||||||
pthread_attr.3 pthread_attr_init.3 \
|
pthread_attr.3 pthread_attr_init.3 \
|
||||||
pthread_attr.3 pthread_attr_setdetachstate.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_setinheritsched.3 \
|
||||||
pthread_attr.3 pthread_attr_setschedparam.3 \
|
pthread_attr.3 pthread_attr_setschedparam.3 \
|
||||||
pthread_attr.3 pthread_attr_setschedpolicy.3 \
|
pthread_attr.3 pthread_attr_setschedpolicy.3 \
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
.Nm pthread_attr_destroy ,
|
.Nm pthread_attr_destroy ,
|
||||||
.Nm pthread_attr_setstacksize ,
|
.Nm pthread_attr_setstacksize ,
|
||||||
.Nm pthread_attr_getstacksize ,
|
.Nm pthread_attr_getstacksize ,
|
||||||
|
.Nm pthread_attr_setguardsize ,
|
||||||
|
.Nm pthread_attr_getguardsize ,
|
||||||
.Nm pthread_attr_setstackaddr ,
|
.Nm pthread_attr_setstackaddr ,
|
||||||
.Nm pthread_attr_getstackaddr ,
|
.Nm pthread_attr_getstackaddr ,
|
||||||
.Nm pthread_attr_setdetachstate ,
|
.Nm pthread_attr_setdetachstate ,
|
||||||
@ -60,6 +62,10 @@
|
|||||||
.Ft int
|
.Ft int
|
||||||
.Fn pthread_attr_getstacksize "const pthread_attr_t *attr" "size_t *stacksize"
|
.Fn pthread_attr_getstacksize "const pthread_attr_t *attr" "size_t *stacksize"
|
||||||
.Ft int
|
.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"
|
.Fn pthread_attr_setstackaddr "pthread_attr_t *attr" "void *stackaddr"
|
||||||
.Ft int
|
.Ft int
|
||||||
.Fn pthread_attr_getstackaddr "const pthread_attr_t *attr" "void **stackaddr"
|
.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
|
# C programs that are used internally by the tests. The build system merely
|
||||||
# compiles these.
|
# compiles these.
|
||||||
BTESTS := hello_b.c
|
BTESTS := guard_b.c hello_b.c
|
||||||
|
|
||||||
# Tests written in perl.
|
# 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).
|
# Munge the file lists to their final executable names (strip the .c).
|
||||||
CTESTS := $(CTESTS:R)
|
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_destroy.c \
|
||||||
uthread_attr_init.c \
|
uthread_attr_init.c \
|
||||||
uthread_attr_getdetachstate.c \
|
uthread_attr_getdetachstate.c \
|
||||||
|
uthread_attr_getguardsize.c \
|
||||||
uthread_attr_getinheritsched.c \
|
uthread_attr_getinheritsched.c \
|
||||||
uthread_attr_getschedparam.c \
|
uthread_attr_getschedparam.c \
|
||||||
uthread_attr_getschedpolicy.c \
|
uthread_attr_getschedpolicy.c \
|
||||||
@ -21,6 +22,7 @@ SRCS+= \
|
|||||||
uthread_attr_getstacksize.c \
|
uthread_attr_getstacksize.c \
|
||||||
uthread_attr_setcreatesuspend_np.c \
|
uthread_attr_setcreatesuspend_np.c \
|
||||||
uthread_attr_setdetachstate.c \
|
uthread_attr_setdetachstate.c \
|
||||||
|
uthread_attr_setguardsize.c \
|
||||||
uthread_attr_setinheritsched.c \
|
uthread_attr_setinheritsched.c \
|
||||||
uthread_attr_setschedparam.c \
|
uthread_attr_setschedparam.c \
|
||||||
uthread_attr_setschedpolicy.c \
|
uthread_attr_setschedpolicy.c \
|
||||||
@ -124,6 +126,7 @@ SRCS+= \
|
|||||||
uthread_socketpair.c \
|
uthread_socketpair.c \
|
||||||
uthread_spec.c \
|
uthread_spec.c \
|
||||||
uthread_spinlock.c \
|
uthread_spinlock.c \
|
||||||
|
uthread_stack.c \
|
||||||
uthread_suspend_np.c \
|
uthread_suspend_np.c \
|
||||||
uthread_switch_np.c \
|
uthread_switch_np.c \
|
||||||
uthread_system.c \
|
uthread_system.c \
|
||||||
|
@ -389,6 +389,7 @@ struct pthread_attr {
|
|||||||
void (*cleanup_attr) ();
|
void (*cleanup_attr) ();
|
||||||
void *stackaddr_attr;
|
void *stackaddr_attr;
|
||||||
size_t stacksize_attr;
|
size_t stacksize_attr;
|
||||||
|
size_t guardsize_attr;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -414,13 +415,13 @@ enum pthread_susp {
|
|||||||
*/
|
*/
|
||||||
#define PTHREAD_STACK_DEFAULT 65536
|
#define PTHREAD_STACK_DEFAULT 65536
|
||||||
/*
|
/*
|
||||||
* Size of red zone at the end of each stack. In actuality, this "red zone" is
|
* Size of default red zone at the end of each stack. In actuality, this "red
|
||||||
* merely an unmapped region, except in the case of the initial stack. Since
|
* zone" is merely an unmapped region, except in the case of the initial stack.
|
||||||
* mmap() makes it possible to specify the maximum growth of a MAP_STACK region,
|
* Since mmap() makes it possible to specify the maximum growth of a MAP_STACK
|
||||||
* an unmapped gap between thread stacks achieves the same effect as explicitly
|
* region, an unmapped gap between thread stacks achieves the same effect as
|
||||||
* mapped red zones.
|
* 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
|
* Maximum size of initial thread's stack. This perhaps deserves to be larger
|
||||||
@ -875,11 +876,6 @@ struct pthread {
|
|||||||
int lineno; /* Source line number. */
|
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.
|
* Global variables for the uthread kernel.
|
||||||
*/
|
*/
|
||||||
@ -992,8 +988,9 @@ SCLASS struct pthread *_thread_initial
|
|||||||
/* Default thread attributes: */
|
/* Default thread attributes: */
|
||||||
SCLASS struct pthread_attr pthread_attr_default
|
SCLASS struct pthread_attr pthread_attr_default
|
||||||
#ifdef GLOBAL_PTHREAD_PRIVATE
|
#ifdef GLOBAL_PTHREAD_PRIVATE
|
||||||
= { SCHED_RR, 0, TIMESLICE_USEC, PTHREAD_DEFAULT_PRIORITY, PTHREAD_CREATE_RUNNING,
|
= { SCHED_RR, 0, TIMESLICE_USEC, PTHREAD_DEFAULT_PRIORITY,
|
||||||
PTHREAD_CREATE_JOINABLE, NULL, NULL, NULL, PTHREAD_STACK_DEFAULT };
|
PTHREAD_CREATE_RUNNING, PTHREAD_CREATE_JOINABLE, NULL, NULL, NULL,
|
||||||
|
PTHREAD_STACK_DEFAULT, PTHREAD_GUARD_DEFAULT };
|
||||||
#else
|
#else
|
||||||
;
|
;
|
||||||
#endif
|
#endif
|
||||||
@ -1141,31 +1138,6 @@ SCLASS pthread_switch_routine_t _sched_switch_hook
|
|||||||
#endif
|
#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:
|
* Declare the kernel scheduler jump buffer and stack:
|
||||||
*/
|
*/
|
||||||
@ -1210,6 +1182,8 @@ void _fd_lock_backout(pthread_t);
|
|||||||
int _find_thread(pthread_t);
|
int _find_thread(pthread_t);
|
||||||
struct pthread *_get_curthread(void);
|
struct pthread *_get_curthread(void);
|
||||||
void _set_curthread(struct pthread *);
|
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_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(int, int, struct timespec *);
|
||||||
int _thread_fd_lock_debug(int, int, struct timespec *,char *fname,int lineno);
|
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 <unistd.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <machine/reg.h>
|
#include <machine/reg.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include "pthread_private.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: */
|
/* Check if a stack was specified in the thread attributes: */
|
||||||
if ((stack = pattr->stackaddr_attr) != NULL) {
|
if ((stack = pattr->stackaddr_attr) != NULL) {
|
||||||
}
|
}
|
||||||
/* Allocate memory for a default-size stack: */
|
/* Allocate a stack: */
|
||||||
else if (pattr->stacksize_attr == PTHREAD_STACK_DEFAULT) {
|
else {
|
||||||
struct stack *spare_stack;
|
stack = _thread_stack_alloc(pattr->stacksize_attr,
|
||||||
|
pattr->guardsize_attr);
|
||||||
/* Allocate or re-use a default-size stack. */
|
if (stack == NULL) {
|
||||||
|
ret = EAGAIN;
|
||||||
/*
|
free(new_thread);
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
* 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: */
|
/* Check for errors: */
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
*
|
*
|
||||||
* $FreeBSD$
|
* $FreeBSD$
|
||||||
*/
|
*/
|
||||||
|
#include <sys/param.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -220,28 +221,16 @@ _fork(void)
|
|||||||
static void
|
static void
|
||||||
free_thread_resources(struct pthread *thread)
|
free_thread_resources(struct pthread *thread)
|
||||||
{
|
{
|
||||||
struct stack *spare_stack;
|
|
||||||
|
|
||||||
/* Check to see if the threads library allocated the stack. */
|
/* Check to see if the threads library allocated the stack. */
|
||||||
if ((thread->attr.stackaddr_attr == NULL) && (thread->stack != NULL)) {
|
if ((thread->attr.stackaddr_attr == NULL) && (thread->stack != NULL)) {
|
||||||
if (thread->attr.stacksize_attr != PTHREAD_STACK_DEFAULT) {
|
/*
|
||||||
/*
|
* Since this is being called from fork, we are currently single
|
||||||
* The threads library malloc()'d the stack;
|
* threaded so there is no need to protect the call to
|
||||||
* just free() it.
|
* _thread_stack_free() with _gc_mutex.
|
||||||
*/
|
*/
|
||||||
free(thread->stack);
|
_thread_stack_free(thread->stack, thread->attr.stacksize_attr,
|
||||||
} else {
|
thread->attr.guardsize_attr);
|
||||||
/*
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thread->specific_data != NULL)
|
if (thread->specific_data != NULL)
|
||||||
|
@ -34,13 +34,12 @@
|
|||||||
* Garbage collector thread. Frees memory allocated for dead threads.
|
* Garbage collector thread. Frees memory allocated for dead threads.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
#include <sys/param.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include "pthread_private.h"
|
#include "pthread_private.h"
|
||||||
|
|
||||||
@ -123,39 +122,20 @@ _thread_gc(pthread_addr_t arg)
|
|||||||
* Check if this thread has detached:
|
* Check if this thread has detached:
|
||||||
*/
|
*/
|
||||||
else if ((pthread->attr.flags &
|
else if ((pthread->attr.flags &
|
||||||
PTHREAD_DETACHED) != 0) {
|
PTHREAD_DETACHED) != 0) {
|
||||||
/* Remove this thread from the dead list: */
|
/* Remove this thread from the dead list: */
|
||||||
TAILQ_REMOVE(&_dead_list, pthread, dle);
|
TAILQ_REMOVE(&_dead_list, pthread, dle);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the stack was not specified by
|
* 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:
|
* been destroyed yet:
|
||||||
*/
|
*/
|
||||||
if (pthread->attr.stackaddr_attr == NULL &&
|
if (pthread->attr.stackaddr_attr == NULL &&
|
||||||
pthread->stack != NULL) {
|
pthread->stack != NULL) {
|
||||||
if (pthread->attr.stacksize_attr
|
_thread_stack_free(pthread->stack,
|
||||||
== PTHREAD_STACK_DEFAULT) {
|
pthread->attr.stacksize_attr,
|
||||||
/*
|
pthread->attr.guardsize_attr);
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -170,37 +150,18 @@ _thread_gc(pthread_addr_t arg)
|
|||||||
* not destroy it.
|
* not destroy it.
|
||||||
*
|
*
|
||||||
* Check if the stack was not specified by
|
* 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:
|
* been destroyed yet:
|
||||||
*/
|
*/
|
||||||
if (pthread->attr.stackaddr_attr == NULL &&
|
if (pthread->attr.stackaddr_attr == NULL &&
|
||||||
pthread->stack != NULL) {
|
pthread->stack != NULL) {
|
||||||
if (pthread->attr.stacksize_attr
|
_thread_stack_free(pthread->stack,
|
||||||
== PTHREAD_STACK_DEFAULT) {
|
pthread->attr.stacksize_attr,
|
||||||
/*
|
pthread->attr.guardsize_attr);
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NULL the stack pointer now
|
* NULL the stack pointer now that the
|
||||||
* that the memory has been freed:
|
* memory has been freed:
|
||||||
*/
|
*/
|
||||||
pthread->stack = NULL;
|
pthread->stack = NULL;
|
||||||
}
|
}
|
||||||
|
@ -268,9 +268,6 @@ _thread_init(void)
|
|||||||
memcpy((void *) &_thread_initial->attr, &pthread_attr_default,
|
memcpy((void *) &_thread_initial->attr, &pthread_attr_default,
|
||||||
sizeof(struct pthread_attr));
|
sizeof(struct pthread_attr));
|
||||||
|
|
||||||
/* Initialize the thread stack cache: */
|
|
||||||
SLIST_INIT(&_stackq);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a red zone below the main stack. All other stacks are
|
* Create a red zone below the main stack. All other stacks are
|
||||||
* constrained to a maximum size by the paramters passed to
|
* constrained to a maximum size by the paramters passed to
|
||||||
@ -279,7 +276,7 @@ _thread_init(void)
|
|||||||
* thread stack that is just beyond.
|
* thread stack that is just beyond.
|
||||||
*/
|
*/
|
||||||
if (mmap((void *) USRSTACK - PTHREAD_STACK_INITIAL -
|
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)
|
-1, 0) == MAP_FAILED)
|
||||||
PANIC("Cannot allocate red zone for initial thread");
|
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
|
# C programs that are used internally by the tests. The build system merely
|
||||||
# compiles these.
|
# compiles these.
|
||||||
BTESTS := hello_b.c
|
BTESTS := guard_b.c hello_b.c
|
||||||
|
|
||||||
# Tests written in perl.
|
# 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).
|
# Munge the file lists to their final executable names (strip the .c).
|
||||||
CTESTS := $(CTESTS:R)
|
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_destroy.c \
|
||||||
uthread_attr_init.c \
|
uthread_attr_init.c \
|
||||||
uthread_attr_getdetachstate.c \
|
uthread_attr_getdetachstate.c \
|
||||||
|
uthread_attr_getguardsize.c \
|
||||||
uthread_attr_getinheritsched.c \
|
uthread_attr_getinheritsched.c \
|
||||||
uthread_attr_getschedparam.c \
|
uthread_attr_getschedparam.c \
|
||||||
uthread_attr_getschedpolicy.c \
|
uthread_attr_getschedpolicy.c \
|
||||||
@ -21,6 +22,7 @@ SRCS+= \
|
|||||||
uthread_attr_getstacksize.c \
|
uthread_attr_getstacksize.c \
|
||||||
uthread_attr_setcreatesuspend_np.c \
|
uthread_attr_setcreatesuspend_np.c \
|
||||||
uthread_attr_setdetachstate.c \
|
uthread_attr_setdetachstate.c \
|
||||||
|
uthread_attr_setguardsize.c \
|
||||||
uthread_attr_setinheritsched.c \
|
uthread_attr_setinheritsched.c \
|
||||||
uthread_attr_setschedparam.c \
|
uthread_attr_setschedparam.c \
|
||||||
uthread_attr_setschedpolicy.c \
|
uthread_attr_setschedpolicy.c \
|
||||||
@ -124,6 +126,7 @@ SRCS+= \
|
|||||||
uthread_socketpair.c \
|
uthread_socketpair.c \
|
||||||
uthread_spec.c \
|
uthread_spec.c \
|
||||||
uthread_spinlock.c \
|
uthread_spinlock.c \
|
||||||
|
uthread_stack.c \
|
||||||
uthread_suspend_np.c \
|
uthread_suspend_np.c \
|
||||||
uthread_switch_np.c \
|
uthread_switch_np.c \
|
||||||
uthread_system.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 <unistd.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <machine/reg.h>
|
#include <machine/reg.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include "pthread_private.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: */
|
/* Check if a stack was specified in the thread attributes: */
|
||||||
if ((stack = pattr->stackaddr_attr) != NULL) {
|
if ((stack = pattr->stackaddr_attr) != NULL) {
|
||||||
}
|
}
|
||||||
/* Allocate memory for a default-size stack: */
|
/* Allocate a stack: */
|
||||||
else if (pattr->stacksize_attr == PTHREAD_STACK_DEFAULT) {
|
else {
|
||||||
struct stack *spare_stack;
|
stack = _thread_stack_alloc(pattr->stacksize_attr,
|
||||||
|
pattr->guardsize_attr);
|
||||||
/* Allocate or re-use a default-size stack. */
|
if (stack == NULL) {
|
||||||
|
ret = EAGAIN;
|
||||||
/*
|
free(new_thread);
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
* 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: */
|
/* Check for errors: */
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
*
|
*
|
||||||
* $FreeBSD$
|
* $FreeBSD$
|
||||||
*/
|
*/
|
||||||
|
#include <sys/param.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -220,28 +221,16 @@ _fork(void)
|
|||||||
static void
|
static void
|
||||||
free_thread_resources(struct pthread *thread)
|
free_thread_resources(struct pthread *thread)
|
||||||
{
|
{
|
||||||
struct stack *spare_stack;
|
|
||||||
|
|
||||||
/* Check to see if the threads library allocated the stack. */
|
/* Check to see if the threads library allocated the stack. */
|
||||||
if ((thread->attr.stackaddr_attr == NULL) && (thread->stack != NULL)) {
|
if ((thread->attr.stackaddr_attr == NULL) && (thread->stack != NULL)) {
|
||||||
if (thread->attr.stacksize_attr != PTHREAD_STACK_DEFAULT) {
|
/*
|
||||||
/*
|
* Since this is being called from fork, we are currently single
|
||||||
* The threads library malloc()'d the stack;
|
* threaded so there is no need to protect the call to
|
||||||
* just free() it.
|
* _thread_stack_free() with _gc_mutex.
|
||||||
*/
|
*/
|
||||||
free(thread->stack);
|
_thread_stack_free(thread->stack, thread->attr.stacksize_attr,
|
||||||
} else {
|
thread->attr.guardsize_attr);
|
||||||
/*
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thread->specific_data != NULL)
|
if (thread->specific_data != NULL)
|
||||||
|
@ -268,9 +268,6 @@ _thread_init(void)
|
|||||||
memcpy((void *) &_thread_initial->attr, &pthread_attr_default,
|
memcpy((void *) &_thread_initial->attr, &pthread_attr_default,
|
||||||
sizeof(struct pthread_attr));
|
sizeof(struct pthread_attr));
|
||||||
|
|
||||||
/* Initialize the thread stack cache: */
|
|
||||||
SLIST_INIT(&_stackq);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a red zone below the main stack. All other stacks are
|
* Create a red zone below the main stack. All other stacks are
|
||||||
* constrained to a maximum size by the paramters passed to
|
* constrained to a maximum size by the paramters passed to
|
||||||
@ -279,7 +276,7 @@ _thread_init(void)
|
|||||||
* thread stack that is just beyond.
|
* thread stack that is just beyond.
|
||||||
*/
|
*/
|
||||||
if (mmap((void *) USRSTACK - PTHREAD_STACK_INITIAL -
|
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)
|
-1, 0) == MAP_FAILED)
|
||||||
PANIC("Cannot allocate red zone for initial thread");
|
PANIC("Cannot allocate red zone for initial thread");
|
||||||
|
|
||||||
|
@ -389,6 +389,7 @@ struct pthread_attr {
|
|||||||
void (*cleanup_attr) ();
|
void (*cleanup_attr) ();
|
||||||
void *stackaddr_attr;
|
void *stackaddr_attr;
|
||||||
size_t stacksize_attr;
|
size_t stacksize_attr;
|
||||||
|
size_t guardsize_attr;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -414,13 +415,13 @@ enum pthread_susp {
|
|||||||
*/
|
*/
|
||||||
#define PTHREAD_STACK_DEFAULT 65536
|
#define PTHREAD_STACK_DEFAULT 65536
|
||||||
/*
|
/*
|
||||||
* Size of red zone at the end of each stack. In actuality, this "red zone" is
|
* Size of default red zone at the end of each stack. In actuality, this "red
|
||||||
* merely an unmapped region, except in the case of the initial stack. Since
|
* zone" is merely an unmapped region, except in the case of the initial stack.
|
||||||
* mmap() makes it possible to specify the maximum growth of a MAP_STACK region,
|
* Since mmap() makes it possible to specify the maximum growth of a MAP_STACK
|
||||||
* an unmapped gap between thread stacks achieves the same effect as explicitly
|
* region, an unmapped gap between thread stacks achieves the same effect as
|
||||||
* mapped red zones.
|
* 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
|
* Maximum size of initial thread's stack. This perhaps deserves to be larger
|
||||||
@ -875,11 +876,6 @@ struct pthread {
|
|||||||
int lineno; /* Source line number. */
|
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.
|
* Global variables for the uthread kernel.
|
||||||
*/
|
*/
|
||||||
@ -992,8 +988,9 @@ SCLASS struct pthread *_thread_initial
|
|||||||
/* Default thread attributes: */
|
/* Default thread attributes: */
|
||||||
SCLASS struct pthread_attr pthread_attr_default
|
SCLASS struct pthread_attr pthread_attr_default
|
||||||
#ifdef GLOBAL_PTHREAD_PRIVATE
|
#ifdef GLOBAL_PTHREAD_PRIVATE
|
||||||
= { SCHED_RR, 0, TIMESLICE_USEC, PTHREAD_DEFAULT_PRIORITY, PTHREAD_CREATE_RUNNING,
|
= { SCHED_RR, 0, TIMESLICE_USEC, PTHREAD_DEFAULT_PRIORITY,
|
||||||
PTHREAD_CREATE_JOINABLE, NULL, NULL, NULL, PTHREAD_STACK_DEFAULT };
|
PTHREAD_CREATE_RUNNING, PTHREAD_CREATE_JOINABLE, NULL, NULL, NULL,
|
||||||
|
PTHREAD_STACK_DEFAULT, PTHREAD_GUARD_DEFAULT };
|
||||||
#else
|
#else
|
||||||
;
|
;
|
||||||
#endif
|
#endif
|
||||||
@ -1141,31 +1138,6 @@ SCLASS pthread_switch_routine_t _sched_switch_hook
|
|||||||
#endif
|
#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:
|
* Declare the kernel scheduler jump buffer and stack:
|
||||||
*/
|
*/
|
||||||
@ -1210,6 +1182,8 @@ void _fd_lock_backout(pthread_t);
|
|||||||
int _find_thread(pthread_t);
|
int _find_thread(pthread_t);
|
||||||
struct pthread *_get_curthread(void);
|
struct pthread *_get_curthread(void);
|
||||||
void _set_curthread(struct pthread *);
|
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_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(int, int, struct timespec *);
|
||||||
int _thread_fd_lock_debug(int, int, struct timespec *,char *fname,int lineno);
|
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+= \
|
MLINKS+= \
|
||||||
pthread_attr.3 pthread_attr_destroy.3 \
|
pthread_attr.3 pthread_attr_destroy.3 \
|
||||||
pthread_attr.3 pthread_attr_getdetachstate.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_getinheritsched.3 \
|
||||||
pthread_attr.3 pthread_attr_getschedparam.3 \
|
pthread_attr.3 pthread_attr_getschedparam.3 \
|
||||||
pthread_attr.3 pthread_attr_getschedpolicy.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_getstacksize.3 \
|
||||||
pthread_attr.3 pthread_attr_init.3 \
|
pthread_attr.3 pthread_attr_init.3 \
|
||||||
pthread_attr.3 pthread_attr_setdetachstate.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_setinheritsched.3 \
|
||||||
pthread_attr.3 pthread_attr_setschedparam.3 \
|
pthread_attr.3 pthread_attr_setschedparam.3 \
|
||||||
pthread_attr.3 pthread_attr_setschedpolicy.3 \
|
pthread_attr.3 pthread_attr_setschedpolicy.3 \
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
.Nm pthread_attr_destroy ,
|
.Nm pthread_attr_destroy ,
|
||||||
.Nm pthread_attr_setstacksize ,
|
.Nm pthread_attr_setstacksize ,
|
||||||
.Nm pthread_attr_getstacksize ,
|
.Nm pthread_attr_getstacksize ,
|
||||||
|
.Nm pthread_attr_setguardsize ,
|
||||||
|
.Nm pthread_attr_getguardsize ,
|
||||||
.Nm pthread_attr_setstackaddr ,
|
.Nm pthread_attr_setstackaddr ,
|
||||||
.Nm pthread_attr_getstackaddr ,
|
.Nm pthread_attr_getstackaddr ,
|
||||||
.Nm pthread_attr_setdetachstate ,
|
.Nm pthread_attr_setdetachstate ,
|
||||||
@ -60,6 +62,10 @@
|
|||||||
.Ft int
|
.Ft int
|
||||||
.Fn pthread_attr_getstacksize "const pthread_attr_t *attr" "size_t *stacksize"
|
.Fn pthread_attr_getstacksize "const pthread_attr_t *attr" "size_t *stacksize"
|
||||||
.Ft int
|
.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"
|
.Fn pthread_attr_setstackaddr "pthread_attr_t *attr" "void *stackaddr"
|
||||||
.Ft int
|
.Ft int
|
||||||
.Fn pthread_attr_getstackaddr "const pthread_attr_t *attr" "void **stackaddr"
|
.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
|
# C programs that are used internally by the tests. The build system merely
|
||||||
# compiles these.
|
# compiles these.
|
||||||
BTESTS := hello_b.c
|
BTESTS := guard_b.c hello_b.c
|
||||||
|
|
||||||
# Tests written in perl.
|
# 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).
|
# Munge the file lists to their final executable names (strip the .c).
|
||||||
CTESTS := $(CTESTS:R)
|
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_destroy.c \
|
||||||
uthread_attr_init.c \
|
uthread_attr_init.c \
|
||||||
uthread_attr_getdetachstate.c \
|
uthread_attr_getdetachstate.c \
|
||||||
|
uthread_attr_getguardsize.c \
|
||||||
uthread_attr_getinheritsched.c \
|
uthread_attr_getinheritsched.c \
|
||||||
uthread_attr_getschedparam.c \
|
uthread_attr_getschedparam.c \
|
||||||
uthread_attr_getschedpolicy.c \
|
uthread_attr_getschedpolicy.c \
|
||||||
@ -21,6 +22,7 @@ SRCS+= \
|
|||||||
uthread_attr_getstacksize.c \
|
uthread_attr_getstacksize.c \
|
||||||
uthread_attr_setcreatesuspend_np.c \
|
uthread_attr_setcreatesuspend_np.c \
|
||||||
uthread_attr_setdetachstate.c \
|
uthread_attr_setdetachstate.c \
|
||||||
|
uthread_attr_setguardsize.c \
|
||||||
uthread_attr_setinheritsched.c \
|
uthread_attr_setinheritsched.c \
|
||||||
uthread_attr_setschedparam.c \
|
uthread_attr_setschedparam.c \
|
||||||
uthread_attr_setschedpolicy.c \
|
uthread_attr_setschedpolicy.c \
|
||||||
@ -124,6 +126,7 @@ SRCS+= \
|
|||||||
uthread_socketpair.c \
|
uthread_socketpair.c \
|
||||||
uthread_spec.c \
|
uthread_spec.c \
|
||||||
uthread_spinlock.c \
|
uthread_spinlock.c \
|
||||||
|
uthread_stack.c \
|
||||||
uthread_suspend_np.c \
|
uthread_suspend_np.c \
|
||||||
uthread_switch_np.c \
|
uthread_switch_np.c \
|
||||||
uthread_system.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 <unistd.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <machine/reg.h>
|
#include <machine/reg.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include "pthread_private.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: */
|
/* Check if a stack was specified in the thread attributes: */
|
||||||
if ((stack = pattr->stackaddr_attr) != NULL) {
|
if ((stack = pattr->stackaddr_attr) != NULL) {
|
||||||
}
|
}
|
||||||
/* Allocate memory for a default-size stack: */
|
/* Allocate a stack: */
|
||||||
else if (pattr->stacksize_attr == PTHREAD_STACK_DEFAULT) {
|
else {
|
||||||
struct stack *spare_stack;
|
stack = _thread_stack_alloc(pattr->stacksize_attr,
|
||||||
|
pattr->guardsize_attr);
|
||||||
/* Allocate or re-use a default-size stack. */
|
if (stack == NULL) {
|
||||||
|
ret = EAGAIN;
|
||||||
/*
|
free(new_thread);
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
* 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: */
|
/* Check for errors: */
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
*
|
*
|
||||||
* $FreeBSD$
|
* $FreeBSD$
|
||||||
*/
|
*/
|
||||||
|
#include <sys/param.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -220,28 +221,16 @@ _fork(void)
|
|||||||
static void
|
static void
|
||||||
free_thread_resources(struct pthread *thread)
|
free_thread_resources(struct pthread *thread)
|
||||||
{
|
{
|
||||||
struct stack *spare_stack;
|
|
||||||
|
|
||||||
/* Check to see if the threads library allocated the stack. */
|
/* Check to see if the threads library allocated the stack. */
|
||||||
if ((thread->attr.stackaddr_attr == NULL) && (thread->stack != NULL)) {
|
if ((thread->attr.stackaddr_attr == NULL) && (thread->stack != NULL)) {
|
||||||
if (thread->attr.stacksize_attr != PTHREAD_STACK_DEFAULT) {
|
/*
|
||||||
/*
|
* Since this is being called from fork, we are currently single
|
||||||
* The threads library malloc()'d the stack;
|
* threaded so there is no need to protect the call to
|
||||||
* just free() it.
|
* _thread_stack_free() with _gc_mutex.
|
||||||
*/
|
*/
|
||||||
free(thread->stack);
|
_thread_stack_free(thread->stack, thread->attr.stacksize_attr,
|
||||||
} else {
|
thread->attr.guardsize_attr);
|
||||||
/*
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thread->specific_data != NULL)
|
if (thread->specific_data != NULL)
|
||||||
|
@ -34,13 +34,12 @@
|
|||||||
* Garbage collector thread. Frees memory allocated for dead threads.
|
* Garbage collector thread. Frees memory allocated for dead threads.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
#include <sys/param.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include "pthread_private.h"
|
#include "pthread_private.h"
|
||||||
|
|
||||||
@ -123,39 +122,20 @@ _thread_gc(pthread_addr_t arg)
|
|||||||
* Check if this thread has detached:
|
* Check if this thread has detached:
|
||||||
*/
|
*/
|
||||||
else if ((pthread->attr.flags &
|
else if ((pthread->attr.flags &
|
||||||
PTHREAD_DETACHED) != 0) {
|
PTHREAD_DETACHED) != 0) {
|
||||||
/* Remove this thread from the dead list: */
|
/* Remove this thread from the dead list: */
|
||||||
TAILQ_REMOVE(&_dead_list, pthread, dle);
|
TAILQ_REMOVE(&_dead_list, pthread, dle);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the stack was not specified by
|
* 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:
|
* been destroyed yet:
|
||||||
*/
|
*/
|
||||||
if (pthread->attr.stackaddr_attr == NULL &&
|
if (pthread->attr.stackaddr_attr == NULL &&
|
||||||
pthread->stack != NULL) {
|
pthread->stack != NULL) {
|
||||||
if (pthread->attr.stacksize_attr
|
_thread_stack_free(pthread->stack,
|
||||||
== PTHREAD_STACK_DEFAULT) {
|
pthread->attr.stacksize_attr,
|
||||||
/*
|
pthread->attr.guardsize_attr);
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -170,37 +150,18 @@ _thread_gc(pthread_addr_t arg)
|
|||||||
* not destroy it.
|
* not destroy it.
|
||||||
*
|
*
|
||||||
* Check if the stack was not specified by
|
* 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:
|
* been destroyed yet:
|
||||||
*/
|
*/
|
||||||
if (pthread->attr.stackaddr_attr == NULL &&
|
if (pthread->attr.stackaddr_attr == NULL &&
|
||||||
pthread->stack != NULL) {
|
pthread->stack != NULL) {
|
||||||
if (pthread->attr.stacksize_attr
|
_thread_stack_free(pthread->stack,
|
||||||
== PTHREAD_STACK_DEFAULT) {
|
pthread->attr.stacksize_attr,
|
||||||
/*
|
pthread->attr.guardsize_attr);
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NULL the stack pointer now
|
* NULL the stack pointer now that the
|
||||||
* that the memory has been freed:
|
* memory has been freed:
|
||||||
*/
|
*/
|
||||||
pthread->stack = NULL;
|
pthread->stack = NULL;
|
||||||
}
|
}
|
||||||
|
@ -268,9 +268,6 @@ _thread_init(void)
|
|||||||
memcpy((void *) &_thread_initial->attr, &pthread_attr_default,
|
memcpy((void *) &_thread_initial->attr, &pthread_attr_default,
|
||||||
sizeof(struct pthread_attr));
|
sizeof(struct pthread_attr));
|
||||||
|
|
||||||
/* Initialize the thread stack cache: */
|
|
||||||
SLIST_INIT(&_stackq);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a red zone below the main stack. All other stacks are
|
* Create a red zone below the main stack. All other stacks are
|
||||||
* constrained to a maximum size by the paramters passed to
|
* constrained to a maximum size by the paramters passed to
|
||||||
@ -279,7 +276,7 @@ _thread_init(void)
|
|||||||
* thread stack that is just beyond.
|
* thread stack that is just beyond.
|
||||||
*/
|
*/
|
||||||
if (mmap((void *) USRSTACK - PTHREAD_STACK_INITIAL -
|
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)
|
-1, 0) == MAP_FAILED)
|
||||||
PANIC("Cannot allocate red zone for initial thread");
|
PANIC("Cannot allocate red zone for initial thread");
|
||||||
|
|
||||||
|
@ -389,6 +389,7 @@ struct pthread_attr {
|
|||||||
void (*cleanup_attr) ();
|
void (*cleanup_attr) ();
|
||||||
void *stackaddr_attr;
|
void *stackaddr_attr;
|
||||||
size_t stacksize_attr;
|
size_t stacksize_attr;
|
||||||
|
size_t guardsize_attr;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -414,13 +415,13 @@ enum pthread_susp {
|
|||||||
*/
|
*/
|
||||||
#define PTHREAD_STACK_DEFAULT 65536
|
#define PTHREAD_STACK_DEFAULT 65536
|
||||||
/*
|
/*
|
||||||
* Size of red zone at the end of each stack. In actuality, this "red zone" is
|
* Size of default red zone at the end of each stack. In actuality, this "red
|
||||||
* merely an unmapped region, except in the case of the initial stack. Since
|
* zone" is merely an unmapped region, except in the case of the initial stack.
|
||||||
* mmap() makes it possible to specify the maximum growth of a MAP_STACK region,
|
* Since mmap() makes it possible to specify the maximum growth of a MAP_STACK
|
||||||
* an unmapped gap between thread stacks achieves the same effect as explicitly
|
* region, an unmapped gap between thread stacks achieves the same effect as
|
||||||
* mapped red zones.
|
* 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
|
* Maximum size of initial thread's stack. This perhaps deserves to be larger
|
||||||
@ -875,11 +876,6 @@ struct pthread {
|
|||||||
int lineno; /* Source line number. */
|
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.
|
* Global variables for the uthread kernel.
|
||||||
*/
|
*/
|
||||||
@ -992,8 +988,9 @@ SCLASS struct pthread *_thread_initial
|
|||||||
/* Default thread attributes: */
|
/* Default thread attributes: */
|
||||||
SCLASS struct pthread_attr pthread_attr_default
|
SCLASS struct pthread_attr pthread_attr_default
|
||||||
#ifdef GLOBAL_PTHREAD_PRIVATE
|
#ifdef GLOBAL_PTHREAD_PRIVATE
|
||||||
= { SCHED_RR, 0, TIMESLICE_USEC, PTHREAD_DEFAULT_PRIORITY, PTHREAD_CREATE_RUNNING,
|
= { SCHED_RR, 0, TIMESLICE_USEC, PTHREAD_DEFAULT_PRIORITY,
|
||||||
PTHREAD_CREATE_JOINABLE, NULL, NULL, NULL, PTHREAD_STACK_DEFAULT };
|
PTHREAD_CREATE_RUNNING, PTHREAD_CREATE_JOINABLE, NULL, NULL, NULL,
|
||||||
|
PTHREAD_STACK_DEFAULT, PTHREAD_GUARD_DEFAULT };
|
||||||
#else
|
#else
|
||||||
;
|
;
|
||||||
#endif
|
#endif
|
||||||
@ -1141,31 +1138,6 @@ SCLASS pthread_switch_routine_t _sched_switch_hook
|
|||||||
#endif
|
#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:
|
* Declare the kernel scheduler jump buffer and stack:
|
||||||
*/
|
*/
|
||||||
@ -1210,6 +1182,8 @@ void _fd_lock_backout(pthread_t);
|
|||||||
int _find_thread(pthread_t);
|
int _find_thread(pthread_t);
|
||||||
struct pthread *_get_curthread(void);
|
struct pthread *_get_curthread(void);
|
||||||
void _set_curthread(struct pthread *);
|
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_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(int, int, struct timespec *);
|
||||||
int _thread_fd_lock_debug(int, int, struct timespec *,char *fname,int lineno);
|
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_destroy ,
|
||||||
.Nm pthread_attr_setstacksize ,
|
.Nm pthread_attr_setstacksize ,
|
||||||
.Nm pthread_attr_getstacksize ,
|
.Nm pthread_attr_getstacksize ,
|
||||||
|
.Nm pthread_attr_setguardsize ,
|
||||||
|
.Nm pthread_attr_getguardsize ,
|
||||||
.Nm pthread_attr_setstackaddr ,
|
.Nm pthread_attr_setstackaddr ,
|
||||||
.Nm pthread_attr_getstackaddr ,
|
.Nm pthread_attr_getstackaddr ,
|
||||||
.Nm pthread_attr_setdetachstate ,
|
.Nm pthread_attr_setdetachstate ,
|
||||||
@ -60,6 +62,10 @@
|
|||||||
.Ft int
|
.Ft int
|
||||||
.Fn pthread_attr_getstacksize "const pthread_attr_t *attr" "size_t *stacksize"
|
.Fn pthread_attr_getstacksize "const pthread_attr_t *attr" "size_t *stacksize"
|
||||||
.Ft int
|
.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"
|
.Fn pthread_attr_setstackaddr "pthread_attr_t *attr" "void *stackaddr"
|
||||||
.Ft int
|
.Ft int
|
||||||
.Fn pthread_attr_getstackaddr "const pthread_attr_t *attr" "void **stackaddr"
|
.Fn pthread_attr_getstackaddr "const pthread_attr_t *attr" "void **stackaddr"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user