--- /dev/null
+// Copyright 2009 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 signal
+
+import (
+ "os"
+ "runtime"
+ "syscall"
+ "testing"
+ "time"
+)
+
+func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) {
+ select {
+ case s := <-c:
+ if s != sig {
+ t.Fatalf("signal was %v, want %v", s, sig)
+ }
+ case <-time.After(1 * time.Second):
+ t.Fatalf("timeout waiting for %v", sig)
+ }
+}
+
+// Test that basic signal handling works.
+func TestSignal(t *testing.T) {
+ // Ask for hangup
+ c := make(chan os.Signal, 1)
+ Notify(c, syscall.Note("hangup"))
+ defer Stop(c)
+
+ // Send this process a hangup
+ t.Logf("hangup...")
+ postNote(syscall.Getpid(), "hangup")
+ waitSig(t, c, syscall.Note("hangup"))
+
+ // Ask for everything we can get.
+ c1 := make(chan os.Signal, 1)
+ Notify(c1)
+
+ // Send this process an alarm
+ t.Logf("alarm...")
+ postNote(syscall.Getpid(), "alarm")
+ waitSig(t, c1, syscall.Note("alarm"))
+
+ // Send two more hangups, to make sure that
+ // they get delivered on c1 and that not reading
+ // from c does not block everything.
+ t.Logf("hangup...")
+ postNote(syscall.Getpid(), "hangup")
+ waitSig(t, c1, syscall.Note("hangup"))
+ t.Logf("hangup...")
+ postNote(syscall.Getpid(), "hangup")
+ waitSig(t, c1, syscall.Note("hangup"))
+
+ // The first SIGHUP should be waiting for us on c.
+ waitSig(t, c, syscall.Note("hangup"))
+}
+
+func TestStress(t *testing.T) {
+ dur := 3 * time.Second
+ if testing.Short() {
+ dur = 100 * time.Millisecond
+ }
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
+ done := make(chan bool)
+ finished := make(chan bool)
+ go func() {
+ sig := make(chan os.Signal, 1)
+ Notify(sig, syscall.Note("alarm"))
+ defer Stop(sig)
+ Loop:
+ for {
+ select {
+ case <-sig:
+ case <-done:
+ break Loop
+ }
+ }
+ finished <- true
+ }()
+ go func() {
+ Loop:
+ for {
+ select {
+ case <-done:
+ break Loop
+ default:
+ postNote(syscall.Getpid(), "alarm")
+ runtime.Gosched()
+ }
+ }
+ finished <- true
+ }()
+ time.Sleep(dur)
+ close(done)
+ <-finished
+ <-finished
+ // When run with 'go test -cpu=1,2,4' alarm from this test can slip
+ // into subsequent TestSignal() causing failure.
+ // Sleep for a while to reduce the possibility of the failure.
+ time.Sleep(10 * time.Millisecond)
+}
+
+// Test that Stop cancels the channel's registrations.
+func TestStop(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping in short mode")
+ }
+ sigs := []string{
+ "alarm",
+ "hangup",
+ }
+
+ for _, sig := range sigs {
+ // Send the signal.
+ // If it's alarm, we should not see it.
+ // If it's hangup, maybe we'll die. Let the flag tell us what to do.
+ if sig != "hangup" {
+ postNote(syscall.Getpid(), sig)
+ }
+ time.Sleep(100 * time.Millisecond)
+
+ // Ask for signal
+ c := make(chan os.Signal, 1)
+ Notify(c, syscall.Note(sig))
+ defer Stop(c)
+
+ // Send this process that signal
+ postNote(syscall.Getpid(), sig)
+ waitSig(t, c, syscall.Note(sig))
+
+ Stop(c)
+ select {
+ case s := <-c:
+ t.Fatalf("unexpected signal %v", s)
+ case <-time.After(100 * time.Millisecond):
+ // nothing to read - good
+ }
+
+ // Send the signal.
+ // If it's alarm, we should not see it.
+ // If it's hangup, maybe we'll die. Let the flag tell us what to do.
+ if sig != "hangup" {
+ postNote(syscall.Getpid(), sig)
+ }
+
+ select {
+ case s := <-c:
+ t.Fatalf("unexpected signal %v", s)
+ case <-time.After(100 * time.Millisecond):
+ // nothing to read - good
+ }
+ }
+}
+
+func itoa(val int) string {
+ if val < 0 {
+ return "-" + itoa(-val)
+ }
+ var buf [32]byte // big enough for int64
+ i := len(buf) - 1
+ for val >= 10 {
+ buf[i] = byte(val%10 + '0')
+ i--
+ val /= 10
+ }
+ buf[i] = byte(val + '0')
+ return string(buf[i:])
+}
+
+func postNote(pid int, note string) error {
+ f, err := os.OpenFile("/proc/"+itoa(pid)+"/note", os.O_WRONLY, 0)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ _, err = f.Write([]byte(note))
+ return err
+}
--- /dev/null
+// Copyright 2009 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.
+
+// This file implements runtime support for signal handling.
+
+package runtime
+
+const qsize = 64
+
+var sig struct {
+ q noteQueue
+ inuse bool
+
+ lock mutex
+ note note
+ sleeping bool
+}
+
+type noteQueue struct {
+ lock mutex
+ data [qsize]*byte
+ ri int
+ wi int
+ full bool
+}
+
+func (q *noteQueue) push(item *byte) bool {
+ lock(&q.lock)
+ if q.full {
+ unlock(&q.lock)
+ return false
+ }
+ q.data[q.wi] = item
+ q.wi++
+ if q.wi == qsize {
+ q.wi = 0
+ }
+ if q.wi == q.ri {
+ q.full = true
+ }
+ unlock(&q.lock)
+ return true
+}
+
+func (q *noteQueue) pop() *byte {
+ lock(&q.lock)
+ q.full = false
+ if q.ri == q.wi {
+ unlock(&q.lock)
+ return nil
+ }
+ item := q.data[q.ri]
+ q.ri++
+ if q.ri == qsize {
+ q.ri = 0
+ }
+ unlock(&q.lock)
+ return item
+}
+
+// Called from sighandler to send a signal back out of the signal handling thread.
+// Reports whether the signal was sent. If not, the caller typically crashes the program.
+func sendNote(s *byte) bool {
+ if !sig.inuse {
+ return false
+ }
+
+ // Add signal to outgoing queue.
+ if !sig.q.push(s) {
+ return false
+ }
+
+ lock(&sig.lock)
+ if sig.sleeping {
+ sig.sleeping = false
+ notewakeup(&sig.note)
+ }
+ unlock(&sig.lock)
+
+ return true
+}
+
+// Called to receive the next queued signal.
+// Must only be called from a single goroutine at a time.
+func signal_recv() string {
+ for {
+ note := sig.q.pop()
+ if note != nil {
+ return gostring(note)
+ }
+
+ lock(&sig.lock)
+ sig.sleeping = true
+ noteclear(&sig.note)
+ unlock(&sig.lock)
+ notetsleepg(&sig.note, -1)
+ }
+}
+
+// Must only be called from a single goroutine at a time.
+func signal_enable(s uint32) {
+ if !sig.inuse {
+ // The first call to signal_enable is for us
+ // to use for initialization. It does not pass
+ // signal information in m.
+ sig.inuse = true // enable reception of signals; cannot disable
+ noteclear(&sig.note)
+ return
+ }
+}
+
+// Must only be called from a single goroutine at a time.
+func signal_disable(s uint32) {
+}