From 830a715b6d68d6e0e998d06abc3aa8e4dcf8d534 Mon Sep 17 00:00:00 2001 From: bbedward Date: Sat, 13 Dec 2025 00:46:30 -0500 Subject: [PATCH] wlcontext: use poll with wake pipe instead of read deadlines --- core/internal/server/clipboard/manager.go | 3 +- core/internal/server/wlcontext/context.go | 76 +++++++++++++++---- core/pkg/go-wayland/wayland/client/context.go | 12 +++ distro/scripts/ppa-status.sh | 39 +++++----- 4 files changed, 94 insertions(+), 36 deletions(-) diff --git a/core/internal/server/clipboard/manager.go b/core/internal/server/clipboard/manager.go index 023c416c..a49f14e6 100644 --- a/core/internal/server/clipboard/manager.go +++ b/core/internal/server/clipboard/manager.go @@ -500,14 +500,13 @@ func (m *Manager) receiveData(offer *ext_data_control.ExtDataControlOfferV1, mim receiveErr := make(chan error, 1) m.post(func() { err := offer.Receive(mimeType, int(w.Fd())) + w.Close() receiveErr <- err }) if err := <-receiveErr; err != nil { - w.Close() return nil, err } - w.Close() type result struct { data []byte diff --git a/core/internal/server/wlcontext/context.go b/core/internal/server/wlcontext/context.go index 01f7f11a..f5ec4dcc 100644 --- a/core/internal/server/wlcontext/context.go +++ b/core/internal/server/wlcontext/context.go @@ -1,11 +1,11 @@ package wlcontext import ( - "errors" "fmt" "os" "sync" - "time" + + "golang.org/x/sys/unix" "github.com/AvengeMedia/DankMaterialShell/core/internal/errdefs" "github.com/AvengeMedia/DankMaterialShell/core/internal/log" @@ -27,6 +27,8 @@ type SharedContext struct { stopChan chan struct{} fatalError chan error cmdQueue chan func() + wakeR int + wakeW int wg sync.WaitGroup mu sync.Mutex started bool @@ -38,11 +40,31 @@ func New() (*SharedContext, error) { return nil, fmt.Errorf("%w: %v", errdefs.ErrNoWaylandDisplay, err) } + fds := make([]int, 2) + if err := unix.Pipe(fds); err != nil { + display.Context().Close() + return nil, fmt.Errorf("failed to create wake pipe: %w", err) + } + if err := unix.SetNonblock(fds[0], true); err != nil { + unix.Close(fds[0]) + unix.Close(fds[1]) + display.Context().Close() + return nil, fmt.Errorf("failed to set wake pipe nonblock: %w", err) + } + if err := unix.SetNonblock(fds[1], true); err != nil { + unix.Close(fds[0]) + unix.Close(fds[1]) + display.Context().Close() + return nil, fmt.Errorf("failed to set wake pipe nonblock: %w", err) + } + sc := &SharedContext{ display: display, stopChan: make(chan struct{}), fatalError: make(chan error, 1), cmdQueue: make(chan func(), 256), + wakeR: fds[0], + wakeW: fds[1], started: false, } @@ -69,6 +91,9 @@ func (sc *SharedContext) Display() *wlclient.Display { func (sc *SharedContext) Post(fn func()) { select { case sc.cmdQueue <- fn: + if _, err := unix.Write(sc.wakeW, []byte{1}); err != nil && err != unix.EAGAIN { + log.Errorf("wake pipe write error: %v", err) + } default: } } @@ -89,7 +114,14 @@ func (sc *SharedContext) eventDispatcher() { } } }() + ctx := sc.display.Context() + wlFd := ctx.Fd() + + pollFds := []unix.PollFd{ + {Fd: int32(wlFd), Events: unix.POLLIN}, + {Fd: int32(sc.wakeR), Events: unix.POLLIN}, + } for { select { @@ -100,20 +132,33 @@ func (sc *SharedContext) eventDispatcher() { sc.drainCmdQueue() - if err := ctx.SetReadDeadline(time.Now().Add(50 * time.Millisecond)); err != nil { - log.Errorf("Failed to set read deadline: %v", err) - } - err := ctx.Dispatch() - if err := ctx.SetReadDeadline(time.Time{}); err != nil { - log.Errorf("Failed to clear read deadline: %v", err) + n, err := unix.Poll(pollFds, 50) + if err != nil { + if err == unix.EINTR { + continue + } + log.Errorf("Poll error: %v", err) + return } - switch { - case err == nil: - case errors.Is(err, os.ErrDeadlineExceeded): - default: - log.Errorf("Wayland connection error: %v", err) - return + if n == 0 { + continue + } + + if pollFds[1].Revents&unix.POLLIN != 0 { + var buf [64]byte + if _, err := unix.Read(sc.wakeR, buf[:]); err != nil && err != unix.EAGAIN { + log.Errorf("wake pipe read error: %v", err) + } + } + + if pollFds[0].Revents&unix.POLLIN != 0 { + if err := ctx.Dispatch(); err != nil { + if !os.IsTimeout(err) { + log.Errorf("Wayland connection error: %v", err) + return + } + } } } } @@ -133,6 +178,9 @@ func (sc *SharedContext) Close() { close(sc.stopChan) sc.wg.Wait() + unix.Close(sc.wakeR) + unix.Close(sc.wakeW) + if sc.display != nil { sc.display.Context().Close() } diff --git a/core/pkg/go-wayland/wayland/client/context.go b/core/pkg/go-wayland/wayland/client/context.go index 56bf4657..4eee2cb6 100644 --- a/core/pkg/go-wayland/wayland/client/context.go +++ b/core/pkg/go-wayland/wayland/client/context.go @@ -58,6 +58,18 @@ func (ctx *Context) SetReadDeadline(t time.Time) error { return ctx.conn.SetReadDeadline(t) } +func (ctx *Context) Fd() int { + rawConn, err := ctx.conn.SyscallConn() + if err != nil { + return -1 + } + var fd int + rawConn.Control(func(f uintptr) { + fd = int(f) + }) + return fd +} + // Dispatch reads and processes incoming messages and calls [client.Dispatcher.Dispatch] on the // respective wayland protocol. // Dispatch must be called on the same goroutine as other interactions with the Context. diff --git a/distro/scripts/ppa-status.sh b/distro/scripts/ppa-status.sh index 78674e6e..d1c9a373 100755 --- a/distro/scripts/ppa-status.sh +++ b/distro/scripts/ppa-status.sh @@ -105,7 +105,7 @@ get_status_display() { # Check each PPA for PPA_NAME in "${PPAS[@]}"; do PPA_ARCHIVE="${LAUNCHPAD_API}/~${PPA_OWNER}/+archive/ubuntu/${PPA_NAME}" - + echo "==========================================" echo "=== PPA: ${PPA_OWNER}/${PPA_NAME} ===" echo "==========================================" @@ -122,55 +122,55 @@ for PPA_NAME in "${PPAS[@]}"; do echo "----------------------------------------" echo "--- $pkg ---" echo "----------------------------------------" - + # Get published sources for this package SOURCES_URL="${PPA_ARCHIVE}?ws.op=getPublishedSources&source_name=${pkg}&distro_series=${LAUNCHPAD_API}/ubuntu/${DISTRO_SERIES}&status=Published" - + SOURCES=$(curl -s "$SOURCES_URL" 2>/dev/null) - + if [[ -z "$SOURCES" ]] || [[ "$SOURCES" == "null" ]]; then echo " ⚠️ No published sources found" echo "" continue fi - + # Get the latest source TOTAL=$(echo "$SOURCES" | jq '.total_size // 0') - + if [[ "$TOTAL" == "0" ]]; then echo " ⚠️ No published sources found for $DISTRO_SERIES" echo "" continue fi - + # Get most recent entry ENTRY=$(echo "$SOURCES" | jq '.entries[0]') - + if [[ "$ENTRY" == "null" ]]; then echo " ⚠️ No source entries found" echo "" continue fi - + # Extract source info VERSION=$(echo "$ENTRY" | jq -r '.source_package_version // "unknown"') STATUS=$(echo "$ENTRY" | jq -r '.status // "unknown"') DATE_PUBLISHED=$(echo "$ENTRY" | jq -r '.date_published // "unknown"') SELF_LINK=$(echo "$ENTRY" | jq -r '.self_link // ""') - + echo " 📦 Version: $VERSION" echo " 📅 Published: ${DATE_PUBLISHED%T*}" echo " 📋 Source Status: $STATUS" echo "" - + # Get builds for this source if [[ -n "$SELF_LINK" && "$SELF_LINK" != "null" ]]; then BUILDS_URL="${SELF_LINK}?ws.op=getBuilds" BUILDS=$(curl -s "$BUILDS_URL" 2>/dev/null) - + if [[ -n "$BUILDS" && "$BUILDS" != "null" ]]; then BUILD_COUNT=$(echo "$BUILDS" | jq '.total_size // 0') - + if [[ "$BUILD_COUNT" -gt 0 ]]; then echo " Builds:" echo "$BUILDS" | jq -r '.entries[] | "\(.arch_tag) \(.buildstate)"' 2>/dev/null | while read -r line; do @@ -182,18 +182,18 @@ for PPA_NAME in "${PPAS[@]}"; do fi fi fi - + # Alternative: Get build records directly from archive BUILD_RECORDS_URL="${PPA_ARCHIVE}?ws.op=getBuildRecords&source_name=${pkg}" BUILD_RECORDS=$(curl -s "$BUILD_RECORDS_URL" 2>/dev/null) - + if [[ -n "$BUILD_RECORDS" && "$BUILD_RECORDS" != "null" ]]; then RECORD_COUNT=$(echo "$BUILD_RECORDS" | jq '.total_size // 0') - + if [[ "$RECORD_COUNT" -gt 0 ]]; then echo "" echo " Recent build history:" - + # Get unique version+arch combinations echo "$BUILD_RECORDS" | jq -r '.entries[:6][] | "\(.source_package_version) \(.arch_tag) \(.buildstate)"' 2>/dev/null | while read -r line; do VER=$(echo "$line" | awk '{print $1}') @@ -204,10 +204,10 @@ for PPA_NAME in "${PPAS[@]}"; do done fi fi - + echo "" done - + echo "View full PPA at: https://launchpad.net/~${PPA_OWNER}/+archive/ubuntu/${PPA_NAME}" echo "" done @@ -215,4 +215,3 @@ done echo "==========================================" echo "Status check complete!" echo "" -