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