github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/misc/cgo/testcarchive/main4.c (about)

     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  }