]> Cypherpunks repositories - gostls13.git/commitdiff
os/signal: add ability to ignore signals and restore initial signal handlers
authorMichael MacInnis <michael.p.macinnis@gmail.com>
Fri, 30 Jan 2015 03:37:41 +0000 (22:37 -0500)
committerIan Lance Taylor <iant@golang.org>
Mon, 16 Feb 2015 14:23:09 +0000 (14:23 +0000)
There is currently no way to ignore signals using the os/signal package.
It is possible to catch a signal and do nothing but this is not the same
as ignoring it. The new function Ignore allows a set of signals to be
ignored. The new function Reset allows the initial handlers for a set of
signals to be restored.

Fixes #5572

Change-Id: I5c0f07956971e3a9ff9b9d9631e6e3a08c20df15
Reviewed-on: https://go-review.googlesource.com/3580
Reviewed-by: Ian Lance Taylor <iant@golang.org>
12 files changed:
src/os/signal/sig.s
src/os/signal/signal.go
src/os/signal/signal_plan9.go
src/os/signal/signal_test.go
src/os/signal/signal_unix.go
src/runtime/os1_nacl.go
src/runtime/os1_windows_386.go
src/runtime/os1_windows_amd64.go
src/runtime/os3_plan9.go
src/runtime/signal1_unix.go
src/runtime/sigqueue.go
src/runtime/sigqueue_plan9.go

index 5a042a096407ea50a421bf1f2ea9519c83da69a9..f54e6ff9c032389a51f83bbf851d811192234d11 100644 (file)
@@ -24,6 +24,9 @@ TEXT ·signal_disable(SB),NOSPLIT,$0
 TEXT ·signal_enable(SB),NOSPLIT,$0
        JMP runtime·signal_enable(SB)
 
+TEXT ·signal_ignore(SB),NOSPLIT,$0
+       JMP runtime·signal_ignore(SB)
+
 TEXT ·signal_recv(SB),NOSPLIT,$0
        JMP runtime·signal_recv(SB)
 
index 81906d6f403858b018dc19b370a132809d8018d5..1625786d490ee11fab3a08c5b853cfb668976d0a 100644 (file)
@@ -28,9 +28,55 @@ func (h *handler) set(sig int) {
        h.mask[sig/32] |= 1 << uint(sig&31)
 }
 
+func (h *handler) clear(sig int) {
+       h.mask[sig/32] &^= 1 << uint(sig&31)
+}
+
+// Stop relaying the signals, sigs, to any channels previously registered to
+// receive them and either reset the signal handlers to their original values
+// (action=disableSignal) or ignore the signals (action=ignoreSignal).
+func cancel(sigs []os.Signal, action func(int)) {
+       handlers.Lock()
+       defer handlers.Unlock()
+
+       remove := func(n int) {
+               var zerohandler handler
+
+               for c, h := range handlers.m {
+                       if h.want(n) {
+                               handlers.ref[n]--
+                               h.clear(n)
+                               if h.mask == zerohandler.mask {
+                                       delete(handlers.m, c)
+                               }
+                       }
+               }
+
+               action(n)
+       }
+
+       if len(sigs) == 0 {
+               for n := 0; n < numSig; n++ {
+                       remove(n)
+               }
+       } else {
+               for _, s := range sigs {
+                       remove(signum(s))
+               }
+       }
+}
+
+// Ignore causes the provided signals to be ignored. If they are received by
+// the program, nothing will happen. Ignore undoes the effect of any prior
+// calls to Notify for the provided signals.
+// If no signals are provided, all incoming signals will be ignored.
+func Ignore(sig ...os.Signal) {
+       cancel(sig, ignoreSignal)
+}
+
 // Notify causes package signal to relay incoming signals to c.
-// If no signals are listed, all incoming signals will be relayed to c.
-// Otherwise, just the listed signals will.
+// If no signals are provided, all incoming signals will be relayed to c.
+// Otherwise, just the provided signals will.
 //
 // Package signal will not block sending to c: the caller must ensure
 // that c has sufficient buffer space to keep up with the expected
