From 47db461546f355b971c3ca35d4dec48778c172ab Mon Sep 17 00:00:00 2001 From: Ophestra Date: Thu, 11 Dec 2025 04:07:55 +0900 Subject: [PATCH] internal/pipewire: generic Core::Error handling This flushes message buffer before queueing the event expecting the error. Since this is quite useful and relatively complex, it is relocated to a method of Context. Signed-off-by: Ophestra --- internal/pipewire/core.go | 37 +++++++++++++++-------------------- internal/pipewire/pipewire.go | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/internal/pipewire/core.go b/internal/pipewire/core.go index a53f02d..d115da3 100644 --- a/internal/pipewire/core.go +++ b/internal/pipewire/core.go @@ -642,33 +642,28 @@ func (registry *Registry) destroy(id Int) error { } // Destroy tries to destroy the global object with id. -func (registry *Registry) Destroy(id Int) error { - if err := registry.destroy(id); err != nil { +func (registry *Registry) Destroy(id Int) (err error) { + asCoreError := registry.ctx.expectsCoreError(registry.ID, &err) + if err != nil { + return + } + if err = registry.destroy(id); err != nil { return err } - destroySeq := registry.ctx.currentSeq() - - err := registry.ctx.GetCore().Sync() - if err == nil { + if err = registry.ctx.GetCore().Sync(); err == nil { return nil } - var coreError *CoreError - if proxyErrors, ok := err.(ProxyConsumeError); !ok || - len(proxyErrors) != 1 || - !errors.As(proxyErrors[0], &coreError) || - coreError == nil || - coreError.ID != registry.ID || - coreError.Sequence != destroySeq { - return err - } + if coreError := asCoreError(); coreError == nil { + return + } else { + switch syscall.Errno(-coreError.Result) { + case syscall.EPERM: + return &PermissionError{registry.ID, coreError.Message} - switch syscall.Errno(-coreError.Result) { - case syscall.EPERM: - return &PermissionError{registry.ID, coreError.Message} - - default: - return coreError + default: + return coreError + } } } diff --git a/internal/pipewire/pipewire.go b/internal/pipewire/pipewire.go index 5473797..636c759 100644 --- a/internal/pipewire/pipewire.go +++ b/internal/pipewire/pipewire.go @@ -16,6 +16,7 @@ package pipewire import ( "encoding/binary" + "errors" "fmt" "io" "net" @@ -786,6 +787,40 @@ func (ctx *Context) Close() (err error) { } } +// expectsCoreError returns a function that inspects an error value and +// returns the address of a [CoreError] if it is the only error present +// and targets the specified proxy and sequence. +// +// The behaviour of expectsCoreError is only correct for an empty buf +// prior to calling. If buf is not empty, [Core.Sync] is called, with +// its return value stored to the value pointed to by errP if not nil, +// and the function is not populated. +// +// The caller must queue a message and call [Core.Sync] immediately +// after calling expectsCoreError. +func (ctx *Context) expectsCoreError(id Int, errP *error) (asCoreError func() (coreError *CoreError)) { + if len(ctx.buf) > 0 { + if err := ctx.GetCore().Sync(); err != nil { + *errP = err + return nil + } + } + + sequence := ctx.sequence + return func() (coreError *CoreError) { + if proxyErrors, ok := (*errP).(ProxyConsumeError); !ok || + len(proxyErrors) != 1 || + !errors.As(proxyErrors[0], &coreError) || + coreError == nil || + coreError.ID != id || + coreError.Sequence != sequence { + // do not return a non-matching CoreError + coreError = nil + } + return + } +} + // A PermissionError describes an error emitted by the server when trying to // perform an operation that the client has no permission for. type PermissionError struct {