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 }