internal/pipewire: generic Core::Error handling
All checks were successful
Test / Create distribution (push) Successful in 58s
Test / Hakurei (push) Successful in 8m1s
Test / Hakurei (race detector) (push) Successful in 10m19s
Test / Sandbox (push) Successful in 1m30s
Test / Sandbox (race detector) (push) Successful in 2m18s
Test / Hpkg (push) Successful in 3m21s
Test / Flake checks (push) Successful in 1m31s

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 <cat@gensokyo.uk>
This commit is contained in:
2025-12-11 04:07:55 +09:00
parent 0a3fe5f907
commit 47db461546
2 changed files with 51 additions and 21 deletions

View File

@@ -642,33 +642,28 @@ func (registry *Registry) destroy(id Int) error {
} }
// Destroy tries to destroy the global object with id. // Destroy tries to destroy the global object with id.
func (registry *Registry) Destroy(id Int) error { func (registry *Registry) Destroy(id Int) (err error) {
if err := registry.destroy(id); err != nil { asCoreError := registry.ctx.expectsCoreError(registry.ID, &err)
if err != nil {
return
}
if err = registry.destroy(id); err != nil {
return err return err
} }
destroySeq := registry.ctx.currentSeq() if err = registry.ctx.GetCore().Sync(); err == nil {
err := registry.ctx.GetCore().Sync()
if err == nil {
return nil return nil
} }
var coreError *CoreError if coreError := asCoreError(); coreError == nil {
if proxyErrors, ok := err.(ProxyConsumeError); !ok || return
len(proxyErrors) != 1 || } else {
!errors.As(proxyErrors[0], &coreError) || switch syscall.Errno(-coreError.Result) {
coreError == nil || case syscall.EPERM:
coreError.ID != registry.ID || return &PermissionError{registry.ID, coreError.Message}
coreError.Sequence != destroySeq {
return err
}
switch syscall.Errno(-coreError.Result) { default:
case syscall.EPERM: return coreError
return &PermissionError{registry.ID, coreError.Message} }
default:
return coreError
} }
} }

View File

@@ -16,6 +16,7 @@ package pipewire
import ( import (
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"io" "io"
"net" "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 // A PermissionError describes an error emitted by the server when trying to
// perform an operation that the client has no permission for. // perform an operation that the client has no permission for.
type PermissionError struct { type PermissionError struct {