Home | History | Annotate | Download | only in signal
      1 // Copyright 2009 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package signal
      6 
      7 import (
      8 	"os"
      9 	"runtime"
     10 	"syscall"
     11 	"testing"
     12 	"time"
     13 )
     14 
     15 func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) {
     16 	select {
     17 	case s := <-c:
     18 		if s != sig {
     19 			t.Fatalf("signal was %v, want %v", s, sig)
     20 		}
     21 	case <-time.After(1 * time.Second):
     22 		t.Fatalf("timeout waiting for %v", sig)
     23 	}
     24 }
     25 
     26 // Test that basic signal handling works.
     27 func TestSignal(t *testing.T) {
     28 	// Ask for hangup
     29 	c := make(chan os.Signal, 1)
     30 	Notify(c, syscall.Note("hangup"))
     31 	defer Stop(c)
     32 
     33 	// Send this process a hangup
     34 	t.Logf("hangup...")
     35 	postNote(syscall.Getpid(), "hangup")
     36 	waitSig(t, c, syscall.Note("hangup"))
     37 
     38 	// Ask for everything we can get.
     39 	c1 := make(chan os.Signal, 1)
     40 	Notify(c1)
     41 
     42 	// Send this process an alarm
     43 	t.Logf("alarm...")
     44 	postNote(syscall.Getpid(), "alarm")
     45 	waitSig(t, c1, syscall.Note("alarm"))
     46 
     47 	// Send two more hangups, to make sure that
     48 	// they get delivered on c1 and that not reading
     49 	// from c does not block everything.
     50 	t.Logf("hangup...")
     51 	postNote(syscall.Getpid(), "hangup")
     52 	waitSig(t, c1, syscall.Note("hangup"))
     53 	t.Logf("hangup...")
     54 	postNote(syscall.Getpid(), "hangup")
     55 	waitSig(t, c1, syscall.Note("hangup"))
     56 
     57 	// The first SIGHUP should be waiting for us on c.
     58 	waitSig(t, c, syscall.Note("hangup"))
     59 }
     60 
     61 func TestStress(t *testing.T) {
     62 	dur := 3 * time.Second
     63 	if testing.Short() {
     64 		dur = 100 * time.Millisecond
     65 	}
     66 	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
     67 	done := make(chan bool)
     68 	finished := make(chan bool)
     69 	go func() {
     70 		sig := make(chan os.Signal, 1)
     71 		Notify(sig, syscall.Note("alarm"))
     72 		defer Stop(sig)
     73 	Loop:
     74 		for {
     75 			select {
     76 			case <-sig:
     77 			case <-done:
     78 				break Loop
     79 			}
     80 		}
     81 		finished <- true
     82 	}()
     83 	go func() {
     84 	Loop:
     85 		for {
     86 			select {
     87 			case <-done:
     88 				break Loop
     89 			default:
     90 				postNote(syscall.Getpid(), "alarm")
     91 				runtime.Gosched()
     92 			}
     93 		}
     94 		finished <- true
     95 	}()
     96 	time.Sleep(dur)
     97 	close(done)
     98 	<-finished
     99 	<-finished
    100 	// When run with 'go test -cpu=1,2,4' alarm from this test can slip
    101 	// into subsequent TestSignal() causing failure.
    102 	// Sleep for a while to reduce the possibility of the failure.
    103 	time.Sleep(10 * time.Millisecond)
    104 }
    105 
    106 // Test that Stop cancels the channel's registrations.
    107 func TestStop(t *testing.T) {
    108 	if testing.Short() {
    109 		t.Skip("skipping in short mode")
    110 	}
    111 	sigs := []string{
    112 		"alarm",
    113 		"hangup",
    114 	}
    115 
    116 	for _, sig := range sigs {
    117 		// Send the signal.
    118 		// If it's alarm, we should not see it.
    119 		// If it's hangup, maybe we'll die. Let the flag tell us what to do.
    120 		if sig != "hangup" {
    121 			postNote(syscall.Getpid(), sig)
    122 		}
    123 		time.Sleep(100 * time.Millisecond)
    124 
    125 		// Ask for signal
    126 		c := make(chan os.Signal, 1)
    127 		Notify(c, syscall.Note(sig))
    128 		defer Stop(c)
    129 
    130 		// Send this process that signal
    131 		postNote(syscall.Getpid(), sig)
    132 		waitSig(t, c, syscall.Note(sig))
    133 
    134 		Stop(c)
    135 		select {
    136 		case s := <-c:
    137 			t.Fatalf("unexpected signal %v", s)
    138 		case <-time.After(100 * time.Millisecond):
    139 			// nothing to read - good
    140 		}
    141 
    142 		// Send the signal.
    143 		// If it's alarm, we should not see it.
    144 		// If it's hangup, maybe we'll die. Let the flag tell us what to do.
    145 		if sig != "hangup" {
    146 			postNote(syscall.Getpid(), sig)
    147 		}
    148 
    149 		select {
    150 		case s := <-c:
    151 			t.Fatalf("unexpected signal %v", s)
    152 		case <-time.After(100 * time.Millisecond):
    153 			// nothing to read - good
    154 		}
    155 	}
    156 }
    157 
    158 func itoa(val int) string {
    159 	if val < 0 {
    160 		return "-" + itoa(-val)
    161 	}
    162 	var buf [32]byte // big enough for int64
    163 	i := len(buf) - 1
    164 	for val >= 10 {
    165 		buf[i] = byte(val%10 + '0')
    166 		i--
    167 		val /= 10
    168 	}
    169 	buf[i] = byte(val + '0')
    170 	return string(buf[i:])
    171 }
    172 
    173 func postNote(pid int, note string) error {
    174 	f, err := os.OpenFile("/proc/"+itoa(pid)+"/note", os.O_WRONLY, 0)
    175 	if err != nil {
    176 		return err
    177 	}
    178 	defer f.Close()
    179 	_, err = f.Write([]byte(note))
    180 	return err
    181 }
    182