]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: disable a signal by restoring the original disposition
authorIan Lance Taylor <iant@golang.org>
Fri, 18 Dec 2015 23:29:51 +0000 (15:29 -0800)
committerIan Lance Taylor <iant@golang.org>
Tue, 5 Jan 2016 00:25:50 +0000 (00:25 +0000)
Fixes #13034.
Fixes #13042.
Update #9896.

Change-Id: I189f381090223dd07086848aac2d69d2c00d80c4
Reviewed-on: https://go-review.googlesource.com/18062
Reviewed-by: Russ Cox <rsc@golang.org>
misc/cgo/testcarchive/main3.c [new file with mode: 0644]
misc/cgo/testcarchive/src/libgo3/libgo3.go [new file with mode: 0644]
misc/cgo/testcarchive/test.bash
misc/cgo/testcshared/main5.c [new file with mode: 0644]
misc/cgo/testcshared/src/libgo5/libgo5.go [new file with mode: 0644]
misc/cgo/testcshared/test.bash
src/os/signal/doc.go
src/runtime/runtime2.go
src/runtime/signal1_unix.go

diff --git a/misc/cgo/testcarchive/main3.c b/misc/cgo/testcarchive/main3.c
new file mode 100644 (file)
index 0000000..2d3e565
--- /dev/null
@@ -0,0 +1,153 @@
+// 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;
+}
diff --git a/misc/cgo/testcarchive/src/libgo3/libgo3.go b/misc/cgo/testcarchive/src/libgo3/libgo3.go
new file mode 100644 (file)
index 0000000..94e5d21
--- /dev/null
@@ -0,0 +1,44 @@
+// 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() {
+}
index d561c02ab7cc48711afde98f9e9c93c2d4344ca9..053833ad94869e5559067352c75744a13c7d72cd 100755 (executable)
@@ -30,7 +30,7 @@ status=0
 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
 if ! $bin arg1 arg2; then
-    echo "FAIL test1"
+    echo "FAIL test1a"
     status=1
 fi
 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
 $(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main.c libgo.a
 if ! $bin arg1 arg2; then
-    echo "FAIL test2"
+    echo "FAIL test1b"
     status=1
 fi
 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
 $(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main.c libgo.a
 if ! $bin arg1 arg2; then
-    echo "FAIL test3"
+    echo "FAIL test1c"
     status=1
 fi
 rm -rf libgo.a libgo.h testp pkg
 
 case "$(go env GOOS)/$(go env GOARCH)" in
 "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
     $(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main2.c libgo2.a
     if ! $bin; then
-        echo "FAIL test4"
+        echo "FAIL test2"
         status=1
     fi
     rm -rf libgo2.a libgo2.h testp pkg
     ;;
 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
diff --git a/misc/cgo/testcshared/main5.c b/misc/cgo/testcshared/main5.c
new file mode 100644 (file)
index 0000000..50ddb47
--- /dev/null
@@ -0,0 +1,197 @@
+// 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;
+}
diff --git a/misc/cgo/testcshared/src/libgo5/libgo5.go b/misc/cgo/testcshared/src/libgo5/libgo5.go
new file mode 100644 (file)
index 0000000..94e5d21
--- /dev/null
@@ -0,0 +1,44 @@
+// 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() {
+}
index 162a62d80e7ddfcd18a2458ebc158c3778ab2903..ac852a007a9ac4c4ee370f7602f680c3b6c88d25 100755 (executable)
@@ -33,8 +33,9 @@ fi
 androidpath=/data/local/tmp/testcshared-$$
 
 function cleanup() {
-       rm -f libgo.$libext libgo2.$libext libgo4.$libext libgo.h libgo4.h
-       rm -f testp testp2 testp3 testp4
+       rm -f libgo.$libext libgo2.$libext libgo4.$libext libgo5.$libext
+       rm -f libgo.h libgo4.h libgo5.h
+       rm -f testp testp2 testp3 testp4 testp5
        rm -rf pkg "${goroot}/${installdir}"
 
        if [ "$goos" == "android" ]; then
@@ -161,6 +162,21 @@ if test "$output" != "PASS"; then
     status=1
 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
     echo "ok"
 fi
index 4a6d1d5c3a4623a43a9152fa41150b6ca6318261..b36c16c8a9e584cc9ea567d6d1d744ae64029835 100644 (file)
@@ -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.
 
 Go code built with -buildmode=c-archive or -buildmode=c-shared will
-not install any other signal handlers. TODO: Describe Notify behavior.
-
-Go code built otherwise 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.
+not install any other signal handlers by default. If there is an
+existing signal handler, the Go runtime will turn on the SA_ONSTACK
+flag and otherwise keep the signal handler. If Notify is called for an
+asynchronous signal, a Go signal handler will be installed for that
+signal. If, later, Reset is called for that signal, the original
+handling for that signal will be reinstalled, restoring the non-Go
+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
 
index 9549d1f531b3dda31d38e293b434bfdf67b826e9..c357f6e6d5e81ab637fad6206620a9852970fa9b 100644 (file)
@@ -482,7 +482,6 @@ const (
        _SigPanic                // if the signal is from the kernel, panic
        _SigDefault              // if the signal isn't explicitly requested, don't monitor it
        _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).
        _SigSetStack             // add SA_ONSTACK to libc handler
        _SigUnblock              // unblocked in minit
index 3bb3ed831272f7b6350dc2bea941375f5b1c8c65..468d6f6946d8463cd79af136ff8f67a008b560a3 100644 (file)
@@ -49,13 +49,13 @@ func initsig() {
                        continue
                }
                fwdSig[i] = getsig(i)
+
                // For some signals, we respect an inherited SIG_IGN handler
                // rather than insist on installing our own default handler.
                // Even these signals can be fetched using the os/signal package.
                switch i {
                case _SIGHUP, _SIGINT:
-                       if getsig(i) == _SIG_IGN {
-                               t.flags = _SigNotify | _SigIgnored
+                       if fwdSig[i] == _SIG_IGN {
                                continue
                        }
                }
@@ -90,9 +90,6 @@ func sigenable(sig uint32) {
                <-maskUpdatedChan
                if t.flags&_SigHandling == 0 {
                        t.flags |= _SigHandling
-                       if getsig(int32(sig)) == _SIG_IGN {
-                               t.flags |= _SigIgnored
-                       }
                        setsig(int32(sig), funcPC(sighandler), true)
                }
        }
@@ -110,11 +107,7 @@ func sigdisable(sig uint32) {
                <-maskUpdatedChan
                if t.flags&_SigHandling != 0 {
                        t.flags &^= _SigHandling
-                       if t.flags&_SigIgnored != 0 {
-                               setsig(int32(sig), _SIG_IGN, true)
-                       } else {
-                               setsig(int32(sig), _SIG_DFL, true)
-                       }
+                       setsig(int32(sig), fwdSig[sig], true)
                }
        }
 }