OLD | NEW |
(Empty) | |
| 1 package cgotest |
| 2 |
| 3 /* |
| 4 void callback(void *f); |
| 5 void callGoFoo(void) { |
| 6 extern void goFoo(void); |
| 7 goFoo(); |
| 8 } |
| 9 */ |
| 10 import "C" |
| 11 |
| 12 import ( |
| 13 "runtime" |
| 14 "testing" |
| 15 "unsafe" |
| 16 ) |
| 17 |
| 18 // nestedCall calls into C, back into Go, and finally to f. |
| 19 func nestedCall(f func()) { |
| 20 // NOTE: Depends on representation of f. |
| 21 // callback(x) calls goCallback(x) |
| 22 C.callback(*(*unsafe.Pointer)(unsafe.Pointer(&f))) |
| 23 } |
| 24 |
| 25 //export goCallback |
| 26 func goCallback(p unsafe.Pointer) { |
| 27 (*(*func())(unsafe.Pointer(&p)))() |
| 28 } |
| 29 |
| 30 func TestCallback(t *testing.T) { |
| 31 var x = false |
| 32 nestedCall(func(){x = true}) |
| 33 if !x { |
| 34 t.Fatal("nestedCall did not call func") |
| 35 } |
| 36 } |
| 37 |
| 38 func TestCallbackGC(t *testing.T) { |
| 39 nestedCall(runtime.GC) |
| 40 } |
| 41 |
| 42 func lockedOSThread() bool // in runtime.c |
| 43 |
| 44 func TestCallbackPanic(t *testing.T) { |
| 45 // Make sure panic during callback unwinds properly. |
| 46 if lockedOSThread() { |
| 47 t.Fatal("locked OS thread on entry to TestCallbackPanic") |
| 48 } |
| 49 defer func() { |
| 50 s := recover() |
| 51 if s == nil { |
| 52 t.Fatal("did not panic") |
| 53 } |
| 54 if s.(string) != "callback panic" { |
| 55 t.Fatal("wrong panic:", s) |
| 56 } |
| 57 if lockedOSThread() { |
| 58 t.Fatal("locked OS thread on exit from TestCallbackPanic
") |
| 59 } |
| 60 }() |
| 61 nestedCall(func(){panic("callback panic")}) |
| 62 panic("nestedCall returned") |
| 63 } |
| 64 |
| 65 func TestCallbackPanicLoop(t *testing.T) { |
| 66 // Make sure we don't blow out m->g0 stack. |
| 67 for i := 0; i < 100000; i++ { |
| 68 TestCallbackPanic(t) |
| 69 } |
| 70 } |
| 71 |
| 72 func TestCallbackPanicLocked(t *testing.T) { |
| 73 runtime.LockOSThread() |
| 74 defer runtime.UnlockOSThread() |
| 75 |
| 76 if !lockedOSThread() { |
| 77 t.Fatal("runtime.LockOSThread didn't") |
| 78 } |
| 79 defer func() { |
| 80 s := recover() |
| 81 if s == nil { |
| 82 t.Fatal("did not panic") |
| 83 } |
| 84 if s.(string) != "callback panic" { |
| 85 t.Fatal("wrong panic:", s) |
| 86 } |
| 87 if !lockedOSThread() { |
| 88 t.Fatal("lost lock on OS thread after panic") |
| 89 } |
| 90 }() |
| 91 nestedCall(func(){panic("callback panic")}) |
| 92 panic("nestedCall returned") |
| 93 } |
| 94 |
| 95 // Callback with zero arguments used to make the stack misaligned, |
| 96 // which broke the garbage collector and other things. |
| 97 func TestZeroArgCallback(t *testing.T) { |
| 98 defer func() { |
| 99 s := recover() |
| 100 if s != nil { |
| 101 t.Fatal("panic during callback:", s) |
| 102 } |
| 103 }() |
| 104 C.callGoFoo() |
| 105 } |
| 106 |
| 107 //export goFoo |
| 108 func goFoo() { |
| 109 x := 1 |
| 110 for i := 0; i < 10000; i++ { |
| 111 // variadic call mallocs + writes to· |
| 112 variadic(x, x, x) |
| 113 if x != 1 { |
| 114 panic("bad x") |
| 115 } |
| 116 } |
| 117 } |
| 118 |
| 119 func variadic(x ...interface{}) {} |
| 120 |
| 121 func TestBlocking(t *testing.T) { |
| 122 c := make(chan int) |
| 123 go func() { |
| 124 for i := 0; i < 10; i++ { |
| 125 c <- <-c |
| 126 } |
| 127 }() |
| 128 nestedCall(func(){ |
| 129 for i := 0; i < 10; i++ { |
| 130 c <- i |
| 131 if j := <-c; j != i { |
| 132 t.Errorf("out of sync %d != %d", j, i) |
| 133 } |
| 134 } |
| 135 }) |
| 136 } |
OLD | NEW |