Skip to content

Commit

Permalink
runtime: correctly handle signals received on foreign threads
Browse files Browse the repository at this point in the history
Fixes #3250.

R=rsc
CC=golang-dev
https://golang.org/cl/10757044
  • Loading branch information
minux committed Jul 11, 2013
1 parent 2a983aa commit 2f1ead7
Show file tree
Hide file tree
Showing 25 changed files with 172 additions and 159 deletions.
1 change: 1 addition & 0 deletions misc/cgo/test/cgo_test.go
Expand Up @@ -42,5 +42,6 @@ func TestCflags(t *testing.T) { testCflags(t) }
func Test5337(t *testing.T) { test5337(t) }
func Test5548(t *testing.T) { test5548(t) }
func Test5603(t *testing.T) { test5603(t) }
func Test3250(t *testing.T) { test3250(t) }

func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }
94 changes: 94 additions & 0 deletions misc/cgo/test/issue3250.go
@@ -0,0 +1,94 @@
// Copyright 2013 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 !windows

package cgotest

/*
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
static void *thread(void *p) {
(void)p;
const int M = 100;
int i;
for (i = 0; i < M; i++) {
pthread_kill(pthread_self(), SIGCHLD);
usleep(rand() % 20 + 5);
}
return NULL;
}
void testSendSIG() {
const int N = 20;
int i;
pthread_t tid[N];
for (i = 0; i < N; i++) {
usleep(rand() % 200 + 100);
pthread_create(&tid[i], 0, thread, NULL);
}
for (i = 0; i < N; i++)
pthread_join(tid[i], 0);
}
*/
import "C"

import (
"os"
"os/signal"
"syscall"
"testing"
"time"
)

func test3250(t *testing.T) {
const (
thres = 5
sig = syscall.SIGCHLD
)
type result struct {
n int
sig os.Signal
}
var (
sigCh = make(chan os.Signal, 10)
waitStart = make(chan struct{})
waitDone = make(chan result)
)

signal.Notify(sigCh, sig)

go func() {
n := 0
alarm := time.After(time.Second * 3)
for {
select {
case <-waitStart:
waitStart = nil
case v := <-sigCh:
n++
if v != sig || n > thres {
waitDone <- result{n, v}
return
}
case <-alarm:
waitDone <- result{n, sig}
return
}
}
}()

waitStart <- struct{}{}
C.testSendSIG()
r := <-waitDone
if r.sig != sig {
t.Fatalf("received signal %v, but want %v", r.sig, sig)
}
t.Logf("got %d signals\n", r.n)
if r.n <= thres {
t.Fatalf("expected more than %d", thres)
}
}
11 changes: 11 additions & 0 deletions misc/cgo/test/issue3250w.go
@@ -0,0 +1,11 @@
// Copyright 2013 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 windows

package cgotest

import "testing"

func test3250(t *testing.T) {}
24 changes: 0 additions & 24 deletions src/pkg/runtime/os_darwin.c
Expand Up @@ -523,30 +523,6 @@ runtime·setprof(bool on)
runtime·sigprocmask(SIG_BLOCK, &sigset_prof, nil);
}

#pragma dataflag 16 // no pointers
static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";

// This runs on a foreign stack, without an m or a g. No stack split.
#pragma textflag 7
void
runtime·badsignal(int32 sig)
{
int32 len;

if (sig == SIGPROF) {
return; // Ignore SIGPROFs intended for a non-Go thread.
}
runtime·write(2, badsignal, sizeof badsignal - 1);
if (0 <= sig && sig < NSIG) {
// Can't call findnull() because it will split stack.
for(len = 0; runtime·sigtab[sig].name[len]; len++)
;
runtime·write(2, runtime·sigtab[sig].name, len);
}
runtime·write(2, "\n", 1);
runtime·exit(1);
}

void
runtime·setsig(int32 i, GoSighandler *fn, bool restart)
{
Expand Down
24 changes: 0 additions & 24 deletions src/pkg/runtime/os_freebsd.c
Expand Up @@ -235,30 +235,6 @@ runtime·setprof(bool on)
USED(on);
}

