| package cgotest | |
| /* | |
| void callback(void *f); | |
| void callGoFoo(void) { | |
| extern void goFoo(void); | |
| goFoo(); | |
| } | |
| */ | |
| import "C" | |
| import ( | |
| "runtime" | |
| "testing" | |
| "unsafe" | |
| ) | |
| // nestedCall calls into C, back into Go, and finally to f. | |
| func nestedCall(f func()) { | |
| // NOTE: Depends on representation of f. | |
| // callback(x) calls goCallback(x) | |
| C.callback(*(*unsafe.Pointer)(unsafe.Pointer(&f))) | |
| } | |
| //export goCallback | |
| func goCallback(p unsafe.Pointer) { | |
| (*(*func())(unsafe.Pointer(&p)))() | |
| } | |
| func testCallback(t *testing.T) { | |
| var x = false | |
| nestedCall(func() { x = true }) | |
| if !x { | |
| t.Fatal("nestedCall did not call func") | |
| } | |
| } | |
| func testCallbackGC(t *testing.T) { | |
| nestedCall(runtime.GC) | |
| } | |
| func lockedOSThread() bool // in runtime.c | |
| func testCallbackPanic(t *testing.T) { | |
| // Make sure panic during callback unwinds properly. | |
| if lockedOSThread() { | |
| t.Fatal("locked OS thread on entry to TestCallbackPanic") | |
| } | |
| defer func() { | |
| s := recover() | |
| if s == nil { | |
| t.Fatal("did not panic") | |
| } | |
| if s.(string) != "callback panic" { | |
| t.Fatal("wrong panic:", s) | |
| } | |
| if lockedOSThread() { | |
| t.Fatal("locked OS thread on exit from TestCallbackPanic") | |
| } | |
| }() | |
| nestedCall(func() { panic("callback panic") }) | |
| panic("nestedCall returned") | |
| } | |
| func testCallbackPanicLoop(t *testing.T) { | |
| // Make sure we don't blow out m->g0 stack. | |
| for i := 0; i < 100000; i++ { | |
| testCallbackPanic(t) | |
| } | |
| } | |
| func testCallbackPanicLocked(t *testing.T) { | |
| runtime.LockOSThread() | |
| defer runtime.UnlockOSThread() | |
| if !lockedOSThread() { | |
| t.Fatal("runtime.LockOSThread didn't") | |
| } | |
| defer func() { | |
| s := recover() | |
| if s == nil { | |
| t.Fatal("did not panic") | |
| } | |
| if s.(string) != "callback panic" { | |
| t.Fatal("wrong panic:", s) | |
| } | |
| if !lockedOSThread() { | |
| t.Fatal("lost lock on OS thread after panic") | |
| } | |
| }() | |
| nestedCall(func() { panic("callback panic") }) | |
| panic("nestedCall returned") | |
| } | |
| // Callback with zero arguments used to make the stack misaligned, | |
| // which broke the garbage collector and other things. | |
| func testZeroArgCallback(t *testing.T) { | |
| defer func() { | |
| s := recover() | |
| if s != nil { | |
| t.Fatal("panic during callback:", s) | |
| } | |
| }() | |
| C.callGoFoo() | |
| } | |
| //export goFoo | |
| func goFoo() { | |
| x := 1 | |
| for i := 0; i < 10000; i++ { | |
| // variadic call mallocs + writes to | |
| variadic(x, x, x) | |
| if x != 1 { | |
| panic("bad x") | |
| } | |
| } | |
| } | |
| func variadic(x ...interface{}) {} | |
| func testBlocking(t *testing.T) { | |
| c := make(chan int) | |
| go func() { | |
| for i := 0; i < 10; i++ { | |
| c <- <-c | |
| } | |
| }() | |
| nestedCall(func() { | |
| for i := 0; i < 10; i++ { | |
| c <- i | |
| if j := <-c; j != i { | |
| t.Errorf("out of sync %d != %d", j, i) | |
| } | |
| } | |
| }) | |
| } |