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