1
0
mirror of https://github.com/golang/go synced 2024-11-23 07:50:05 -07:00

runtime: preserve signal stack when calling Go on C thread

When calling a Go function on a C thread, if the C thread already has an
alternate signal stack, use that signal stack instead of installing a
new one.

Update #9896.

Change-Id: I62aa3a6a4a1dc4040fca050757299c8e6736987c
Reviewed-on: https://go-review.googlesource.com/18108
Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
Ian Lance Taylor 2015-12-23 18:38:18 -08:00
parent 62c280ac1c
commit a7cad52e04
11 changed files with 383 additions and 14 deletions

View File

@ -0,0 +1,194 @@
// Copyright 2015 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.
// Test a C thread that calls sigaltstack and then calls Go code.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include <pthread.h>
#include "libgo4.h"
static void die(const char* msg) {
perror(msg);
exit(EXIT_FAILURE);
}
static int ok = 1;
static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
}
// Set up the SIGIO signal handler in a high priority constructor, so
// that it is installed before the Go code starts.
static void init(void) __attribute__ ((constructor (200)));
static void init() {
struct sigaction sa;
memset(&sa, 0, sizeof sa);
sa.sa_sigaction = ioHandler;
if (sigemptyset(&sa.sa_mask) < 0) {
die("sigemptyset");
}
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
if (sigaction(SIGIO, &sa, NULL) < 0) {
die("sigaction");
}
}
// Test raising SIGIO on a C thread with an alternate signal stack
// when there is a Go signal handler for SIGIO.
static void* thread1(void* arg) {
pthread_t* ptid = (pthread_t*)(arg);
stack_t ss;
int i;
stack_t nss;
// Set up an alternate signal stack for this thread.
memset(&ss, 0, sizeof ss);
ss.ss_sp = malloc(SIGSTKSZ);
if (ss.ss_sp == NULL) {
die("malloc");
}
ss.ss_flags = 0;
ss.ss_size = SIGSTKSZ;
if (sigaltstack(&ss, NULL) < 0) {
die("sigaltstack");
}
// Send ourselves a SIGIO. This will be caught by the Go
// signal handler which should forward to the C signal
// handler.
i = pthread_kill(*ptid, SIGIO);
if (i != 0) {
fprintf(stderr, "pthread_kill: %s\n", strerror(i));
exit(EXIT_FAILURE);
}
// Wait until the signal has been delivered.
i = 0;
while (SIGIOCount() == 0) {
if (sched_yield() < 0) {
perror("sched_yield");
}
i++;
if (i > 10000) {
fprintf(stderr, "looping too long waiting for signal\n");
exit(EXIT_FAILURE);
}
}
// We should still be on the same signal stack.
if (sigaltstack(NULL, &nss) < 0) {
die("sigaltstack check");
}
if ((nss.ss_flags & SS_DISABLE) != 0) {
fprintf(stderr, "sigaltstack disabled on return from Go\n");
ok = 0;
} else if (nss.ss_sp != ss.ss_sp) {
fprintf(stderr, "sigalstack changed on return from Go\n");
ok = 0;
}
return NULL;
}
// Test calling a Go function to raise SIGIO on a C thread with an
// alternate signal stack when there is a Go signal handler for SIGIO.
static void* thread2(void* arg) {
pthread_t* ptid = (pthread_t*)(arg);
stack_t ss;
int i;
int oldcount;
stack_t nss;
// Set up an alternate signal stack for this thread.
memset(&ss, 0, sizeof ss);
ss.ss_sp = malloc(SIGSTKSZ);
if (ss.ss_sp == NULL) {
die("malloc");
}
ss.ss_flags = 0;
ss.ss_size = SIGSTKSZ;
if (sigaltstack(&ss, NULL) < 0) {
die("sigaltstack");
}
oldcount = SIGIOCount();
// Call a Go function that will call a C function to send us a
// SIGIO.
GoRaiseSIGIO(ptid);
// Wait until the signal has been delivered.
i = 0;
while (SIGIOCount() == oldcount) {
if (sched_yield() < 0) {
perror("sched_yield");
}
i++;
if (i > 10000) {
fprintf(stderr, "looping too long waiting for signal\n");
exit(EXIT_FAILURE);
}
}
// We should still be on the same signal stack.
if (sigaltstack(NULL, &nss) < 0) {
die("sigaltstack check");
}
if ((nss.ss_flags & SS_DISABLE) != 0) {
fprintf(stderr, "sigaltstack disabled on return from Go\n");
ok = 0;
} else if (nss.ss_sp != ss.ss_sp) {
fprintf(stderr, "sigalstack changed on return from Go\n");
ok = 0;
}
return NULL;
}
int main(int argc, char **argv) {
pthread_t tid;
int i;
// Tell the Go library to start looking for SIGIO.
GoCatchSIGIO();
i = pthread_create(&tid, NULL, thread1, (void*)(&tid));
if (i != 0) {
fprintf(stderr, "pthread_create: %s\n", strerror(i));
exit(EXIT_FAILURE);
}
i = pthread_join(tid, NULL);
if (i != 0) {
fprintf(stderr, "pthread_join: %s\n", strerror(i));
exit(EXIT_FAILURE);
}
i = pthread_create(&tid, NULL, thread2, (void*)(&tid));
if (i != 0) {
fprintf(stderr, "pthread_create: %s\n", strerror(i));
exit(EXIT_FAILURE);
}
i = pthread_join(tid, NULL);
if (i != 0) {
fprintf(stderr, "pthread_join: %s\n", strerror(i));
exit(EXIT_FAILURE);
}
if (!ok) {
exit(EXIT_FAILURE);
}
printf("PASS\n");
return 0;
}

