Fail loudly on a drm read instead of leaving the panel frozen on its last frame

This commit is contained in:
flamingcow
2026-08-04 22:11:58 -07:00
parent dd94f4cab5
commit f5d05d1fc5
+19 -2
View File
@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"math" "math"
"path/filepath" "path/filepath"
"sync/atomic"
"unsafe" "unsafe"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
@@ -49,6 +50,9 @@ type framebuffer struct {
// One token per completed flip. The render loop waits on this rather than on // One token per completed flip. The render loop waits on this rather than on
// a timer, so drawing is paced by the panel instead of by a guess at its rate. // a timer, so drawing is paced by the panel instead of by a guess at its rate.
flips chan struct{} flips chan struct{}
// Set before the fd goes, so the event reader can tell shutdown from failure.
closing atomic.Bool
} }
func (fb *framebuffer) offset(x, y int) int { func (fb *framebuffer) offset(x, y int) int {
@@ -275,18 +279,30 @@ func openFramebuffer() (*framebuffer, error) {
} }
// Flip completions arrive on the drm fd as a stream of length-prefixed events. // Flip completions arrive on the drm fd as a stream of length-prefixed events.
// Nothing else feeds fb.flips, so returning early here freezes the panel on its
// last frame while everything else goes on running.
func (fb *framebuffer) readEvents() { func (fb *framebuffer) readEvents() {
buf := make([]byte, 4096) buf := make([]byte, 4096)
for { for {
n, err := unix.Read(fb.fd, buf) n, err := unix.Read(fb.fd, buf)
if n <= 0 || err != nil { if fb.closing.Load() {
return return
} }
if err == unix.EINTR || err == unix.EAGAIN {
continue
}
if err != nil {
panic(fmt.Sprintf("reading drm events: %v", err))
}
if n == 0 {
panic("drm fd reported end of file")
}
for off := 0; off+8 <= n; { for off := 0; off+8 <= n; {
typ := binary.LittleEndian.Uint32(buf[off:]) typ := binary.LittleEndian.Uint32(buf[off:])
length := int(binary.LittleEndian.Uint32(buf[off+4:])) length := int(binary.LittleEndian.Uint32(buf[off+4:]))
if length < 8 || off+length > n { if length < 8 || off+length > n {
return panic(fmt.Sprintf("drm event at offset %d claims %d bytes of %d read",
off, length, n))
} }
if typ == drm.EventFlipComplete { if typ == drm.EventFlipComplete {
select { select {
@@ -300,6 +316,7 @@ func (fb *framebuffer) readEvents() {
} }
func (fb *framebuffer) close() { func (fb *framebuffer) close() {
fb.closing.Store(true)
for i := range fb.bufs { for i := range fb.bufs {
if fb.bufs[i].mem != nil { if fb.bufs[i].mem != nil {
unix.Munmap(fb.bufs[i].mem) unix.Munmap(fb.bufs[i].mem)