github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/misc/cgo/testcshared/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 that a signal handler that uses up stack space does not crash
     6  // if the signal is delivered to a thread running a goroutine.
     7  // This is a lot like misc/cgo/testcarchive/main2.c.
     8  
     9  #include <setjmp.h>
    10  #include <signal.h>
    11  #include <stddef.h>
    12  #include <stdio.h>
    13  #include <stdlib.h>
    14  #include <string.h>
    15  #include <sys/types.h>
    16  #include <unistd.h>
    17  #include <sched.h>
    18  #include <time.h>
    19  #include <dlfcn.h>
    20  
    21  static void die(const char* msg) {
    22  	perror(msg);
    23  	exit(EXIT_FAILURE);
    24  }
    25  
    26  static volatile sig_atomic_t sigioSeen;
    27  
    28  // Use up some stack space.
    29  static void recur(int i, char *p) {
    30  	char a[1024];
    31  
    32  	*p = '\0';
    33  	if (i > 0) {
    34  		recur(i - 1, a);
    35  	}
    36  }
    37  
    38  // Signal handler that uses up more stack space than a goroutine will have.
    39  static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
    40  	char a[1024];
    41  
    42  	recur(4, a);
    43  	sigioSeen = 1;
    44  }
    45  
    46  static jmp_buf jmp;
    47  static char* nullPointer;
    48  
    49  // Signal handler for SIGSEGV on a C thread.
    50  static void segvHandler(int signo, siginfo_t* info, void* ctxt) {
    51  	sigset_t mask;
    52  	int i;
    53  
    54  	if (sigemptyset(&mask) < 0) {
    55  		die("sigemptyset");
    56  	}
    57  	if (sigaddset(&mask, SIGSEGV) < 0) {
    58  		die("sigaddset");
    59  	}
    60  	i = sigprocmask(SIG_UNBLOCK, &mask, NULL);
    61  	if (i != 0) {
    62  		fprintf(stderr, "sigprocmask: %s\n", strerror(i));
    63  		exit(EXIT_FAILURE);
    64  	}
    65  
    66  	// Don't try this at home.
    67  	longjmp(jmp, signo);
    68  
    69  	// We should never get here.
    70  	abort();
    71  }
    72  
    73  int main(int argc, char** argv) {
    74  	int verbose;
    75  	struct sigaction sa;
    76  	void* handle;
    77  	void (*fn)(void);
    78  	sigset_t mask;
    79  	int i;
    80  
    81  	verbose = argc > 2;
    82  	setvbuf(stdout, NULL, _IONBF, 0);
    83  
    84  	// Call setsid so that we can use kill(0, SIGIO) below.
    85  	// Don't check the return value so that this works both from
    86  	// a job control shell and from a shell script.
    87  	setsid();
    88  
    89  	if (verbose) {
    90  		printf("calling sigaction\n");
    91  	}
    92  
    93  	memset(&sa, 0, sizeof sa);
    94  	sa.sa_sigaction = ioHandler;
    95  	if (sigemptyset(&sa.sa_mask) < 0) {
    96  		die("sigemptyset");
    97  	}
    98  	sa.sa_flags = SA_SIGINFO;
    99  	if (sigaction(SIGIO, &sa, NULL) < 0) {
   100  		die("sigaction");
   101  	}
   102  
   103  	sa.sa_sigaction = segvHandler;
   104  	if (sigaction(SIGSEGV, &sa, NULL) < 0 || sigaction(SIGBUS, &sa, NULL) < 0) {
   105  		die("sigaction");
   106  	}
   107  
   108  	if (verbose) {
   109  		printf("calling dlopen\n");
   110  	}
   111  
   112  	handle = dlopen(argv[1], RTLD_NOW | RTLD_GLOBAL);
   113  	if (handle == NULL) {
   114  		fprintf(stderr, "%s\n", dlerror());
   115  		exit(EXIT_FAILURE);
   116  	}
   117  
   118  	if (verbose) {
   119  		printf("calling dlsym\n");
   120  	}
   121  
   122  	// Start some goroutines.
   123  	fn = (void(*)(void))dlsym(handle, "RunGoroutines");
   124  	if (fn == NULL) {
   125  		fprintf(stderr, "%s\n", dlerror());
   126  		exit(EXIT_FAILURE);
   127  	}
   128  
   129  	if (verbose) {
   130  		printf("calling RunGoroutines\n");
   131  	}
   132  
   133  	fn();
   134  
   135  	// Block SIGIO in this thread to make it more likely that it
   136  	// will be delivered to a goroutine.
   137  
   138  	if (verbose) {
   139  		printf("calling pthread_sigmask\n");
   140  	}
   141  
   142  	if (sigemptyset(&mask) < 0) {
   143  		die("sigemptyset");
   144  	}
   145  	if (sigaddset(&mask, SIGIO) < 0) {
   146  		die("sigaddset");
   147  	}
   148  	i = pthread_sigmask(SIG_BLOCK, &mask, NULL);
   149  	if (i != 0) {
   150  		fprintf(stderr, "pthread_sigmask: %s\n", strerror(i));
   151  		exit(EXIT_FAILURE);
   152  	}
   153  
   154  	if (verbose) {
   155  		printf("calling kill\n");
   156  	}
   157  
   158  	if (kill(0, SIGIO) < 0) {
   159  		die("kill");
   160  	}
   161  
   162  	if (verbose) {
   163  		printf("waiting for sigioSeen\n");
   164  	}
   165  
   166  	// Wait until the signal has been delivered.
   167  	i = 0;
   168  	while (!sigioSeen) {
   169  		if (sched_yield() < 0) {
   170  			perror("sched_yield");
   171  		}
   172  		i++;
   173  		if (i > 100000) {
   174  			fprintf(stderr, "looping too long waiting for signal\n");
   175  			exit(EXIT_FAILURE);
   176  		}
   177  	}
   178  
   179  	if (verbose) {
   180  		printf("calling setjmp\n");
   181  	}
   182  
   183  	// Test that a SIGSEGV on this thread is delivered to us.
   184  	if (setjmp(jmp) == 0) {
   185  		if (verbose) {
   186  			printf("triggering SIGSEGV\n");
   187  		}
   188  
   189  		*nullPointer = '\0';
   190  
   191  		fprintf(stderr, "continued after address error\n");
   192  		exit(EXIT_FAILURE);
   193  	}
   194  
   195  	if (verbose) {
   196  		printf("calling dlsym\n");
   197  	}
   198  
   199  	// Make sure that a SIGSEGV in Go causes a run-time panic.
   200  	fn = (void (*)(void))dlsym(handle, "TestSEGV");
   201  	if (fn == NULL) {
   202  		fprintf(stderr, "%s\n", dlerror());
   203  		exit(EXIT_FAILURE);
   204  	}
   205  
   206  	if (verbose) {
   207  		printf("calling TestSEGV\n");
   208  	}
   209  
   210  	fn();
   211  
   212  	printf("PASS\n");
   213  	return 0;
   214  }