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