internal/pipewire: finalizers for dangling files
All checks were successful
Test / Create distribution (push) Successful in 27s
Test / Sandbox (race detector) (push) Successful in 41s
Test / Sandbox (push) Successful in 43s
Test / Hpkg (push) Successful in 43s
Test / Hakurei (push) Successful in 47s
Test / Hakurei (race detector) (push) Successful in 3m47s
Test / Flake checks (push) Successful in 1m26s
All checks were successful
Test / Create distribution (push) Successful in 27s
Test / Sandbox (race detector) (push) Successful in 41s
Test / Sandbox (push) Successful in 43s
Test / Hpkg (push) Successful in 43s
Test / Hakurei (push) Successful in 47s
Test / Hakurei (race detector) (push) Successful in 3m47s
Test / Flake checks (push) Successful in 1m26s
This makes their handling much less error-prone. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
c34439fc5f
commit
054c91879f
@ -16,11 +16,11 @@ package pipewire
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"maps"
|
"maps"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -102,6 +102,9 @@ type Context struct {
|
|||||||
headerFiles int
|
headerFiles int
|
||||||
// Files from the server. This is discarded on every Roundtrip so eventProxy
|
// Files from the server. This is discarded on every Roundtrip so eventProxy
|
||||||
// implementations must make sure to close them to avoid leaking fds.
|
// implementations must make sure to close them to avoid leaking fds.
|
||||||
|
//
|
||||||
|
// These are not automatically set up as [os.File] because it is impossible
|
||||||
|
// to undo the effects of os.NewFile, which can be inconvenient for some uses.
|
||||||
receivedFiles []int
|
receivedFiles []int
|
||||||
// Pending footer value for the next outgoing message.
|
// Pending footer value for the next outgoing message.
|
||||||
// Newer footers appear to simply replace the existing one.
|
// Newer footers appear to simply replace the existing one.
|
||||||
@ -428,8 +431,8 @@ func (e UnexpectedFilesError) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A DanglingFilesError holds onto files that were sent by the server but no [Header]
|
// A DanglingFilesError holds onto files that were sent by the server but no [Header]
|
||||||
// accounts for. These must be closed to avoid leaking fds.
|
// accounts for. These are closed by their finalizers if discarded.
|
||||||
type DanglingFilesError []int
|
type DanglingFilesError []*os.File
|
||||||
|
|
||||||
func (e DanglingFilesError) Error() string {
|
func (e DanglingFilesError) Error() string {
|
||||||
return "received " + strconv.Itoa(len(e)) + " dangling files"
|
return "received " + strconv.Itoa(len(e)) + " dangling files"
|
||||||
@ -591,17 +594,37 @@ func (ctx *Context) Roundtrip() (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var joinError []error
|
// prepared here so finalizers are set up, but should not prevent proxyErrors
|
||||||
if len(proxyErrors) > 0 {
|
// from reaching the caller as those describe the cause of these dangling fds
|
||||||
joinError = append(joinError, proxyErrors)
|
var danglingFiles DanglingFilesError
|
||||||
}
|
if len(ctx.receivedFiles) > receivedHeaderFiles {
|
||||||
if len(ctx.receivedFiles) < receivedHeaderFiles {
|
danglingFds := ctx.receivedFiles[receivedHeaderFiles:]
|
||||||
joinError = append(joinError, DanglingFilesError(ctx.receivedFiles[len(ctx.receivedFiles)-receivedHeaderFiles:]))
|
// having multiple *os.File with the same fd causes serious problems
|
||||||
}
|
slices.Sort(danglingFds)
|
||||||
if len(joinError) > 0 {
|
danglingFds = slices.Compact(danglingFds)
|
||||||
return errors.Join(joinError...)
|
|
||||||
|
danglingFiles = make(DanglingFilesError, 0, len(danglingFds))
|
||||||
|
for _, fd := range danglingFds {
|
||||||
|
// hold these as *os.File so they are closed if this error never reaches the caller,
|
||||||
|
// or the caller discards or otherwise does not handle this error, to avoid leaking fds
|
||||||
|
danglingFiles = append(danglingFiles, os.NewFile(uintptr(fd),
|
||||||
|
"dangling fd "+strconv.Itoa(fd)+" received from PipeWire"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// these are checked and made available first since they describe the cause
|
||||||
|
// of so-called symptoms checked after this point; the symptoms should only
|
||||||
|
// be made available as a catch-all if these are unavailable
|
||||||
|
if len(proxyErrors) > 0 {
|
||||||
|
return proxyErrors
|
||||||
|
}
|
||||||
|
|
||||||
|
// populated early for finalizers
|
||||||
|
if len(danglingFiles) > 0 {
|
||||||
|
return danglingFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
// this check must happen after everything else passes
|
||||||
if len(ctx.pendingIds) != 0 {
|
if len(ctx.pendingIds) != 0 {
|
||||||
return UnacknowledgedProxyError(slices.Collect(maps.Keys(ctx.pendingIds)))
|
return UnacknowledgedProxyError(slices.Collect(maps.Keys(ctx.pendingIds)))
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user