#pragma dataflag 16 // no pointers
static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";

// This runs on a foreign stack, without an m or a g. No stack split.
#pragma textflag 7
void
runtime·badsignal(int32 sig)
{
int32 len;

if (sig == SIGPROF) {
return; // Ignore SIGPROFs intended for a non-Go thread.
}
runtime·write(2, badsignal, sizeof badsignal - 1);
if (0 <= sig && sig < NSIG) {
// Can't call findnull() because it will split stack.
for(len = 0; runtime·sigtab[sig].name[len]; len++)
;
runtime·write(2, runtime·sigtab[sig].name, len);
}
runtime·write(2, "\n", 1);
runtime·exit(1);
}

extern void runtime·sigtramp(void);

typedef struct sigaction {
Expand Down
24 changes: 0 additions & 24 deletions src/pkg/runtime/os_linux.c
Expand Up @@ -284,30 +284,6 @@ runtime·setprof(bool on)
USED(on);
}

#pragma dataflag 16 // no pointers
static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";

// This runs on a foreign stack, without an m or a g. No stack split.
#pragma textflag 7
void
runtime·badsignal(int32 sig)
{
int32 len;

if (sig == SIGPROF) {
return; // Ignore SIGPROFs intended for a non-Go thread.
}
runtime·write(2, badsignal, sizeof badsignal - 1);
if (0 <= sig && sig < NSIG) {
// Can't call findnull() because it will split stack.
for(len = 0; runtime·sigtab[sig].name[len]; len++)
;
runtime·write(2, runtime·sigtab[sig].name, len);
}
runtime·write(2, "\n", 1);
runtime·exit(1);
}

#ifdef GOARCH_386
#define sa_handler k_sa_handler
#endif
Expand Down
24 changes: 0 additions & 24 deletions src/pkg/runtime/os_netbsd.c
Expand Up @@ -275,30 +275,6 @@ runtime·setprof(bool on)
USED(on);
}

#pragma dataflag 16 // no pointers
static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";

// This runs on a foreign stack, without an m or a g. No stack split.
#pragma textflag 7
void
runtime·badsignal(int32 sig)
{
int32 len;

if (sig == SIGPROF) {
return; // Ignore SIGPROFs intended for a non-Go thread.
}
runtime·write(2, badsignal, sizeof badsignal - 1);
if (0 <= sig && sig < NSIG) {
// Can't call findnull() because it will split stack.
for(len = 0; runtime·sigtab[sig].name[len]; len++)
;
runtime·write(2, runtime·sigtab[sig].name, len);
}
runtime·write(2, "\n", 1);
runtime·exit(1);
}

extern void runtime·sigtramp(void);

typedef struct sigaction {
Expand Down
24 changes: 0 additions & 24 deletions src/pkg/runtime/os_openbsd.c
Expand Up @@ -257,30 +257,6 @@ runtime·setprof(bool on)
USED(on);
}

#pragma dataflag 16 // no pointers
static int8 badsignal[] = "runtime: signal received on thread not created by Go: ";

// This runs on a foreign stack, without an m or a g. No stack split.
#pragma textflag 7
void
runtime·badsignal(int32 sig)
{
int32 len;

if (sig == SIGPROF) {
return; // Ignore SIGPROFs intended for a non-Go thread.
}
runtime·write(2, badsignal, sizeof badsignal - 1);
if (0 <= sig && sig < NSIG) {
// Can't call findnull() because it will split stack.
for(len = 0; runtime·sigtab[sig].name[len]; len++)
;
runtime·write(2, runtime·sigtab[sig].name, len);
}
runtime·write(2, "\n", 1);
runtime·exit(1);
}

extern void runtime·sigtramp(void);

