1
0
mirror of https://github.com/golang/go synced 2024-11-23 19:20:03 -07:00

os/signal, runtime: implement notes on Plan 9

This change is an implementation of the signal
runtime and os/signal package on Plan 9.

Contrary to Unix, on Plan 9 a signal is called
a note and is represented by a string.

For this reason, the sigsend and signal_recv
functions had to be reimplemented specifically
for Plan 9.

In order to reuse most of the code and internal
interface of the os/signal package, the note
strings are mapped to integers.

Thanks to Russ Cox for the early review.

Change-Id: I95836645efe21942bb1939f43f87fb3c0eaaef1a
Reviewed-on: https://go-review.googlesource.com/2164
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Rob Pike <r@golang.org>
This commit is contained in:
David du Colombier 2015-01-22 23:38:29 +01:00
parent 1b523384dc
commit d5d4ab7819
7 changed files with 356 additions and 22 deletions

View File

@ -5,8 +5,6 @@
// Package signal implements access to incoming signals.
package signal
// BUG(rsc): This package is not yet implemented on Plan 9.
import (
"os"
"sync"

View File

@ -0,0 +1,55 @@
// Copyright 2012 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"
"syscall"
)
var sigtab = make(map[os.Signal]int)
// In sig.s; jumps to runtime.
func signal_disable(uint32)
func signal_enable(uint32)
func signal_recv() string
func init() {
signal_enable(0) // first call - initialize
go loop()
}
func loop() {
for {
process(syscall.Note(signal_recv()))
}
}
const numSig = 256
func signum(sig os.Signal) int {
switch sig := sig.(type) {
case syscall.Note:
n, ok := sigtab[sig]
if !ok {
n = len(sigtab) + 1
if n > numSig {
return -1
}
sigtab[sig] = n
}
return n
default:
return -1
}
}
func enableSignal(sig int) {
signal_enable(uint32(sig))
}
func disableSignal(sig int) {
signal_disable(uint32(sig))
}

View File

@ -0,0 +1,181 @@
// 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
}

View File

@ -1,17 +0,0 @@
// Copyright 2012 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.
// +build plan9
package signal
import "os"
const numSig = 0
func signum(sig os.Signal) int { return -1 }
func disableSignal(int) {}
func enableSignal(int) {}

View File

@ -72,9 +72,9 @@ func sighandler(_ureg *ureg, note *byte, gp *g) int {
return _NCONT
}
if flags&_SigNotify != 0 {
// TODO(ality): See if os/signal wants it.
//if(sigsend(...))
// return _NCONT;
if sendNote(note) {
return _NCONT
}
}
if flags&_SigKill != 0 {
goto Exit

View File

@ -24,6 +24,8 @@
// unnecessary rechecks of sig.mask, but it cannot lead to missed signals
// nor deadlocks.
// +build !plan9
package runtime
import "unsafe"

View File

@ -0,0 +1,115 @@
// 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) {
}