2012-05-29 23:10:54 -06:00
|
|
|
// 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 cgo
|
|
|
|
|
|
|
|
package runtime_test
|
|
|
|
|
|
|
|
import (
|
2015-01-02 22:12:34 -07:00
|
|
|
"os/exec"
|
2013-08-07 14:04:28 -06:00
|
|
|
"runtime"
|
2014-10-28 19:53:09 -06:00
|
|
|
"strings"
|
2012-05-29 23:10:54 -06:00
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestCgoCrashHandler(t *testing.T) {
|
2013-02-20 01:15:02 -07:00
|
|
|
testCrashHandler(t, true)
|
2012-05-29 23:10:54 -06:00
|
|
|
}
|
2013-02-28 01:07:26 -07:00
|
|
|
|
|
|
|
func TestCgoSignalDeadlock(t *testing.T) {
|
2013-08-07 14:04:28 -06:00
|
|
|
if testing.Short() && runtime.GOOS == "windows" {
|
|
|
|
t.Skip("Skipping in short mode") // takes up to 64 seconds
|
|
|
|
}
|
2013-02-28 01:07:26 -07:00
|
|
|
got := executeTest(t, cgoSignalDeadlockSource, nil)
|
|
|
|
want := "OK\n"
|
|
|
|
if got != want {
|
|
|
|
t.Fatalf("expected %q, but got %q", want, got)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-07 14:31:52 -06:00
|
|
|
func TestCgoTraceback(t *testing.T) {
|
|
|
|
got := executeTest(t, cgoTracebackSource, nil)
|
|
|
|
want := "OK\n"
|
|
|
|
if got != want {
|
|
|
|
t.Fatalf("expected %q, but got %q", want, got)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-29 14:16:13 -06:00
|
|
|
func TestCgoCallbackGC(t *testing.T) {
|
|
|
|
if runtime.GOOS == "plan9" || runtime.GOOS == "windows" {
|
|
|
|
t.Skipf("no pthreads on %s", runtime.GOOS)
|
|
|
|
}
|
|
|
|
got := executeTest(t, cgoCallbackGCSource, nil)
|
|
|
|
want := "OK\n"
|
|
|
|
if got != want {
|
|
|
|
t.Fatalf("expected %q, but got %q", want, got)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-28 19:53:09 -06:00
|
|
|
func TestCgoExternalThreadPanic(t *testing.T) {
|
2014-10-29 17:24:37 -06:00
|
|
|
if runtime.GOOS == "plan9" {
|
2014-10-28 22:02:29 -06:00
|
|
|
t.Skipf("no pthreads on %s", runtime.GOOS)
|
|
|
|
}
|
2014-10-29 17:24:37 -06:00
|
|
|
csrc := cgoExternalThreadPanicC
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
csrc = cgoExternalThreadPanicC_windows
|
|
|
|
}
|
|
|
|
got := executeTest(t, cgoExternalThreadPanicSource, nil, "main.c", csrc)
|
2014-10-28 19:53:09 -06:00
|
|
|
want := "panic: BOOM"
|
|
|
|
if !strings.Contains(got, want) {
|
|
|
|
t.Fatalf("want failure containing %q. output:\n%s\n", want, got)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-27 17:15:38 -07:00
|
|
|
func TestCgoExternalThreadSIGPROF(t *testing.T) {
|
|
|
|
// issue 9456.
|
2014-12-31 23:10:39 -07:00
|
|
|
switch runtime.GOOS {
|
|
|
|
case "plan9", "windows":
|
2014-12-27 17:15:38 -07:00
|
|
|
t.Skipf("no pthreads on %s", runtime.GOOS)
|
2014-12-31 23:10:39 -07:00
|
|
|
case "darwin":
|
2015-04-11 17:00:53 -06:00
|
|
|
if runtime.GOARCH != "arm" && runtime.GOARCH != "arm64" {
|
2015-02-26 16:05:47 -07:00
|
|
|
// static constructor needs external linking, but we don't support
|
|
|
|
// external linking on OS X 10.6.
|
|
|
|
out, err := exec.Command("uname", "-r").Output()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("uname -r failed: %v", err)
|
|
|
|
}
|
|
|
|
// OS X 10.6 == Darwin 10.x
|
|
|
|
if strings.HasPrefix(string(out), "10.") {
|
|
|
|
t.Skipf("no external linking on OS X 10.6")
|
|
|
|
}
|
2014-12-31 23:10:39 -07:00
|
|
|
}
|
2014-12-27 17:15:38 -07:00
|
|
|
}
|
2014-12-16 16:34:55 -07:00
|
|
|
if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" {
|
|
|
|
// TODO(austin) External linking not implemented on
|
|
|
|
// ppc64 (issue #8912)
|
|
|
|
t.Skipf("no external linking on ppc64")
|
|
|
|
}
|
2014-12-27 17:15:38 -07:00
|
|
|
got := executeTest(t, cgoExternalThreadSIGPROFSource, nil)
|
|
|
|
want := "OK\n"
|
|
|
|
if got != want {
|
|
|
|
t.Fatalf("expected %q, but got %q", want, got)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-21 23:34:48 -06:00
|
|
|
func TestCgoExternalThreadSignal(t *testing.T) {
|
|
|
|
// issue 10139
|
|
|
|
switch runtime.GOOS {
|
|
|
|
case "plan9", "windows":
|
|
|
|
t.Skipf("no pthreads on %s", runtime.GOOS)
|
|
|
|
}
|
|
|
|
got := executeTest(t, cgoExternalThreadSignalSource, nil)
|
|
|
|
want := "OK\n"
|
|
|
|
if got != want {
|
|
|
|
t.Fatalf("expected %q, but got %q", want, got)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-15 22:46:22 -06:00
|
|
|
func TestCgoDLLImports(t *testing.T) {
|
|
|
|
// test issue 9356
|
|
|
|
if runtime.GOOS != "windows" {
|
|
|
|
t.Skip("skipping windows specific test")
|
|
|
|
}
|
|
|
|
got := executeTest(t, cgoDLLImportsMainSource, nil, "a/a.go", cgoDLLImportsPkgSource)
|
|
|
|
want := "OK\n"
|
|
|
|
if got != want {
|
|
|
|
t.Fatalf("expected %q, but got %v", want, got)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-28 01:07:26 -07:00
|
|
|
const cgoSignalDeadlockSource = `
|
|
|
|
package main
|
|
|
|
|
|
|
|
import "C"
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"runtime"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
runtime.GOMAXPROCS(100)
|
|
|
|
ping := make(chan bool)
|
|
|
|
go func() {
|
|
|
|
for i := 0; ; i++ {
|
|
|
|
runtime.Gosched()
|
|
|
|
select {
|
|
|
|
case done := <-ping:
|
|
|
|
if done {
|
|
|
|
ping <- true
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ping <- true
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
func() {
|
|
|
|
defer func() {
|
|
|
|
recover()
|
|
|
|
}()
|
|
|
|
var s *string
|
|
|
|
*s = ""
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
time.Sleep(time.Millisecond)
|
|
|
|
for i := 0; i < 64; i++ {
|
|
|
|
go func() {
|
|
|
|
runtime.LockOSThread()
|
|
|
|
select {}
|
|
|
|
}()
|
|
|
|
go func() {
|
|
|
|
runtime.LockOSThread()
|
|
|
|
select {}
|
|
|
|
}()
|
|
|
|
time.Sleep(time.Millisecond)
|
|
|
|
ping <- false
|
|
|
|
select {
|
|
|
|
case <-ping:
|
|
|
|
case <-time.After(time.Second):
|
|
|
|
fmt.Printf("HANG\n")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ping <- true
|
|
|
|
select {
|
|
|
|
case <-ping:
|
|
|
|
case <-time.After(time.Second):
|
|
|
|
fmt.Printf("HANG\n")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
fmt.Printf("OK\n")
|
|
|
|
}
|
|
|
|
`
|
2013-08-07 14:31:52 -06:00
|
|
|
|
|
|
|
const cgoTracebackSource = `
|
|
|
|
package main
|
|
|
|
|
|
|
|
/* void foo(void) {} */
|
|
|
|
import "C"
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"runtime"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
C.foo()
|
|
|
|
buf := make([]byte, 1)
|
|
|
|
runtime.Stack(buf, true)
|
|
|
|
fmt.Printf("OK\n")
|
|
|
|
}
|
|
|
|
`
|
2014-10-28 19:53:09 -06:00
|
|
|
|
2015-07-29 14:16:13 -06:00
|
|
|
const cgoCallbackGCSource = `
|
|
|
|
package main
|
|
|
|
|
|
|
|
import "runtime"
|
|
|
|
|
|
|
|
/*
|
|
|
|
#include <pthread.h>
|
|
|
|
|
|
|
|
void go_callback();
|
|
|
|
|
|
|
|
static void *thr(void *arg) {
|
|
|
|
go_callback();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void foo() {
|
|
|
|
pthread_t th;
|
|
|
|
pthread_create(&th, 0, thr, 0);
|
|
|
|
pthread_join(th, 0);
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
import "fmt"
|
|
|
|
|
|
|
|
//export go_callback
|
|
|
|
func go_callback() {
|
|
|
|
runtime.GC()
|
|
|
|
grow()
|
|
|
|
runtime.GC()
|
|
|
|
}
|
|
|
|
|
|
|
|
var cnt int
|
|
|
|
|
|
|
|
func grow() {
|
|
|
|
x := 10000
|
|
|
|
sum := 0
|
|
|
|
if grow1(&x, &sum) == 0 {
|
|
|
|
panic("bad")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func grow1(x, sum *int) int {
|
|
|
|
if *x == 0 {
|
|
|
|
return *sum + 1
|
|
|
|
}
|
|
|
|
*x--
|
|
|
|
sum1 := *sum + *x
|
|
|
|
return grow1(x, &sum1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
const P = 100
|
|
|
|
done := make(chan bool)
|
|
|
|
// allocate a bunch of stack frames and spray them with pointers
|
|
|
|
for i := 0; i < P; i++ {
|
|
|
|
go func() {
|
|
|
|
grow()
|
|
|
|
done <- true
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
for i := 0; i < P; i++ {
|
|
|
|
<-done
|
|
|
|
}
|
|
|
|
// now give these stack frames to cgo callbacks
|
|
|
|
for i := 0; i < P; i++ {
|
|
|
|
go func() {
|
|
|
|
C.foo()
|
|
|
|
done <- true
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
for i := 0; i < P; i++ {
|
|
|
|
<-done
|
|
|
|
}
|
|
|
|
fmt.Printf("OK\n")
|
|
|
|
}
|
|
|
|
`
|
|
|
|
|
2014-10-28 19:53:09 -06:00
|
|
|
const cgoExternalThreadPanicSource = `
|
|
|
|
package main
|
|
|
|
|
|
|
|
// void start(void);
|
|
|
|
import "C"
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
C.start()
|
|
|
|
select {}
|
|
|
|
}
|
|
|
|
|
|
|
|
//export gopanic
|
|
|
|
func gopanic() {
|
|
|
|
panic("BOOM")
|
|
|
|
}
|
|
|
|
`
|
|
|
|
|
|
|
|
const cgoExternalThreadPanicC = `
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
|
|
|
|
void gopanic(void);
|
|
|
|
|
|
|
|
static void*
|
|
|
|
die(void* x)
|
|
|
|
{
|
|
|
|
gopanic();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
start(void)
|
|
|
|
{
|
|
|
|
pthread_t t;
|
|
|
|
if(pthread_create(&t, 0, die, 0) != 0)
|
|
|
|
printf("pthread_create failed\n");
|
|
|
|
}
|
|
|
|
`
|
2014-10-29 17:24:37 -06:00
|
|
|
|
|
|
|
const cgoExternalThreadPanicC_windows = `
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
void gopanic(void);
|
|
|
|
|
|
|
|
static void*
|
|
|
|
die(void* x)
|
|
|
|
{
|
|
|
|
gopanic();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
start(void)
|
|
|
|
{
|
|
|
|
if(_beginthreadex(0, 0, die, 0, 0, 0) != 0)
|
|
|
|
printf("_beginthreadex failed\n");
|
|
|
|
}
|
|
|
|
`
|
2014-12-27 17:15:38 -07:00
|
|
|
|
|
|
|
const cgoExternalThreadSIGPROFSource = `
|
|
|
|
package main
|
|
|
|
|
|
|
|
/*
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
|
|
|
|
volatile int32_t spinlock;
|
|
|
|
|
|
|
|
static void *thread1(void *p) {
|
|
|
|
(void)p;
|
|
|
|
while (spinlock == 0)
|
|
|
|
;
|
|
|
|
pthread_kill(pthread_self(), SIGPROF);
|
|
|
|
spinlock = 0;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
__attribute__((constructor)) void issue9456() {
|
|
|
|
pthread_t tid;
|
|
|
|
pthread_create(&tid, 0, thread1, NULL);
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
|
|
|
|
import (
|
|
|
|
"runtime"
|
|
|
|
"sync/atomic"
|
|
|
|
"unsafe"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
// This test intends to test that sending SIGPROF to foreign threads
|
|
|
|
// before we make any cgo call will not abort the whole process, so
|
2015-07-10 17:17:11 -06:00
|
|
|
// we cannot make any cgo call here. See https://golang.org/issue/9456.
|
2014-12-27 17:15:38 -07:00
|
|
|
atomic.StoreInt32((*int32)(unsafe.Pointer(&C.spinlock)), 1)
|
|
|
|
for atomic.LoadInt32((*int32)(unsafe.Pointer(&C.spinlock))) == 1 {
|
|
|
|
runtime.Gosched()
|
|
|
|
}
|
|
|
|
println("OK")
|
|
|
|
}
|
|
|
|
`
|
2015-03-15 22:46:22 -06:00
|
|
|
|
2015-07-21 23:34:48 -06:00
|
|
|
const cgoExternalThreadSignalSource = `
|
|
|
|
package main
|
|
|
|
|
|
|
|
/*
|
|
|
|
#include <pthread.h>
|
|
|
|
|
|
|
|
void **nullptr;
|
|
|
|
|
|
|
|
void *crash(void *p) {
|
|
|
|
*nullptr = p;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int start_crashing_thread(void) {
|
|
|
|
pthread_t tid;
|
|
|
|
return pthread_create(&tid, 0, crash, 0);
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
if len(os.Args) > 1 && os.Args[1] == "crash" {
|
|
|
|
i := C.start_crashing_thread()
|
|
|
|
if i != 0 {
|
|
|
|
fmt.Println("pthread_create failed:", i)
|
|
|
|
// Exit with 0 because parent expects us to crash.
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// We should crash immediately, but give it plenty of
|
|
|
|
// time before failing (by exiting 0) in case we are
|
|
|
|
// running on a slow system.
|
|
|
|
time.Sleep(5 * time.Second)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
out, err := exec.Command(os.Args[0], "crash").CombinedOutput()
|
|
|
|
if err == nil {
|
|
|
|
fmt.Println("C signal did not crash as expected\n")
|
|
|
|
fmt.Printf("%s\n", out)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println("OK")
|
|
|
|
}
|
|
|
|
`
|
|
|
|
|
2015-03-15 22:46:22 -06:00
|
|
|
const cgoDLLImportsMainSource = `
|
|
|
|
package main
|
|
|
|
|
|
|
|
/*
|
|
|
|
#include <windows.h>
|
|
|
|
|
|
|
|
DWORD getthread() {
|
|
|
|
return GetCurrentThreadId();
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
|
|
|
|
import "./a"
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
C.getthread()
|
|
|
|
a.GetThread()
|
|
|
|
println("OK")
|
|
|
|
}
|
|
|
|
`
|
|
|
|
|
|
|
|
const cgoDLLImportsPkgSource = `
|
|
|
|
package a
|
|
|
|
|
|
|
|
/*
|
|
|
|
#cgo CFLAGS: -mnop-fun-dllimport
|
|
|
|
|
|
|
|
#include <windows.h>
|
|
|
|
|
|
|
|
DWORD agetthread() {
|
|
|
|
return GetCurrentThreadId();
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
|
|
|
|
func GetThread() uint32 {
|
|
|
|
return uint32(C.agetthread())
|
|
|
|
}
|
|
|
|
`
|