typedef struct sigaction {
Expand Down
2 changes: 1 addition & 1 deletion src/pkg/runtime/os_plan9.c
Expand Up @@ -336,7 +336,7 @@ static int8 badsignal[] = "runtime: signal received on thread not created by Go.
// This runs on a foreign stack, without an m or a g. No stack split.
#pragma textflag 7
void
runtime·badsignal(void)
runtime·badsignal2(void)
{
runtime·pwrite(2, badsignal, sizeof badsignal - 1, -1LL);
runtime·exits(badsignal);
Expand Down
9 changes: 9 additions & 0 deletions src/pkg/runtime/sigqueue.goc
Expand Up @@ -28,6 +28,7 @@ package runtime
#include "runtime.h"
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
#include "cgocall.h"

static struct {
Note;
Expand Down Expand Up @@ -155,3 +156,11 @@ func signal_disable(s uint32) {
sig.wanted[s/32] &= ~(1U<<(s&31));
runtime·sigdisable(s);
}

// This runs on a foreign stack, without an m or a g. No stack split.
#pragma textflag 7
void
runtime·badsignal(uintptr sig)
{
runtime·cgocallback((void (*)(void))runtime·sigsend, &sig, sizeof(sig));
}
8 changes: 5 additions & 3 deletions src/pkg/runtime/sys_darwin_386.s
Expand Up @@ -238,11 +238,12 @@ TEXT runtime·sigtramp(SB),7,$40
// check that m exists
MOVL m(CX), BP
CMPL BP, $0
JNE 5(PC)
JNE 6(PC)
MOVL sig+8(FP), BX
MOVL BX, 0(SP)
CALL runtime·badsignal(SB)
RET
MOVL $runtime·badsignal(SB), AX
CALL AX
JMP sigtramp_ret

// save g
MOVL g(CX), DI
Expand All @@ -269,6 +270,7 @@ TEXT runtime·sigtramp(SB),7,$40
MOVL 20(SP), DI
MOVL DI, g(CX)

sigtramp_ret:
// call sigreturn
MOVL context+16(FP), CX
MOVL style+4(FP), BX
Expand Down
13 changes: 8 additions & 5 deletions src/pkg/runtime/sys_darwin_amd64.s
Expand Up @@ -192,13 +192,17 @@ TEXT runtime·sigaction(SB),7,$0
TEXT runtime·sigtramp(SB),7,$64
get_tls(BX)

MOVQ R8, 32(SP) // save ucontext
MOVQ SI, 40(SP) // save infostyle

// check that m exists
MOVQ m(BX), BP
CMPQ BP, $0
JNE 4(PC)
JNE 5(PC)
MOVL DX, 0(SP)
CALL runtime·badsignal(SB)
RET
MOVQ $runtime·badsignal(SB), AX
CALL AX
JMP sigtramp_ret

// save g
MOVQ g(BX), R10
Expand All @@ -213,15 +217,14 @@ TEXT runtime·sigtramp(SB),7,$64
MOVQ R8, 16(SP)
MOVQ R10, 24(SP)

MOVQ R8, 32(SP) // save ucontext
MOVQ SI, 40(SP) // save infostyle
CALL DI

// restore g
get_tls(BX)
MOVQ 48(SP), R10
MOVQ R10, g(BX)

sigtramp_ret:
// call sigreturn
MOVL $(0x2000000+184), AX // sigreturn(ucontext, infostyle)
MOVQ 32(SP), DI // saved ucontext
Expand Down
10 changes: 6 additions & 4 deletions src/pkg/runtime/sys_freebsd_386.s
Expand Up @@ -183,11 +183,12 @@ TEXT runtime·sigtramp(SB),7,$44
// check that m exists
MOVL m(CX), BX
CMPL BX, $0
JNE 5(PC)
JNE 6(PC)
MOVL signo+0(FP), BX
MOVL BX, 0(SP)
CALL runtime·badsignal(SB)
RET
MOVL $runtime·badsignal(SB), AX
CALL AX
JMP sigtramp_ret

// save g
MOVL g(CX), DI
Expand All @@ -212,7 +213,8 @@ TEXT runtime·sigtramp(SB),7,$44
get_tls(CX)
MOVL 20(SP), BX
MOVL BX, g(CX)


sigtramp_ret:
// call sigreturn
MOVL context+8(FP), AX
MOVL $0, 0(SP) // syscall gap
Expand Down

0 comments on commit 2f1ead7

Please sign in to comment.