| // 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 |
| } |