Commit a7d2b4d7 authored by Ian Lance Taylor's avatar Ian Lance Taylor

runtime: disable a signal by restoring the original disposition

Fixes #13034.
Fixes #13042.
Update #9896.

Change-Id: I189f381090223dd07086848aac2d69d2c00d80c4
Reviewed-on: https://go-review.googlesource.com/18062Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent 6c8a141a
// 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 os/signal.Notify and os/signal.Reset.
// This is a lot like misc/cgo/testcshared/main5.c.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include "libgo3.h"
static void die(const char* msg) {
perror(msg);
exit(EXIT_FAILURE);
}
static volatile sig_atomic_t sigioSeen;
static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
sigioSeen = 1;
}
int main(int argc, char** argv) {
int verbose;
struct sigaction sa;
int i;
verbose = argc > 2;
setvbuf(stdout, NULL, _IONBF, 0);
if (verbose) {
printf("calling sigaction\n");
}
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");
}
// At this point there should not be a Go signal handler
// installed for SIGIO.
if (verbose) {
printf("raising SIGIO\n");
}
if (raise(SIGIO) < 0) {
die("raise");
}
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 > 10000) {
fprintf(stderr, "looping too long waiting for signal\n");
exit(EXIT_FAILURE);
}
}
sigioSeen = 0;
// Tell the Go code to catch SIGIO.
if (verbose) {
printf("calling CatchSIGIO\n");
}
CatchSIGIO();
if (verbose) {
printf("raising SIGIO\n");
}
if (raise(SIGIO) < 0) {
die("raise");
}
if (verbose) {
printf("calling SawSIGIO\n");
}
if (!SawSIGIO()) {
fprintf(stderr, "Go handler did not see SIGIO\n");
exit(EXIT_FAILURE);
}
if (sigioSeen != 0) {
fprintf(stderr, "C handler saw SIGIO when only Go handler should have\n");
exit(EXIT_FAILURE);
}
// Tell the Go code to stop catching SIGIO.
if (verbose) {
printf("calling ResetSIGIO\n");
}
ResetSIGIO();
if (verbose) {
printf("raising SIGIO\n");
}
if (raise(SIGIO) < 0) {
die("raise");
}
if (verbose) {
printf("calling SawSIGIO\n");
}
if (SawSIGIO()) {
fprintf(stderr, "Go handler saw SIGIO after Reset\n");
exit(EXIT_FAILURE);
}
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 > 10000) {
fprintf(stderr, "looping too long waiting for signal\n");
exit(EXIT_FAILURE);
}
}
printf("PASS\n");
return 0;
}
// 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.
package main
import "C"
import (
"os"
"os/signal"
"syscall"
"time"
)
// The channel used to read SIGIO signals.
var sigioChan chan os.Signal
// CatchSIGIO starts catching SIGIO signals.
//export CatchSIGIO
func CatchSIGIO() {
sigioChan = make(chan os.Signal, 1)
signal.Notify(sigioChan, syscall.SIGIO)
}
// ResetSIGIO stops catching SIGIO signals.
//export ResetSIGIO
func ResetSIGIO() {
signal.Reset(syscall.SIGIO)
}
// SawSIGIO returns whether we saw a SIGIO within a brief pause.
//export SawSIGIO
func SawSIGIO() C.int {
select {
case <-sigioChan:
return 1
case <-time.After(100 * time.Millisecond):
return 0
}
}
func main() {
}
...@@ -30,7 +30,7 @@ status=0 ...@@ -30,7 +30,7 @@ status=0
GOPATH=$(pwd) go install -buildmode=c-archive libgo GOPATH=$(pwd) go install -buildmode=c-archive libgo
$(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main.c pkg/$(go env GOOS)_$(go env GOARCH)/libgo.a $(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main.c pkg/$(go env GOOS)_$(go env GOARCH)/libgo.a
if ! $bin arg1 arg2; then if ! $bin arg1 arg2; then
echo "FAIL test1" echo "FAIL test1a"
status=1 status=1
fi fi
rm -f libgo.a libgo.h testp rm -f libgo.a libgo.h testp
...@@ -41,7 +41,7 @@ rm -f libgo.a libgo.h testp ...@@ -41,7 +41,7 @@ rm -f libgo.a libgo.h testp
GOPATH=$(pwd) go build -buildmode=c-archive src/libgo/libgo.go GOPATH=$(pwd) go build -buildmode=c-archive src/libgo/libgo.go
$(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main.c libgo.a $(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main.c libgo.a
if ! $bin arg1 arg2; then if ! $bin arg1 arg2; then
echo "FAIL test2" echo "FAIL test1b"
status=1 status=1
fi fi
rm -f libgo.a libgo.h testp rm -f libgo.a libgo.h testp
...@@ -49,24 +49,32 @@ rm -f libgo.a libgo.h testp ...@@ -49,24 +49,32 @@ rm -f libgo.a libgo.h testp
GOPATH=$(pwd) go build -buildmode=c-archive -o libgo.a libgo GOPATH=$(pwd) go build -buildmode=c-archive -o libgo.a libgo
$(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main.c libgo.a $(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main.c libgo.a
if ! $bin arg1 arg2; then if ! $bin arg1 arg2; then
echo "FAIL test3" echo "FAIL test1c"
status=1 status=1
fi fi
rm -rf libgo.a libgo.h testp pkg rm -rf libgo.a libgo.h testp pkg
case "$(go env GOOS)/$(go env GOARCH)" in case "$(go env GOOS)/$(go env GOARCH)" in
"darwin/arm" | "darwin/arm64") "darwin/arm" | "darwin/arm64")
echo "Skipping test4; see https://golang.org/issue/13701" echo "Skipping test2; see https://golang.org/issue/13701"
;; ;;
*) *)
GOPATH=$(pwd) go build -buildmode=c-archive -o libgo2.a libgo2 GOPATH=$(pwd) go build -buildmode=c-archive -o libgo2.a libgo2
$(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main2.c libgo2.a $(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main2.c libgo2.a
if ! $bin; then if ! $bin; then
echo "FAIL test4" echo "FAIL test2"
status=1 status=1
fi fi
rm -rf libgo2.a libgo2.h testp pkg rm -rf libgo2.a libgo2.h testp pkg
;; ;;
esac esac
GOPATH=$(pwd) go build -buildmode=c-archive -o libgo3.a libgo3
$(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main3.c libgo3.a
if ! $bin; then
echo "FAIL test3"
status=1
fi
rm -rf libgo3.a libgo3.h testp pkg
exit $status exit $status
// 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 that a signal handler works in non-Go code when using
// os/signal.Notify.
// This is a lot like misc/cgo/testcarchive/main3.c.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include <dlfcn.h>
static void die(const char* msg) {
perror(msg);
exit(EXIT_FAILURE);
}
static volatile sig_atomic_t sigioSeen;
static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
sigioSeen = 1;
}
int main(int argc, char** argv) {
int verbose;
struct sigaction sa;
void* handle;
void (*fn1)(void);
int (*sawSIGIO)(void);
int i;
verbose = argc > 2;
setvbuf(stdout, NULL, _IONBF, 0);
if (verbose) {
printf("calling sigaction\n");
}
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");
}
if (verbose) {
printf("calling dlopen\n");
}
handle = dlopen(argv[1], RTLD_NOW | RTLD_GLOBAL);
if (handle == NULL) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
// At this point there should not be a Go signal handler
// installed for SIGIO.
if (verbose) {
printf("raising SIGIO\n");
}
if (raise(SIGIO) < 0) {
die("raise");
}
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 > 10000) {
fprintf(stderr, "looping too long waiting for signal\n");
exit(EXIT_FAILURE);
}
}
sigioSeen = 0;
// Tell the Go code to catch SIGIO.
if (verbose) {
printf("calling dlsym\n");
}
fn1 = (void(*)(void))dlsym(handle, "CatchSIGIO");
if (fn1 == NULL) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
if (verbose) {
printf("calling CatchSIGIO\n");
}
fn1();
if (verbose) {
printf("raising SIGIO\n");
}
if (raise(SIGIO) < 0) {
die("raise");
}
if (verbose) {
printf("calling dlsym\n");
}
// Check that the Go code saw SIGIO.
sawSIGIO = (int (*)(void))dlsym(handle, "SawSIGIO");
if (sawSIGIO == NULL) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
if (verbose) {
printf("calling SawSIGIO\n");
}
if (!sawSIGIO()) {
fprintf(stderr, "Go handler did not see SIGIO\n");
exit(EXIT_FAILURE);
}
if (sigioSeen != 0) {
fprintf(stderr, "C handler saw SIGIO when only Go handler should have\n");
exit(EXIT_FAILURE);
}
// Tell the Go code to stop catching SIGIO.
if (verbose) {
printf("calling dlsym\n");
}
fn1 = (void(*)(void))dlsym(handle, "ResetSIGIO");
if (fn1 == NULL) {
fprintf(stderr, "%s\n", dlerror());
exit(EXIT_FAILURE);
}
if (verbose) {
printf("calling ResetSIGIO\n");
}
fn1();
if (verbose) {
printf("raising SIGIO\n");
}
if (raise(SIGIO) < 0) {
die("raise");
}
if (verbose) {
printf("calling SawSIGIO\n");
}
if (sawSIGIO()) {
fprintf(stderr, "Go handler saw SIGIO after Reset\n");
exit(EXIT_FAILURE);
}
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 > 10000) {
fprintf(stderr, "looping too long waiting for signal\n");
exit(EXIT_FAILURE);
}
}
printf("PASS\n");
return 0;
}
// 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.
package main
import "C"
import (
"os"
"os/signal"
"syscall"
"time"
)
// The channel used to read SIGIO signals.
var sigioChan chan os.Signal
// CatchSIGIO starts catching SIGIO signals.
//export CatchSIGIO
func CatchSIGIO() {
sigioChan = make(chan os.Signal, 1)
signal.Notify(sigioChan, syscall.SIGIO)
}
// ResetSIGIO stops catching SIGIO signals.
//export ResetSIGIO
func ResetSIGIO() {
signal.Reset(syscall.SIGIO)
}
// SawSIGIO returns whether we saw a SIGIO within a brief pause.
//export SawSIGIO
func SawSIGIO() C.int {
select {
case <-sigioChan:
return 1
case <-time.After(100 * time.Millisecond):
return 0
}
}
func main() {
}
...@@ -33,8 +33,9 @@ fi ...@@ -33,8 +33,9 @@ fi
androidpath=/data/local/tmp/testcshared-$$ androidpath=/data/local/tmp/testcshared-$$
function cleanup() { function cleanup() {
rm -f libgo.$libext libgo2.$libext libgo4.$libext libgo.h libgo4.h rm -f libgo.$libext libgo2.$libext libgo4.$libext libgo5.$libext
rm -f testp testp2 testp3 testp4 rm -f libgo.h libgo4.h libgo5.h
rm -f testp testp2 testp3 testp4 testp5
rm -rf pkg "${goroot}/${installdir}" rm -rf pkg "${goroot}/${installdir}"
if [ "$goos" == "android" ]; then if [ "$goos" == "android" ]; then
...@@ -161,6 +162,21 @@ if test "$output" != "PASS"; then ...@@ -161,6 +162,21 @@ if test "$output" != "PASS"; then
status=1 status=1
fi fi
# test5: tests signal handlers with os/signal.Notify
GOPATH=$(pwd) go build -buildmode=c-shared $suffix -o libgo5.$libext libgo5
binpush libgo5.$libext
$(go env CC) ${GOGCCFLAGS} -pthread -o testp5 main5.c -ldl
binpush testp5
output=$(run ./testp5 ./libgo5.$libext 2>&1)
if test "$output" != "PASS"; then
echo "FAIL test5 got ${output}"
if test "$goos" != "android"; then
echo "re-running test5 in verbose mode"
./testp5 ./libgo5.$libext verbose
fi
status=1
fi
if test $status = 0; then if test $status = 0; then
echo "ok" echo "ok"
fi fi
......
...@@ -162,13 +162,20 @@ signal arrives while executing non-Go code, the Go runtime will invoke ...@@ -162,13 +162,20 @@ signal arrives while executing non-Go code, the Go runtime will invoke
the existing signal handler instead of the Go signal handler. the existing signal handler instead of the Go signal handler.
Go code built with -buildmode=c-archive or -buildmode=c-shared will Go code built with -buildmode=c-archive or -buildmode=c-shared will
not install any other signal handlers. TODO: Describe Notify behavior. not install any other signal handlers by default. If there is an
existing signal handler, the Go runtime will turn on the SA_ONSTACK
Go code built otherwise will install a signal handler for the flag and otherwise keep the signal handler. If Notify is called for an
asynchronous signals listed above, and save any existing signal asynchronous signal, a Go signal handler will be installed for that
handler. If a signal is delivered to a non-Go thread, it will act as signal. If, later, Reset is called for that signal, the original
described above, except that if there is an existing non-Go signal handling for that signal will be reinstalled, restoring the non-Go
handler, that handler will be installed before raising the signal. signal handler if any.
Go code built without -buildmode=c-archive or -buildmode=c-shared will
install a signal handler for the asynchronous signals listed above,
and save any existing signal handler. If a signal is delivered to a
non-Go thread, it will act as described above, except that if there is
an existing non-Go signal handler, that handler will be installed
before raising the signal.
Windows Windows
......
...@@ -482,7 +482,6 @@ const ( ...@@ -482,7 +482,6 @@ const (
_SigPanic // if the signal is from the kernel, panic _SigPanic // if the signal is from the kernel, panic
_SigDefault // if the signal isn't explicitly requested, don't monitor it _SigDefault // if the signal isn't explicitly requested, don't monitor it
_SigHandling // our signal handler is registered _SigHandling // our signal handler is registered
_SigIgnored // the signal was ignored before we registered for it
_SigGoExit // cause all runtime procs to exit (only used on Plan 9). _SigGoExit // cause all runtime procs to exit (only used on Plan 9).
_SigSetStack // add SA_ONSTACK to libc handler _SigSetStack // add SA_ONSTACK to libc handler
_SigUnblock // unblocked in minit _SigUnblock // unblocked in minit
......
...@@ -49,13 +49,13 @@ func initsig() { ...@@ -49,13 +49,13 @@ func initsig() {
continue continue
} }
fwdSig[i] = getsig(i) fwdSig[i] = getsig(i)
// For some signals, we respect an inherited SIG_IGN handler // For some signals, we respect an inherited SIG_IGN handler
// rather than insist on installing our own default handler. // rather than insist on installing our own default handler.
// Even these signals can be fetched using the os/signal package. // Even these signals can be fetched using the os/signal package.
switch i { switch i {
case _SIGHUP, _SIGINT: case _SIGHUP, _SIGINT:
if getsig(i) == _SIG_IGN { if fwdSig[i] == _SIG_IGN {
t.flags = _SigNotify | _SigIgnored
continue continue
} }
} }
...@@ -90,9 +90,6 @@ func sigenable(sig uint32) { ...@@ -90,9 +90,6 @@ func sigenable(sig uint32) {
<-maskUpdatedChan <-maskUpdatedChan
if t.flags&_SigHandling == 0 { if t.flags&_SigHandling == 0 {
t.flags |= _SigHandling t.flags |= _SigHandling
if getsig(int32(sig)) == _SIG_IGN {
t.flags |= _SigIgnored
}
setsig(int32(sig), funcPC(sighandler), true) setsig(int32(sig), funcPC(sighandler), true)
} }
} }
...@@ -110,11 +107,7 @@ func sigdisable(sig uint32) { ...@@ -110,11 +107,7 @@ func sigdisable(sig uint32) {
<-maskUpdatedChan <-maskUpdatedChan
if t.flags&_SigHandling != 0 { if t.flags&_SigHandling != 0 {
t.flags &^= _SigHandling t.flags &^= _SigHandling
if t.flags&_SigIgnored != 0 { setsig(int32(sig), fwdSig[sig], true)
setsig(int32(sig), _SIG_IGN, true)
} else {
setsig(int32(sig), _SIG_DFL, true)
}
} }
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment