|  | // 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" | 
|  |  | 
|  | 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(SIGSTKSZ); | 
|  | if (ss.ss_sp == NULL) { | 
|  | die("malloc"); | 
|  | } | 
|  | ss.ss_flags = 0; | 
|  | ss.ss_size = SIGSTKSZ; | 
|  | 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, "sigalstack 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(SIGSTKSZ); | 
|  | if (ss.ss_sp == NULL) { | 
|  | die("malloc"); | 
|  | } | 
|  | ss.ss_flags = 0; | 
|  | ss.ss_size = SIGSTKSZ; | 
|  | 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, "sigalstack 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; | 
|  | } |