Home | History | Annotate | Download | only in testcarchive
      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