| // Copyright 2015 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | // Test a C thread that calls sigaltstack and then calls Go code. | 
 |  | 
 | #include <signal.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <time.h> | 
 | #include <sched.h> | 
 | #include <pthread.h> | 
 |  | 
 | #include "libgo4.h" | 
 |  | 
 | #ifdef _AIX | 
 | // On AIX, CSIGSTKSZ is too small to handle Go sighandler. | 
 | #define CSIGSTKSZ 0x4000 | 
 | #else | 
 | #define CSIGSTKSZ SIGSTKSZ | 
 | #endif | 
 |  | 
 | static void die(const char* msg) { | 
 | 	perror(msg); | 
 | 	exit(EXIT_FAILURE); | 
 | } | 
 |  | 
 | static int ok = 1; | 
 |  | 
 | static void ioHandler(int signo, siginfo_t* info, void* ctxt) { | 
 | } | 
 |  | 
 | // Set up the SIGIO signal handler in a high priority constructor, so | 
 | // that it is installed before the Go code starts. | 
 |  | 
 | static void init(void) __attribute__ ((constructor (200))); | 
 |  | 
 | static void init() { | 
 | 	struct sigaction sa; | 
 |  | 
 | 	memset(&sa, 0, sizeof sa); | 
 | 	sa.sa_sigaction = ioHandler; | 
 | 	if (sigemptyset(&sa.sa_mask) < 0) { | 
 | 		die("sigemptyset"); | 
 | 	} | 
 | 	sa.sa_flags = SA_SIGINFO | SA_ONSTACK; | 
 | 	if (sigaction(SIGIO, &sa, NULL) < 0) { | 
 | 		die("sigaction"); | 
 | 	} | 
 | } | 
 |  | 
 | // Test raising SIGIO on a C thread with an alternate signal stack | 
 | // when there is a Go signal handler for SIGIO. | 
 | static void* thread1(void* arg __attribute__ ((unused))) { | 
 | 	stack_t ss; | 
 | 	int i; | 
 | 	stack_t nss; | 
 | 	struct timespec ts; | 
 |  | 
 | 	// Set up an alternate signal stack for this thread. | 
 | 	memset(&ss, 0, sizeof ss); | 
 | 	ss.ss_sp = malloc(CSIGSTKSZ); | 
 | 	if (ss.ss_sp == NULL) { | 
 | 		die("malloc"); | 
 | 	} | 
 | 	ss.ss_flags = 0; | 
 | 	ss.ss_size = CSIGSTKSZ; | 
 | 	if (sigaltstack(&ss, NULL) < 0) { | 
 | 		die("sigaltstack"); | 
 | 	} | 
 |  | 
 | 	// Send ourselves a SIGIO.  This will be caught by the Go | 
 | 	// signal handler which should forward to the C signal | 
 | 	// handler. | 
 | 	i = pthread_kill(pthread_self(), SIGIO); | 
 | 	if (i != 0) { | 
 | 		fprintf(stderr, "pthread_kill: %s\n", strerror(i)); | 
 | 		exit(EXIT_FAILURE); | 
 | 	} | 
 |  | 
 | 	// Wait until the signal has been delivered. | 
 | 	i = 0; | 
 | 	while (SIGIOCount() == 0) { | 
 | 		ts.tv_sec = 0; | 
 | 		ts.tv_nsec = 1000000; | 
 | 		nanosleep(&ts, NULL); | 
 | 		i++; | 
 | 		if (i > 5000) { | 
 | 			fprintf(stderr, "looping too long waiting for signal\n"); | 
 | 			exit(EXIT_FAILURE); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// We should still be on the same signal stack. | 
 | 	if (sigaltstack(NULL, &nss) < 0) { | 
 | 		die("sigaltstack check"); | 
 | 	} | 
 | 	if ((nss.ss_flags & SS_DISABLE) != 0) { | 
 | 		fprintf(stderr, "sigaltstack disabled on return from Go\n"); | 
 | 		ok = 0; | 
 | 	} else if (nss.ss_sp != ss.ss_sp) { | 
 | 		fprintf(stderr, "sigaltstack changed on return from Go\n"); | 
 | 		ok = 0; | 
 | 	} | 
 |  | 
 | 	return NULL; | 
 | } | 
 |  | 
 | // Test calling a Go function to raise SIGIO on a C thread with an | 
 | // alternate signal stack when there is a Go signal handler for SIGIO. | 
 | static void* thread2(void* arg __attribute__ ((unused))) { | 
 | 	stack_t ss; | 
 | 	int i; | 
 | 	int oldcount; | 
 | 	pthread_t tid; | 
 | 	struct timespec ts; | 
 | 	stack_t nss; | 
 |  | 
 | 	// Set up an alternate signal stack for this thread. | 
 | 	memset(&ss, 0, sizeof ss); | 
 | 	ss.ss_sp = malloc(CSIGSTKSZ); | 
 | 	if (ss.ss_sp == NULL) { | 
 | 		die("malloc"); | 
 | 	} | 
 | 	ss.ss_flags = 0; | 
 | 	ss.ss_size = CSIGSTKSZ; | 
 | 	if (sigaltstack(&ss, NULL) < 0) { | 
 | 		die("sigaltstack"); | 
 | 	} | 
 |  | 
 | 	oldcount = SIGIOCount(); | 
 |  | 
 | 	// Call a Go function that will call a C function to send us a | 
 | 	// SIGIO. | 
 | 	tid = pthread_self(); | 
 | 	GoRaiseSIGIO(&tid); | 
 |  | 
 | 	// Wait until the signal has been delivered. | 
 | 	i = 0; | 
 | 	while (SIGIOCount() == oldcount) { | 
 | 		ts.tv_sec = 0; | 
 | 		ts.tv_nsec = 1000000; | 
 | 		nanosleep(&ts, NULL); | 
 | 		i++; | 
 | 		if (i > 5000) { | 
 | 			fprintf(stderr, "looping too long waiting for signal\n"); | 
 | 			exit(EXIT_FAILURE); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// We should still be on the same signal stack. | 
 | 	if (sigaltstack(NULL, &nss) < 0) { | 
 | 		die("sigaltstack check"); | 
 | 	} | 
 | 	if ((nss.ss_flags & SS_DISABLE) != 0) { | 
 | 		fprintf(stderr, "sigaltstack disabled on return from Go\n"); | 
 | 		ok = 0; | 
 | 	} else if (nss.ss_sp != ss.ss_sp) { | 
 | 		fprintf(stderr, "sigaltstack changed on return from Go\n"); | 
 | 		ok = 0; | 
 | 	} | 
 |  | 
 | 	return NULL; | 
 | } | 
 |  | 
 | int main(int argc, char **argv) { | 
 | 	pthread_t tid; | 
 | 	int i; | 
 |  | 
 | 	// Tell the Go library to start looking for SIGIO. | 
 | 	GoCatchSIGIO(); | 
 |  | 
 | 	i = pthread_create(&tid, NULL, thread1, NULL); | 
 | 	if (i != 0) { | 
 | 		fprintf(stderr, "pthread_create: %s\n", strerror(i)); | 
 | 		exit(EXIT_FAILURE); | 
 | 	} | 
 |  | 
 | 	i = pthread_join(tid, NULL); | 
 | 	if (i != 0) { | 
 | 		fprintf(stderr, "pthread_join: %s\n", strerror(i)); | 
 | 		exit(EXIT_FAILURE); | 
 | 	} | 
 |  | 
 | 	i = pthread_create(&tid, NULL, thread2, NULL); | 
 | 	if (i != 0) { | 
 | 		fprintf(stderr, "pthread_create: %s\n", strerror(i)); | 
 | 		exit(EXIT_FAILURE); | 
 | 	} | 
 |  | 
 | 	i = pthread_join(tid, NULL); | 
 | 	if (i != 0) { | 
 | 		fprintf(stderr, "pthread_join: %s\n", strerror(i)); | 
 | 		exit(EXIT_FAILURE); | 
 | 	} | 
 |  | 
 | 	if (!ok) { | 
 | 		exit(EXIT_FAILURE); | 
 | 	} | 
 |  | 
 | 	printf("PASS\n"); | 
 | 	return 0; | 
 | } |