@@ -85,6 +131,13 @@ func Notify(c chan<- os.Signal, sig ...os.Signal) {
        }
 }
 
+// Reset undoes the effect of any prior calls to Notify for the provided
+// signals.
+// If no signals are provided, all signal handlers will be reset.
+func Reset(sig ...os.Signal) {
+       cancel(sig, disableSignal)
+}
+
 // Stop causes package signal to stop relaying incoming signals to c.
 // It undoes the effect of all prior calls to Notify using c.
 // When Stop returns, it is guaranteed that c will receive no more signals.
index 45355da48acc46f665d127298e6778f3db669ffc..b065ae520d7817a8396f9db5778a8e619190ba5d 100644 (file)
@@ -14,6 +14,7 @@ var sigtab = make(map[os.Signal]int)
 // In sig.s; jumps to runtime.
 func signal_disable(uint32)
 func signal_enable(uint32)
+func signal_ignore(uint32)
 func signal_recv() string
 
 func init() {
@@ -53,3 +54,7 @@ func enableSignal(sig int) {
 func disableSignal(sig int) {
        signal_disable(uint32(sig))
 }
+
+func ignoreSignal(sig int) {
+       signal_ignore(uint32(sig))
+}
index 22337a72d4bdbd52eacd4573a0300e38fd173d7c..a71633c8907b98d9a624e04fdb3b2f9ca0b10c8c 100644 (file)
@@ -109,6 +109,72 @@ func TestStress(t *testing.T) {
        time.Sleep(10 * time.Millisecond)
 }
 
+func testCancel(t *testing.T, ignore bool) {
+       // Send SIGWINCH. By default this signal should be ignored.
+       syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
+       time.Sleep(100 * time.Millisecond)
+
+       // Ask to be notified on c1 when a SIGWINCH is received.
+       c1 := make(chan os.Signal, 1)
+       Notify(c1, syscall.SIGWINCH)
+       defer Stop(c1)
+
+       // Ask to be notified on c2 when a SIGHUP is received.
+       c2 := make(chan os.Signal, 1)
+       Notify(c2, syscall.SIGHUP)
+       defer Stop(c2)
+
+       // Send this process a SIGWINCH and wait for notification on c1.
+       syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
+       waitSig(t, c1, syscall.SIGWINCH)
+
+       // Send this process a SIGHUP and wait for notification on c2.
+       syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
+       waitSig(t, c2, syscall.SIGHUP)
+
+       // Ignore, or reset the signal handlers for, SIGWINCH and SIGHUP.
+       if ignore {
+               Ignore(syscall.SIGWINCH, syscall.SIGHUP)
+       } else {
+               Reset(syscall.SIGWINCH, syscall.SIGHUP)
+       }
+
+       // Send this process a SIGWINCH. It should be ignored.
+       syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
+
+       // If ignoring, Send this process a SIGHUP. It should be ignored.
+       if ignore {
+               syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
+       }
+
+       select {
+       case s := <-c1:
+               t.Fatalf("unexpected signal %v", s)
+       case <-time.After(100 * time.Millisecond):
+               // nothing to read - good
+       }
+
+       select {
+       case s := <-c2:
+               t.Fatalf("unexpected signal %v", s)
+       case <-time.After(100 * time.Millisecond):
+               // nothing to read - good
+       }
+
+       // Reset the signal handlers for all signals.
+       Reset()
+}
+
+// Test that Reset cancels registration for listed signals on all channels.
+func TestReset(t *testing.T) {
+       testCancel(t, false)
+}
+
+// Test that Ignore cancels registration for listed signals on all channels.
+func TestIgnore(t *testing.T) {
+       testCancel(t, true)
+}
+
 var sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop")
 
 // Test that Stop cancels the channel's registrations.
index 94b8ab3ddbf455795b5d222dcc6f1ca71b175e88..1bdf1d7271808b30bbbf06e7aee0448725e94ad1 100644 (file)
@@ -14,6 +14,7 @@ import (
 // In assembly.
 func signal_disable(uint32)
 func signal_enable(uint32)
+func signal_ignore(uint32)
 func signal_recv() uint32
 
 func loop() {
@@ -51,3 +52,7 @@ func enableSignal(sig int) {
 func disableSignal(sig int) {
        signal_disable(uint32(sig))
 }
+
+func ignoreSignal(sig int) {
+       signal_ignore(uint32(sig))
+}
index c44d87645e8d03df70aac79f45fa630590ea671b..a27a13f114c39fbce71246af30b898483ddefb01 100644 (file)
@@ -174,6 +174,7 @@ func munmap(addr unsafe.Pointer, n uintptr)               {}
 func resetcpuprofiler(hz int32)                           {}
 func sigdisable(uint32)                                   {}
 func sigenable(uint32)                                    {}
+func sigignore(uint32)                                    {}
 func closeonexec(int32)                                   {}
 
 var writelock uint32 // test-and-set spin lock for write
index b7eae204d1ce7d9a0dda950207be5d1717dcd04c..f105539659fd82301fa80d0c05d6866a7b2c13d5 100644 (file)
@@ -118,3 +118,6 @@ func sigenable(sig uint32) {
 
 func sigdisable(sig uint32) {
 }
+
+func sigignore(sig uint32) {
+}
index 4163fcf23d59fd991e584588f5d2fd354157a4b9..1b6b99988d9d617392406229d03835ee8ed33c15 100644 (file)
@@ -137,3 +137,6 @@ func sigenable(sig uint32) {
 
 func sigdisable(sig uint32) {
 }
+
+func sigignore(sig uint32) {
+}
index 8ecbca01746279e569e8a15601c2d8b2a6986f70..27519debd355b5d352adf4d780a3a0d809ef3245 100644 (file)
@@ -106,6 +106,9 @@ func sigenable(sig uint32) {
 func sigdisable(sig uint32) {
 }
 
+func sigignore(sig uint32) {
+}
+
 func resetcpuprofiler(hz int32) {
        // TODO: Enable profiling interrupts.
        getg().m.profilehz = hz
index 253ee9f465c0257da5e2230fbb54d541d1360e07..184fd125fb8513e784b657f0ae0a42aac394ad0e 100644 (file)
@@ -78,6 +78,18 @@ func sigdisable(sig uint32) {
        }
 }
 
+func sigignore(sig uint32) {
+       if sig >= uint32(len(sigtable)) {
+               return
+       }
+
+       t := &sigtable[sig]
+       if t.flags&_SigNotify != 0 {
+               t.flags &^= _SigHandling
+               setsig(int32(sig), _SIG_IGN, true)
+       }
+}
+
 func resetcpuprofiler(hz int32) {
        var it itimerval
        if hz == 0 {
index 5cfc926f678d4da79aa7065ab557f0866b64dfdc..df3c9c0e612320d6790e6a8381c64b1d412385cc 100644 (file)
@@ -153,6 +153,15 @@ func signal_disable(s uint32) {
        sigdisable(s)
 }
 
+// Must only be called from a single goroutine at a time.
+func signal_ignore(s uint32) {
+       if int(s) >= len(sig.wanted)*32 {
+               return
+       }
+       sig.wanted[s/32] &^= 1 << (s & 31)
+       sigignore(s)
+}
+
 // This runs on a foreign stack, without an m or a g.  No stack split.
 //go:nosplit
 func badsignal(sig uintptr) {
index b029a300a83cc5656a18529725b1cf548015e0e2..38f0a57b90525f10328b726194a5eea06224abf3 100644 (file)
@@ -113,3 +113,7 @@ func signal_enable(s uint32) {
 // Must only be called from a single goroutine at a time.
 func signal_disable(s uint32) {
 }
+
+// Must only be called from a single goroutine at a time.
+func signal_ignore(s uint32) {
+}