xref: /openbsd/regress/lib/libpthread/stack/stack.c (revision 0eba73db)
1 /* $OpenBSD: stack.c,v 1.4 2014/08/10 05:08:31 guenther Exp $ */
2 /* PUBLIC DOMAIN Feb 2012 <guenther@openbsd.org> */
3 
4 /* Test the handling of the pthread_attr_t stack attributes */
5 
6 #include <sys/types.h>
7 #include <sys/mman.h>
8 #include <stdint.h>
9 #include <limits.h>
10 #include <pthread.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include "test.h"
16 
17 #define LARGE_SIZE	(1024 * 1024)
18 
19 /* thread main for plain location tests */
20 void *
tmain0(void * arg)21 tmain0(void *arg)
22 {
23 	int s;
24 
25 	return (&s);
26 }
27 
28 /* thread main for testing a large buffer on the stack */
29 void *
tmain1(void * arg)30 tmain1(void *arg)
31 {
32 	char buf[LARGE_SIZE];
33 
34 	memset(buf, 0xd0, sizeof(buf));
35 	return (buf + LARGE_SIZE/2);
36 }
37 
38 /*
39  * struct and thread main for testing that a thread's stack is where
40  * we put it
41  */
42 struct st
43 {
44 	char *addr;
45 	size_t size;
46 };
47 void *
tmain2(void * arg)48 tmain2(void *arg)
49 {
50 	struct st *s = arg;
51 
52 	ASSERT((char *)&s >= s->addr && (char *)&s - s->addr < s->size);
53 	return (NULL);
54 }
55 
56 int
main(void)57 main(void)
58 {
59 	pthread_attr_t attr;
60 	pthread_t t;
61 	struct st thread_stack;
62 	void *addr, *addr2;
63 	size_t size, size2, pagesize;
64 	int err;
65 
66 	pagesize = (size_t)sysconf(_SC_PAGESIZE);
67 
68 	CHECKr(pthread_attr_init(&attr));
69 
70 	/* verify that the initial values are what we expect */
71 	size = 1;
72 	CHECKr(pthread_attr_getguardsize(&attr, &size));
73 	ASSERT(size != 1);		/* must have changed */
74 	ASSERT(size != 0);		/* we default to having a guardpage */
75 
76 	size = 1;
77 	CHECKr(pthread_attr_getstacksize(&attr, &size));
78 	ASSERT(size >= PTHREAD_STACK_MIN);
79 
80 	addr = &addr;
81 	CHECKr(pthread_attr_getstackaddr(&attr, &addr));
82 	ASSERT(addr == NULL);		/* default must be NULL */
83 
84 	addr2 = &addr;
85 	size2 = 1;
86 	CHECKr(pthread_attr_getstack(&attr, &addr2, &size2));
87 	ASSERT(addr2 == addr);		/* must match the other calls */
88 	ASSERT(size2 == size);
89 
90 	/* verify that too small a size is rejected */
91 	err = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN - 1);
92 	ASSERT(err == EINVAL);
93 	CHECKr(pthread_attr_getstacksize(&attr, &size2));
94 	ASSERT(size2 == size);
95 
96 
97 	/* create a thread with the default stack attr so we can test reuse */
98 	CHECKr(pthread_create(&t, NULL, &tmain0, NULL));
99 	sleep(1);
100 	CHECKr(pthread_join(t, &addr));
101 
102 	/*
103 	 * verify that the stack has *not* been freed: we expect it to be
104 	 * cached for reuse.  This is unportable for the same reasons as
105 	 * the mquery() test below.  :-/
106 	 */
107 	*(int *)addr = 100;
108 
109 
110 	/* do the above again and make sure the stack got reused */
111 	CHECKr(pthread_create(&t, NULL, &tmain0, NULL));
112 	sleep(1);
113 	CHECKr(pthread_join(t, &addr2));
114 	ASSERT(addr == addr2);
115 
116 
117 	/*
118 	 * increase the stacksize, then verify that the change sticks,
119 	 * and that a large buffer fits on the resulting thread's stack
120 	 */
121 	size2 += LARGE_SIZE;
122 	CHECKr(pthread_attr_setstacksize(&attr, size2));
123 	CHECKr(pthread_attr_getstacksize(&attr, &size));
124 	ASSERT(size == size2);
125 
126 	CHECKr(pthread_create(&t, &attr, &tmain1, NULL));
127 	sleep(1);
128 	CHECKr(pthread_join(t, &addr));
129 
130 	/* test whether the stack has been freed */
131 	/* XXX yow, this is grossly unportable, as it depends on the stack
132 	 * not being cached, the thread being marked freeable before
133 	 * pthread_join() calls the gc routine (thus the sleep), and this
134 	 * being testable by mquery */
135 	addr = (void *)((uintptr_t)addr & ~(pagesize - 1));
136 	ASSERT(mquery(addr, pagesize, PROT_READ, MAP_FIXED|MAP_ANON, -1, 0)
137 	    == addr);
138 
139 	/* the attr wasn't modified by pthread_create, right? */
140 	size = 1;
141 	CHECKr(pthread_attr_getstacksize(&attr, &size));
142 	ASSERT(size == size2);
143 
144 
145 	/* allocate our own stack and verify the thread uses it */
146 	size = pagesize * 4;
147 	addr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE,
148 	    -1, 0);
149 	ASSERT(addr != MAP_FAILED);
150 	memset(addr, 0xd0, size);
151 	CHECKr(pthread_attr_setstack(&attr, addr, size));
152 
153 	CHECKr(pthread_attr_getstacksize(&attr, &size2));
154 	ASSERT(size2 == size);
155 	CHECKr(pthread_attr_getstackaddr(&attr, &addr2));
156 	ASSERT(addr2 == addr);
157 	CHECKr(pthread_attr_getstack(&attr, &addr2, &size2));
158 	ASSERT(addr2 == addr);
159 	ASSERT(size2 == size);
160 
161 	thread_stack.addr = addr;
162 	thread_stack.size = size;
163 	CHECKr(pthread_create(&t, &attr, &tmain2, &thread_stack));
164 	sleep(1);
165 	CHECKr(pthread_join(t, NULL));
166 
167 	/* verify that the stack we allocated was *not* freed */
168 	memset(addr, 0xd0, size);
169 
170 	return (0);
171 }
172