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