View File

@ -0,0 +1,52 @@
// Copyright 2015 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 main
/*
#include <signal.h>
#include <pthread.h>
// Raise SIGIO.
static void CRaiseSIGIO(pthread_t* p) {
pthread_kill(*p, SIGIO);
}
*/
import "C"
import (
"os"
"os/signal"
"sync/atomic"
"syscall"
)
var sigioCount int32
// Catch SIGIO.
//export GoCatchSIGIO
func GoCatchSIGIO() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGIO)
go func() {
for range c {
atomic.AddInt32(&sigioCount, 1)
}
}()
}
// Raise SIGIO.
//export GoRaiseSIGIO
func GoRaiseSIGIO(p *C.pthread_t) {
C.CRaiseSIGIO(p)
}
// Return the number of SIGIO signals seen.
//export SIGIOCount
func SIGIOCount() C.int {
return C.int(atomic.LoadInt32(&sigioCount))
}
func main() {
}

View File

@ -77,4 +77,12 @@ if ! $bin; then
fi
rm -rf libgo3.a libgo3.h testp pkg
GOPATH=$(pwd) go build -buildmode=c-archive -o libgo4.a libgo4
$(go env CC) $(go env GOGCCFLAGS) $ccargs -o testp main4.c libgo4.a
if ! $bin; then
echo "FAIL test4"
status=1
fi
rm -rf libgo4.a libgo4.h testp pkg
exit $status

View File

