Got rid of my Windows input lag problems on Windows (triggered by the switch to the concurrent garbage collector sweep routine in the Go runtime) by treating <-uitask as a Windows message itself posted to the message loop thread and not making the message loop a CPU waster.
This commit is contained in:
parent
43dd5121b2
commit
bd51e3e9a7
|
@ -7,6 +7,12 @@ import (
|
|||
"runtime"
|
||||
)
|
||||
|
||||
/*
|
||||
problem: messages have to be dispatched on the same thread as system calls, and we can't mux GetMessage() with select, and PeekMessage() every iteration is wasteful (and leads to lag for me (only) with the concurrent garbage collector sweep)
|
||||
solution: use PostThreadMessage() to send uimsgs out to the message loop, which runs on its own goroutine
|
||||
I had come up with this first but wanted to try other things before doing it (and wasn't really sure if user-defined messages were safe, not quite understanding the system); nsf came up with it independently and explained that this was really the only right way to do it, so thanks to him
|
||||
*/
|
||||
|
||||
var uitask chan *uimsg
|
||||
|
||||
type uimsg struct {
|
||||
|
@ -20,35 +26,52 @@ type uiret struct {
|
|||
err error
|
||||
}
|
||||
|
||||
const (
|
||||
_WM_APP = 0x8000 + iota
|
||||
msgRequested
|
||||
)
|
||||
|
||||
var (
|
||||
_postThreadMessage = user32.NewProc("PostThreadMessageW")
|
||||
)
|
||||
|
||||
func ui(initDone chan error) {
|
||||
runtime.LockOSThread()
|
||||
|
||||
uitask = make(chan *uimsg)
|
||||
initDone <- doWindowsInit()
|
||||
|
||||
threadIDReq := make(chan uintptr)
|
||||
msglooperrs := make(chan error)
|
||||
go msgloop(threadIDReq, msglooperrs)
|
||||
threadID := <-threadIDReq
|
||||
|
||||
quit := false
|
||||
for !quit {
|
||||
select {
|
||||
case m := <-uitask:
|
||||
r1, _, err := m.call.Call(m.p...)
|
||||
m.ret <- uiret{
|
||||
ret: r1,
|
||||
err: err,
|
||||
r1, _, err := _postThreadMessage.Call(
|
||||
threadID,
|
||||
msgRequested,
|
||||
uintptr(0),
|
||||
uintptr(unsafe.Pointer(m)))
|
||||
if r1 == 0 { // failure
|
||||
panic("error sending message to message loop to call function: " + err.Error()) // TODO
|
||||
}
|
||||
case err := <-msglooperrs:
|
||||
if err == nil { // WM_QUIT; no error
|
||||
quit = true
|
||||
} else {
|
||||
panic("unexpected return from message loop: " + err.Error()) // TODO
|
||||
}
|
||||
default:
|
||||
quit = msgloopstep()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
_PM_REMOVE = 0x0001
|
||||
)
|
||||
|
||||
var (
|
||||
_dispatchMessage = user32.NewProc("DispatchMessageW")
|
||||
_getMessage = user32.NewProc("GetMessageW")
|
||||
_peekMessage = user32.NewProc("PeekMessageW")
|
||||
_getCurrentThreadID = kernel32.NewProc("GetCurrentThreadId")
|
||||
_postQuitMessage = user32.NewProc("PostQuitMessage")
|
||||
_sendMessage = user32.NewProc("SendMessageW")
|
||||
_translateMessage = user32.NewProc("TranslateMessage")
|
||||
|
@ -56,7 +79,9 @@ var (
|
|||
|
||||
var getMessageFail = -1 // because Go doesn't let me
|
||||
|
||||
func msgloopstep() (quit bool) {
|
||||
func msgloop(threadID chan uintptr, errors chan error) {
|
||||
runtime.LockOSThread()
|
||||
|
||||
var msg struct {
|
||||
Hwnd _HWND
|
||||
Message uint32
|
||||
|
@ -66,19 +91,32 @@ func msgloopstep() (quit bool) {
|
|||
Pt _POINT
|
||||
}
|
||||
|
||||
r1, _, _ := _peekMessage.Call(
|
||||
uintptr(unsafe.Pointer(&msg)),
|
||||
uintptr(_NULL),
|
||||
uintptr(0),
|
||||
uintptr(0),
|
||||
uintptr(_PM_REMOVE))
|
||||
if r1 == 0 { // no message available
|
||||
return false
|
||||
r1, _, _ := _getCurrentThreadID.Call()
|
||||
threadID <- r1
|
||||
for {
|
||||
r1, _, err := _getMessage.Call(
|
||||
uintptr(unsafe.Pointer(&msg)),
|
||||
uintptr(_NULL),
|
||||
uintptr(0),
|
||||
uintptr(0))
|
||||
if r1 == uintptr(getMessageFail) { // error
|
||||
errors <- err
|
||||
return
|
||||
}
|
||||
if r1 == 0 { // WM_QUIT message
|
||||
errors <- nil
|
||||
return
|
||||
}
|
||||
if msg.Message == msgRequested {
|
||||
m := (*uimsg)(unsafe.Pointer(msg.LParam))
|
||||
r1, _, err := m.call.Call(m.p...)
|
||||
m.ret <- uiret{
|
||||
ret: r1,
|
||||
err: err,
|
||||
}
|
||||
continue
|
||||
}
|
||||
_translateMessage.Call(uintptr(unsafe.Pointer(&msg)))
|
||||
_dispatchMessage.Call(uintptr(unsafe.Pointer(&msg)))
|
||||
}
|
||||
if msg.Message == _WM_QUIT {
|
||||
return true
|
||||
}
|
||||
_translateMessage.Call(uintptr(unsafe.Pointer(&msg)))
|
||||
_dispatchMessage.Call(uintptr(unsafe.Pointer(&msg)))
|
||||
return false
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue