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