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