@ -152,7 +152,22 @@ func sigblock() {
func minit() {
// Initialize signal handling.
_g_ := getg()
signalstack(&_g_.m.gsignal.stack)
var st stackt
sigaltstack(nil, &st)
if st.ss_flags&_SS_DISABLE != 0 {
signalstack(&_g_.m.gsignal.stack)
_g_.m.newSigstack = true
} else {
// Use existing signal stack.
stsp := uintptr(unsafe.Pointer(st.ss_sp))
_g_.m.gsignal.stack.lo = stsp
_g_.m.gsignal.stack.hi = stsp + st.ss_size
_g_.m.gsignal.stackguard0 = stsp + _StackGuard
_g_.m.gsignal.stackguard1 = stsp + _StackGuard
_g_.m.gsignal.stackAlloc = st.ss_size
_g_.m.newSigstack = false
}
// restore signal mask from m.sigmask and unblock essential signals
nmask := _g_.m.sigmask
@ -167,7 +182,9 @@ func minit() {
// Called from dropm to undo the effect of an minit.
//go:nosplit
func unminit() {
signalstack(nil)
if getg().m.newSigstack {
signalstack(nil)
}
}
// Mach IPC, to get at semaphores

View File

@ -141,7 +141,21 @@ func minit() {
_g_.m.procid = uint64(*(*int32)(unsafe.Pointer(&_g_.m.procid)))
// Initialize signal handling
signalstack(&_g_.m.gsignal.stack)
var st sigaltstackt
sigaltstack(nil, &st)
if st.ss_flags&_SS_DISABLE != 0 {
signalstack(&_g_.m.gsignal.stack)
_g_.m.newSigstack = true
} else {
// Use existing signal stack.
stsp := uintptr(unsafe.Pointer(st.ss_sp))
_g_.m.gsignal.stack.lo = stsp
_g_.m.gsignal.stack.hi = stsp + st.ss_size
_g_.m.gsignal.stackguard0 = stsp + _StackGuard
_g_.m.gsignal.stackguard1 = stsp + _StackGuard
_g_.m.gsignal.stackAlloc = st.ss_size
_g_.m.newSigstack = false
}
// restore signal mask from m.sigmask and unblock essential signals
nmask := _g_.m.sigmask
@ -155,7 +169,9 @@ func minit() {
// Called from dropm to undo the effect of an minit.
func unminit() {
signalstack(nil)
if getg().m.newSigstack {
signalstack(nil)
}
}
func memlimit() uintptr {

View File

@ -147,7 +147,21 @@ func minit() {
}
// Initialize signal handling.
signalstack(&_g_.m.gsignal.stack)
var st stackt
sigaltstack(nil, &st)
if st.ss_flags&_SS_DISABLE != 0 {
signalstack(&_g_.m.gsignal.stack)
_g_.m.newSigstack = true
} else {
// Use existing signal stack.
stsp := uintptr(unsafe.Pointer(st.ss_sp))
_g_.m.gsignal.stack.lo = stsp
_g_.m.gsignal.stack.hi = stsp + st.ss_size
_g_.m.gsignal.stackguard0 = stsp + _StackGuard
_g_.m.gsignal.stackguard1 = stsp + _StackGuard
_g_.m.gsignal.stackAlloc = st.ss_size
_g_.m.newSigstack = false
}
// restore signal mask from m.sigmask and unblock essential signals
nmask := _g_.m.sigmask
@ -162,7 +176,9 @@ func minit() {
// Called from dropm to undo the effect of an minit.
//go:nosplit
func unminit() {
signalstack(nil)
if getg().m.newSigstack {
signalstack(nil)
}
}
func memlimit() uintptr {

View File

@ -221,7 +221,22 @@ func gettid() uint32
func minit() {
// Initialize signal handling.
_g_ := getg()
signalstack(&_g_.m.gsignal.stack)
var st sigaltstackt
sigaltstack(nil, &st)
if st.ss_flags&_SS_DISABLE != 0 {
signalstack(&_g_.m.gsignal.stack)
_g_.m.newSigstack = true
} else {
// Use existing signal stack.
stsp := uintptr(unsafe.Pointer(st.ss_sp))
_g_.m.gsignal.stack.lo = stsp
_g_.m.gsignal.stack.hi = stsp + st.ss_size
_g_.m.gsignal.stackguard0 = stsp + _StackGuard
_g_.m.gsignal.stackguard1 = stsp + _StackGuard
_g_.m.gsignal.stackAlloc = st.ss_size
_g_.m.newSigstack = false
}
// for debuggers, in case cgo created the thread
_g_.m.procid = uint64(gettid())
@ -239,7 +254,9 @@ func minit() {
// Called from dropm to undo the effect of an minit.
//go:nosplit
func unminit() {
signalstack(nil)
if getg().m.newSigstack {
signalstack(nil)
}
}
func memlimit() uintptr {

View File

@ -160,7 +160,21 @@ func minit() {
_g_.m.procid = uint64(lwp_self())
// Initialize signal handling
signalstack(&_g_.m.gsignal.stack)
var st sigaltstackt
sigaltstack(nil, &st)
if st.ss_flags&_SS_DISABLE != 0 {
signalstack(&_g_.m.gsignal.stack)
_g_.m.newSigstack = true
} else {
// Use existing signal stack.
stsp := uintptr(unsafe.Pointer(st.ss_sp))
_g_.m.gsignal.stack.lo = stsp
_g_.m.gsignal.stack.hi = stsp + st.ss_size
_g_.m.gsignal.stackguard0 = stsp + _StackGuard
_g_.m.gsignal.stackguard1 = stsp + _StackGuard
_g_.m.gsignal.stackAlloc = st.ss_size
_g_.m.newSigstack = false
}
// restore signal mask from m.sigmask and unblock essential signals
nmask := _g_.m.sigmask
@ -175,7 +189,9 @@ func minit() {
// Called from dropm to undo the effect of an minit.
//go:nosplit
func unminit() {
signalstack(nil)
if getg().m.newSigstack {
signalstack(nil)
}
}
func memlimit() uintptr {

View File

@ -174,7 +174,21 @@ func minit() {
_g_.m.procid = uint64(*(*int32)(unsafe.Pointer(&_g_.m.procid)))
// Initialize signal handling
signalstack(&_g_.m.gsignal.stack)
var st stackt
sigaltstack(nil, &st)
if st.ss_flags&_SS_DISABLE != 0 {
signalstack(&_g_.m.gsignal.stack)
_g_.m.newSigstack = true
} else {
// Use existing signal stack.
stsp := uintptr(unsafe.Pointer(st.ss_sp))
_g_.m.gsignal.stack.lo = stsp
_g_.m.gsignal.stack.hi = stsp + st.ss_size
_g_.m.gsignal.stackguard0 = stsp + _StackGuard
_g_.m.gsignal.stackguard1 = stsp + _StackGuard
_g_.m.gsignal.stackAlloc = st.ss_size
_g_.m.newSigstack = false
}
// restore signal mask from m.sigmask and unblock essential signals
nmask := _g_.m.sigmask
@ -189,7 +203,9 @@ func minit() {
// Called from dropm to undo the effect of an minit.
//go:nosplit
func unminit() {
signalstack(nil)
if getg().m.newSigstack {
signalstack(nil)
}
}
func memlimit() uintptr {

View File

@ -213,7 +213,21 @@ func minit() {
_g_ := getg()
asmcgocall(unsafe.Pointer(funcPC(miniterrno)), unsafe.Pointer(&libc____errno))
// Initialize signal handling
signalstack(&_g_.m.gsignal.stack)
var st sigaltstackt
sigaltstack(nil, &st)
if st.ss_flags&_SS_DISABLE != 0 {
signalstack(&_g_.m.gsignal.stack)
_g_.m.newSigstack = true
} else {
// Use existing signal stack.
stsp := uintptr(unsafe.Pointer(st.ss_sp))
_g_.m.gsignal.stack.lo = stsp
_g_.m.gsignal.stack.hi = stsp + uintptr(st.ss_size)
_g_.m.gsignal.stackguard0 = stsp + _StackGuard
_g_.m.gsignal.stackguard1 = stsp + _StackGuard
_g_.m.gsignal.stackAlloc = uintptr(st.ss_size)
_g_.m.newSigstack = false
}
// restore signal mask from m.sigmask and unblock essential signals
nmask := _g_.m.sigmask
@ -227,7 +241,9 @@ func minit() {
// Called from dropm to undo the effect of an minit.
func unminit() {
signalstack(nil)
if getg().m.newSigstack {
signalstack(nil)
}
}
func memlimit() uintptr {

View File

@ -303,6 +303,7 @@ type m struct {
spinning bool // m is out of work and is actively looking for work
blocked bool // m is blocked on a note
inwb bool // m is executing a write barrier
newSigstack bool // minit on C thread called sigaltstack
printlock int8
fastrand uint32
ncgocall uint64 // number of cgo calls in total