github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/misc/cgo/test/callback.go (about)

     1  // Copyright 2011 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 cgotest
     6  
     7  /*
     8  void callback(void *f);
     9  void callGoFoo(void);
    10  */
    11  import "C"
    12  
    13  import (
    14  	"./backdoor"
    15  	"path"
    16  	"runtime"
    17  	"strings"
    18  	"testing"
    19  	"unsafe"
    20  )
    21  
    22  // nestedCall calls into C, back into Go, and finally to f.
    23  func nestedCall(f func()) {
    24  	// NOTE: Depends on representation of f.
    25  	// callback(x) calls goCallback(x)
    26  	C.callback(*(*unsafe.Pointer)(unsafe.Pointer(&f)))
    27  }
    28  
    29  //export goCallback
    30  func goCallback(p unsafe.Pointer) {
    31  	(*(*func())(unsafe.Pointer(&p)))()
    32  }
    33  
    34  func testCallback(t *testing.T) {
    35  	var x = false
    36  	nestedCall(func() { x = true })
    37  	if !x {
    38  		t.Fatal("nestedCall did not call func")
    39  	}
    40  }
    41  
    42  func testCallbackGC(t *testing.T) {
    43  	nestedCall(runtime.GC)
    44  }
    45  
    46  var lockedOSThread = backdoor.LockedOSThread
    47  
    48  func testCallbackPanic(t *testing.T) {
    49  	// Make sure panic during callback unwinds properly.
    50  	if lockedOSThread() {
    51  		t.Fatal("locked OS thread on entry to TestCallbackPanic")
    52  	}
    53  	defer func() {
    54  		s := recover()
    55  		if s == nil {
    56  			t.Fatal("did not panic")
    57  		}
    58  		if s.(string) != "callback panic" {
    59  			t.Fatal("wrong panic:", s)
    60  		}
    61  		if lockedOSThread() {
    62  			t.Fatal("locked OS thread on exit from TestCallbackPanic")
    63  		}
    64  	}()
    65  	nestedCall(func() { panic("callback panic") })
    66  	panic("nestedCall returned")
    67  }
    68  
    69  func testCallbackPanicLoop(t *testing.T) {
    70  	// Make sure we don't blow out m->g0 stack.
    71  	for i := 0; i < 100000; i++ {
    72  		testCallbackPanic(t)
    73  	}
    74  }
    75  
    76  func testCallbackPanicLocked(t *testing.T) {
    77  	runtime.LockOSThread()
    78  	defer runtime.UnlockOSThread()
    79  
    80  	if !lockedOSThread() {
    81  		t.Fatal("runtime.LockOSThread didn't")
    82  	}
    83  	defer func() {
    84  		s := recover()
    85  		if s == nil {
    86  			t.Fatal("did not panic")
    87  		}
    88  		if s.(string) != "callback panic" {
    89  			t.Fatal("wrong panic:", s)
    90  		}
    91  		if !lockedOSThread() {
    92  			t.Fatal("lost lock on OS thread after panic")
    93  		}
    94  	}()
    95  	nestedCall(func() { panic("callback panic") })
    96  	panic("nestedCall returned")
    97  }
    98  
    99  // Callback with zero arguments used to make the stack misaligned,
   100  // which broke the garbage collector and other things.
   101  func testZeroArgCallback(t *testing.T) {
   102  	defer func() {
   103  		s := recover()
   104  		if s != nil {
   105  			t.Fatal("panic during callback:", s)
   106  		}
   107  	}()
   108  	C.callGoFoo()
   109  }
   110  
   111  //export goFoo
   112  func goFoo() {
   113  	x := 1
   114  	for i := 0; i < 10000; i++ {
   115  		// variadic call mallocs + writes to
   116  		variadic(x, x, x)
   117  		if x != 1 {
   118  			panic("bad x")
   119  		}
   120  	}
   121  }
   122  
   123  func variadic(x ...interface{}) {}
   124  
   125  func testBlocking(t *testing.T) {
   126  	c := make(chan int)
   127  	go func() {
   128  		for i := 0; i < 10; i++ {
   129  			c <- <-c
   130  		}
   131  	}()
   132  	nestedCall(func() {
   133  		for i := 0; i < 10; i++ {
   134  			c <- i
   135  			if j := <-c; j != i {
   136  				t.Errorf("out of sync %d != %d", j, i)
   137  			}
   138  		}
   139  	})
   140  }
   141  
   142  // Test that the stack can be unwound through a call out and call back
   143  // into Go.
   144  func testCallbackCallers(t *testing.T) {
   145  	pc := make([]uintptr, 100)
   146  	n := 0
   147  	name := []string{
   148  		"test.goCallback",
   149  		"runtime.cgocallbackg",
   150  		"runtime.cgocallback_gofunc",
   151  		"return",
   152  		"runtime.cgocall",
   153  		"test._Cfunc_callback",
   154  		"test.nestedCall",
   155  		"test.testCallbackCallers",
   156  		"test.TestCallbackCallers",
   157  		"testing.tRunner",
   158  		"runtime.goexit",
   159  	}
   160  	nestedCall(func() {
   161  		n = runtime.Callers(2, pc)
   162  	})
   163  	if n != len(name) {
   164  		t.Errorf("expected %d frames, got %d", len(name), n)
   165  	}
   166  	for i := 0; i < n; i++ {
   167  		f := runtime.FuncForPC(pc[i])
   168  		if f == nil {
   169  			t.Fatalf("expected non-nil Func for pc %p", pc[i])
   170  		}
   171  		fname := f.Name()
   172  		// Remove the prepended pathname from automatically
   173  		// generated cgo function names.
   174  		if strings.HasPrefix(fname, "_") {
   175  			fname = path.Base(f.Name()[1:])
   176  		}
   177  		if fname != name[i] {
   178  			t.Errorf("expected function name %s, got %s", name[i], fname)
   179  		}
   180  	}
   181  }