Commit b776b9e7 authored by Alex Brainman's avatar Alex Brainman

runtime: add windows callback tests

Just a copy of cgo callback tests from misc/cgo/test.

R=rsc
CC=golang-dev, hectorchu
https://golang.org/cl/5331062
parent 2fcb0452
...@@ -18,6 +18,8 @@ var F64toint = f64toint ...@@ -18,6 +18,8 @@ var F64toint = f64toint
func entersyscall() func entersyscall()
func exitsyscall() func exitsyscall()
func golockedOSThread() bool
var Entersyscall = entersyscall var Entersyscall = entersyscall
var Exitsyscall = exitsyscall var Exitsyscall = exitsyscall
var LockedOSThread = golockedOSThread
...@@ -1586,6 +1586,14 @@ runtime·lockedOSThread(void) ...@@ -1586,6 +1586,14 @@ runtime·lockedOSThread(void)
return g->lockedm != nil && m->lockedg != nil; return g->lockedm != nil && m->lockedg != nil;
} }
// for testing of callbacks
void
runtime·golockedOSThread(bool ret)
{
ret = runtime·lockedOSThread();
FLUSH(&ret);
}
// for testing of wire, unwire // for testing of wire, unwire
void void
runtime·mid(uint32 ret) runtime·mid(uint32 ret)
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package runtime_test package runtime_test
import ( import (
"runtime"
"syscall" "syscall"
"testing" "testing"
"unsafe" "unsafe"
...@@ -120,7 +121,7 @@ func TestCDecl(t *testing.T) { ...@@ -120,7 +121,7 @@ func TestCDecl(t *testing.T) {
} }
} }
func TestCallback(t *testing.T) { func TestEnumWindows(t *testing.T) {
d := GetDLL(t, "user32.dll") d := GetDLL(t, "user32.dll")
isWindows := d.Proc("IsWindow") isWindows := d.Proc("IsWindow")
counter := 0 counter := 0
...@@ -144,6 +145,99 @@ func TestCallback(t *testing.T) { ...@@ -144,6 +145,99 @@ func TestCallback(t *testing.T) {
} }
} }
func callback(hwnd syscall.Handle, lparam uintptr) uintptr {
(*(*func())(unsafe.Pointer(&lparam)))()
return 0 // stop enumeration
}
// nestedCall calls into Windows, back into Go, and finally to f.
func nestedCall(t *testing.T, f func()) {
c := syscall.NewCallback(callback)
d := GetDLL(t, "user32.dll")
defer d.Release()
d.Proc("EnumWindows").Call(c, uintptr(*(*unsafe.Pointer)(unsafe.Pointer(&f))))
}
func TestCallback(t *testing.T) {
var x = false
nestedCall(t, func() { x = true })
if !x {
t.Fatal("nestedCall did not call func")
}
}
func TestCallbackGC(t *testing.T) {
nestedCall(t, runtime.GC)
}
func TestCallbackPanic(t *testing.T) {
// Make sure panic during callback unwinds properly.
if runtime.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 runtime.LockedOSThread() {
t.Fatal("locked OS thread on exit from TestCallbackPanic")
}
}()
nestedCall(t, 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 !runtime.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 !runtime.LockedOSThread() {
t.Fatal("lost lock on OS thread after panic")
}
}()
nestedCall(t, func() { panic("callback panic") })
panic("nestedCall returned")
}
func TestBlockingCallback(t *testing.T) {
c := make(chan int)
go func() {
for i := 0; i < 10; i++ {
c <- <-c
}
}()
nestedCall(t, func() {
for i := 0; i < 10; i++ {
c <- i
if j := <-c; j != i {
t.Errorf("out of sync %d != %d", j, i)
}
}
})
}
func TestCallbackInAnotherThread(t *testing.T) { func TestCallbackInAnotherThread(t *testing.T) {
// TODO: test a function which calls back in another thread: QueueUserAPC() or CreateThread() // TODO: test a function which calls back in another thread: QueueUserAPC() or CreateThread()
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment