1 /** $MirOS: src/lib/libpthread/uthread/uthread_stack.c,v 1.2 2005/11/21 21:42:41 tg Exp $ */
2 /* $OpenBSD: uthread_stack.c,v 1.7 2000/03/22 02:06:05 d Exp $ */
3 /*
4 * Copyright 1999, David Leonard. All rights reserved.
5 * <insert BSD-style license&disclaimer>
6 */
7
8 /*
9 * Thread stack allocation.
10 *
11 * If stack pointers grow down, towards the beginning of stack storage,
12 * the first page of the storage is protected using mprotect() so as
13 * to generate a SIGSEGV if a thread overflows its stack. Similarly,
14 * for stacks that grow up, the last page of the storage is protected.
15 */
16
17 #include <sys/param.h>
18 #include <sys/user.h>
19 #include <sys/mman.h>
20 #include <stddef.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <pthread.h>
24 #include <pthread_np.h>
25 #include "pthread_private.h"
26
27 struct stack *
_thread_stack_alloc(base,size)28 _thread_stack_alloc(base, size)
29 void *base;
30 size_t size;
31 {
32 struct stack *stack;
33 int nbpg = getpagesize();
34
35 /* Maintain a stack of default-sized stacks that we can re-use. */
36 if (base == NULL && size == PTHREAD_STACK_DEFAULT) {
37 if (pthread_mutex_lock(&_gc_mutex) != 0)
38 PANIC("Cannot lock gc mutex");
39
40 if ((stack = SLIST_FIRST(&_stackq)) != NULL) {
41 SLIST_REMOVE_HEAD(&_stackq, qe);
42 if (pthread_mutex_unlock(&_gc_mutex) != 0)
43 PANIC("Cannot unlock gc mutex");
44 return stack;
45 }
46 if (pthread_mutex_unlock(&_gc_mutex) != 0)
47 PANIC("Cannot unlock gc mutex");
48 }
49
50 /* Allocate some storage to hold information about the stack: */
51 stack = (struct stack *)malloc(sizeof (struct stack));
52 if (stack == NULL)
53 return NULL;
54
55 if (base != NULL) {
56 /* Use the user's storage */
57 stack->base = base;
58 stack->size = size;
59 stack->redzone = NULL;
60 stack->storage = NULL;
61 return stack;
62 }
63
64 /* Allocate some storage for the stack, with some overhead: */
65 stack->storage = malloc(size + nbpg * 2);
66 if (stack->storage == NULL) {
67 free(stack);
68 return NULL;
69 }
70
71 /*
72 * Compute the location of the red zone.
73 * Use ptrdiff_t to convert the storage base pointer
74 * into an integer so that page alignment can be done with
75 * integer arithmetic.
76 */
77 #if defined(MACHINE_STACK_GROWS_UP)
78 /* Red zone is the last page of the storage: */
79 stack->redzone = (void *)(((ptrdiff_t)stack->storage +
80 size + nbpg - 1) & ~(nbpg - 1));
81 stack->base = (caddr_t)stack->storage;
82 stack->size = size;
83 #else
84 /* Red zone is the first page of the storage: */
85 stack->redzone = (void *)(((ptrdiff_t)stack->storage +
86 nbpg - 1) & ~(nbpg - 1));
87 stack->base = (caddr_t)stack->redzone + nbpg;
88 stack->size = size;
89 #endif
90 if (mprotect(stack->redzone, nbpg, 0) == -1)
91 PANIC("Cannot protect stack red zone");
92
93 return stack;
94 }
95
96 void
_thread_stack_free(stack)97 _thread_stack_free(stack)
98 struct stack *stack;
99 {
100 int nbpg = getpagesize();
101
102 /* Cache allocated stacks of default size: */
103 if (stack->storage != NULL && stack->size == PTHREAD_STACK_DEFAULT)
104 SLIST_INSERT_HEAD(&_stackq, stack, qe);
105 else {
106 /* Restore storage protection to what malloc gave us: */
107 if (stack->redzone)
108 mprotect(stack->redzone, nbpg,
109 PROT_READ|PROT_WRITE);
110
111 /* Free storage: */
112 if (stack->storage)
113 free(stack->storage);
114
115 /* Free stack information storage: */
116 free(stack);
117 }
118 }
119