1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Test a C thread that calls sigaltstack and then calls Go code. 6 7 #include <signal.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <time.h> 12 #include <sched.h> 13 #include <pthread.h> 14 15 #include "libgo4.h" 16 17 static void die(const char* msg) { 18 perror(msg); 19 exit(EXIT_FAILURE); 20 } 21 22 static int ok = 1; 23 24 static void ioHandler(int signo, siginfo_t* info, void* ctxt) { 25 } 26 27 // Set up the SIGIO signal handler in a high priority constructor, so 28 // that it is installed before the Go code starts. 29 30 static void init(void) __attribute__ ((constructor (200))); 31 32 static void init() { 33 struct sigaction sa; 34 35 memset(&sa, 0, sizeof sa); 36 sa.sa_sigaction = ioHandler; 37 if (sigemptyset(&sa.sa_mask) < 0) { 38 die("sigemptyset"); 39 } 40 sa.sa_flags = SA_SIGINFO | SA_ONSTACK; 41 if (sigaction(SIGIO, &sa, NULL) < 0) { 42 die("sigaction"); 43 } 44 } 45 46 // Test raising SIGIO on a C thread with an alternate signal stack 47 // when there is a Go signal handler for SIGIO. 48 static void* thread1(void* arg __attribute__ ((unused))) { 49 stack_t ss; 50 int i; 51 stack_t nss; 52 struct timespec ts; 53 54 // Set up an alternate signal stack for this thread. 55 memset(&ss, 0, sizeof ss); 56 ss.ss_sp = malloc(SIGSTKSZ); 57 if (ss.ss_sp == NULL) { 58 die("malloc"); 59 } 60 ss.ss_flags = 0; 61 ss.ss_size = SIGSTKSZ; 62 if (sigaltstack(&ss, NULL) < 0) { 63 die("sigaltstack"); 64 } 65 66 // Send ourselves a SIGIO. This will be caught by the Go 67 // signal handler which should forward to the C signal 68 // handler. 69 i = pthread_kill(pthread_self(), SIGIO); 70 if (i != 0) { 71 fprintf(stderr, "pthread_kill: %s\n", strerror(i)); 72 exit(EXIT_FAILURE); 73 } 74 75 // Wait until the signal has been delivered. 76 i = 0; 77 while (SIGIOCount() == 0) { 78 ts.tv_sec = 0; 79 ts.tv_nsec = 1000000; 80 nanosleep(&ts, NULL); 81 i++; 82 if (i > 5000) { 83 fprintf(stderr, "looping too long waiting for signal\n"); 84 exit(EXIT_FAILURE); 85 } 86 } 87 88 // We should still be on the same signal stack. 89 if (sigaltstack(NULL, &nss) < 0) { 90 die("sigaltstack check"); 91 } 92 if ((nss.ss_flags & SS_DISABLE) != 0) { 93 fprintf(stderr, "sigaltstack disabled on return from Go\n"); 94 ok = 0; 95 } else if (nss.ss_sp != ss.ss_sp) { 96 fprintf(stderr, "sigalstack changed on return from Go\n"); 97 ok = 0; 98 } 99 100 return NULL; 101 } 102 103 // Test calling a Go function to raise SIGIO on a C thread with an 104 // alternate signal stack when there is a Go signal handler for SIGIO. 105 static void* thread2(void* arg __attribute__ ((unused))) { 106 stack_t ss; 107 int i; 108 int oldcount; 109 pthread_t tid; 110 struct timespec ts; 111 stack_t nss; 112 113 // Set up an alternate signal stack for this thread. 114 memset(&ss, 0, sizeof ss); 115 ss.ss_sp = malloc(SIGSTKSZ); 116 if (ss.ss_sp == NULL) { 117 die("malloc"); 118 } 119 ss.ss_flags = 0; 120 ss.ss_size = SIGSTKSZ; 121 if (sigaltstack(&ss, NULL) < 0) { 122 die("sigaltstack"); 123 } 124 125 oldcount = SIGIOCount(); 126 127 // Call a Go function that will call a C function to send us a 128 // SIGIO. 129 tid = pthread_self(); 130 GoRaiseSIGIO(&tid); 131 132 // Wait until the signal has been delivered. 133 i = 0; 134 while (SIGIOCount() == oldcount) { 135 ts.tv_sec = 0; 136 ts.tv_nsec = 1000000; 137 nanosleep(&ts, NULL); 138 i++; 139 if (i > 5000) { 140 fprintf(stderr, "looping too long waiting for signal\n"); 141 exit(EXIT_FAILURE); 142 } 143 } 144 145 // We should still be on the same signal stack. 146 if (sigaltstack(NULL, &nss) < 0) { 147 die("sigaltstack check"); 148 } 149 if ((nss.ss_flags & SS_DISABLE) != 0) { 150 fprintf(stderr, "sigaltstack disabled on return from Go\n"); 151 ok = 0; 152 } else if (nss.ss_sp != ss.ss_sp) { 153 fprintf(stderr, "sigalstack changed on return from Go\n"); 154 ok = 0; 155 } 156 157 return NULL; 158 } 159 160 int main(int argc, char **argv) { 161 pthread_t tid; 162 int i; 163 164 // Tell the Go library to start looking for SIGIO. 165 GoCatchSIGIO(); 166 167 i = pthread_create(&tid, NULL, thread1, NULL); 168 if (i != 0) { 169 fprintf(stderr, "pthread_create: %s\n", strerror(i)); 170 exit(EXIT_FAILURE); 171 } 172 173 i = pthread_join(tid, NULL); 174 if (i != 0) { 175 fprintf(stderr, "pthread_join: %s\n", strerror(i)); 176 exit(EXIT_FAILURE); 177 } 178 179 i = pthread_create(&tid, NULL, thread2, NULL); 180 if (i != 0) { 181 fprintf(stderr, "pthread_create: %s\n", strerror(i)); 182 exit(EXIT_FAILURE); 183 } 184 185 i = pthread_join(tid, NULL); 186 if (i != 0) { 187 fprintf(stderr, "pthread_join: %s\n", strerror(i)); 188 exit(EXIT_FAILURE); 189 } 190 191 if (!ok) { 192 exit(EXIT_FAILURE); 193 } 194 195 printf("PASS\n"); 196 return 0; 197 } 198