diff options
| author | Ophestra <cat@gensokyo.uk> | 2025-11-12 23:12:38 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2025-11-12 23:16:13 +0900 |
| commit | f347d44c2281f507e3786aa498f634bfa4c92d84 (patch) | |
| tree | fe85b41d5f6a5e308b87585f46efd64f90221e2b /helper/proc | |
| parent | b5630f6883c874d458d9ae3c666cfb7ae5e3121d (diff) | |
internal/helper: relocate from helper
This package is ugly and is pending removal only kept alive by xdg-dbus-proxy.
Its exported symbols are made available until v0.4.0 where it will be removed for #24.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'helper/proc')
| -rw-r--r-- | helper/proc/deprecated.go | 63 | ||||
| -rw-r--r-- | helper/proc/files.go | 162 | ||||
| -rw-r--r-- | helper/proc/pipe.go | 110 |
3 files changed, 63 insertions, 272 deletions
diff --git a/helper/proc/deprecated.go b/helper/proc/deprecated.go new file mode 100644 index 00000000..91897526 --- /dev/null +++ b/helper/proc/deprecated.go @@ -0,0 +1,63 @@ +// Deprecated: This package will be removed in 0.4. +package proc + +import ( + "context" + "io" + "os" + "os/exec" + "time" + _ "unsafe" // for go:linkname + + "hakurei.app/internal/helper/proc" +) + +//go:linkname FulfillmentTimeout hakurei.app/internal/helper/proc.FulfillmentTimeout +var FulfillmentTimeout time.Duration + +// A File is an extra file with deferred initialisation. +type File = proc.File + +// ExtraFilesPre is a linked list storing addresses of [os.File]. +type ExtraFilesPre = proc.ExtraFilesPre + +// Fulfill calls the [File.Fulfill] method on all files, starts cmd and blocks until all fulfillment completes. +// +//go:linkname Fulfill hakurei.app/internal/helper/proc.Fulfill +func Fulfill(ctx context.Context, + v *[]*os.File, start func() error, + files []File, extraFiles *ExtraFilesPre, +) (err error) + +// InitFile initialises f as part of the slice extraFiles points to, +// and returns its final fd value. +// +//go:linkname InitFile hakurei.app/internal/helper/proc.InitFile +func InitFile(f File, extraFiles *ExtraFilesPre) (fd uintptr) + +// BaseFile implements the Init method of the File interface and provides indirect access to extra file state. +type BaseFile = proc.BaseFile + +//go:linkname ExtraFile hakurei.app/internal/helper/proc.ExtraFile +func ExtraFile(cmd *exec.Cmd, f *os.File) (fd uintptr) + +//go:linkname ExtraFileSlice hakurei.app/internal/helper/proc.ExtraFileSlice +func ExtraFileSlice(extraFiles *[]*os.File, f *os.File) (fd uintptr) + +// NewWriterTo returns a [File] that receives content from wt on fulfillment. +// +//go:linkname NewWriterTo hakurei.app/internal/helper/proc.NewWriterTo +func NewWriterTo(wt io.WriterTo) File + +// NewStat returns a [File] implementing the behaviour +// of the receiving end of xdg-dbus-proxy stat fd. +// +//go:linkname NewStat hakurei.app/internal/helper/proc.NewStat +func NewStat(s *io.Closer) File + +var ( + //go:linkname ErrStatFault hakurei.app/internal/helper/proc.ErrStatFault + ErrStatFault error + //go:linkname ErrStatRead hakurei.app/internal/helper/proc.ErrStatRead + ErrStatRead error +) diff --git a/helper/proc/files.go b/helper/proc/files.go deleted file mode 100644 index 0612718e..00000000 --- a/helper/proc/files.go +++ /dev/null @@ -1,162 +0,0 @@ -package proc - -import ( - "context" - "os" - "os/exec" - "sync/atomic" - "syscall" - "testing" - "time" -) - -var FulfillmentTimeout = 2 * time.Second - -func init() { - if testing.Testing() { - FulfillmentTimeout *= 10 - } -} - -// A File is an extra file with deferred initialisation. -type File interface { - // Init initialises File state. Init must not be called more than once. - Init(fd uintptr, v **os.File) uintptr - // Fd returns the fd value set on initialisation. - Fd() uintptr - // ErrCount returns count of error values emitted during fulfillment. - ErrCount() int - // Fulfill is called prior to process creation and must populate its corresponding file address. - // Calls to dispatchErr must match the return value of ErrCount. - // Fulfill must not be called more than once. - Fulfill(ctx context.Context, dispatchErr func(error)) error -} - -// ExtraFilesPre is a linked list storing addresses of [os.File]. -type ExtraFilesPre struct { - n *ExtraFilesPre - v *os.File -} - -// Append grows the list by one entry and returns an address of the address of [os.File] stored in the new entry. -func (f *ExtraFilesPre) Append() (uintptr, **os.File) { return f.append(3) } - -// Files returns a slice pointing to a continuous segment of memory containing all addresses stored in f in order. -func (f *ExtraFilesPre) Files() []*os.File { return f.copy(make([]*os.File, 0, f.len())) } - -func (f *ExtraFilesPre) append(i uintptr) (uintptr, **os.File) { - if f.n == nil { - f.n = new(ExtraFilesPre) - return i, &f.v - } - return f.n.append(i + 1) -} -func (f *ExtraFilesPre) len() uintptr { - if f == nil { - return 0 - } - return f.n.len() + 1 -} -func (f *ExtraFilesPre) copy(e []*os.File) []*os.File { - if f == nil { - // the public methods ensure the first call is never nil; - // the last element is unused, slice it off here - return e[:len(e)-1] - } - return f.n.copy(append(e, f.v)) -} - -// Fulfill calls the [File.Fulfill] method on all files, starts cmd and blocks until all fulfillment completes. -func Fulfill(ctx context.Context, - v *[]*os.File, start func() error, - files []File, extraFiles *ExtraFilesPre, -) (err error) { - var ecs int - for _, o := range files { - ecs += o.ErrCount() - } - ec := make(chan error, ecs) - - c, cancel := context.WithTimeout(ctx, FulfillmentTimeout) - defer cancel() - - for _, f := range files { - err = f.Fulfill(c, makeDispatchErr(f, ec)) - if err != nil { - return - } - } - - *v = extraFiles.Files() - if err = start(); err != nil { - return - } - - for ecs > 0 { - select { - case err = <-ec: - ecs-- - if err != nil { - break - } - case <-ctx.Done(): - err = syscall.ECANCELED - break - } - } - return -} - -// InitFile initialises f as part of the slice extraFiles points to, -// and returns its final fd value. -func InitFile(f File, extraFiles *ExtraFilesPre) (fd uintptr) { return f.Init(extraFiles.Append()) } - -// BaseFile implements the Init method of the File interface and provides indirect access to extra file state. -type BaseFile struct { - fd uintptr - v **os.File -} - -func (f *BaseFile) Init(fd uintptr, v **os.File) uintptr { - if v == nil || fd < 3 { - panic("invalid extra file initial state") - } - if f.v != nil { - panic("extra file initialised twice") - } - f.fd, f.v = fd, v - return fd -} - -func (f *BaseFile) Fd() uintptr { - if f.v == nil { - panic("use of uninitialised extra file") - } - return f.fd -} - -func (f *BaseFile) Set(v *os.File) { - *f.v = v // runtime guards against use before init -} - -func makeDispatchErr(f File, ec chan<- error) func(error) { - c := new(atomic.Int32) - c.Store(int32(f.ErrCount())) - return func(err error) { - if c.Add(-1) < 0 { - panic("unexpected error dispatches") - } - ec <- err - } -} - -func ExtraFile(cmd *exec.Cmd, f *os.File) (fd uintptr) { - return ExtraFileSlice(&cmd.ExtraFiles, f) -} - -func ExtraFileSlice(extraFiles *[]*os.File, f *os.File) (fd uintptr) { - // ExtraFiles: If non-nil, entry i becomes file descriptor 3+i. - fd = uintptr(3 + len(*extraFiles)) - *extraFiles = append(*extraFiles, f) - return -} diff --git a/helper/proc/pipe.go b/helper/proc/pipe.go deleted file mode 100644 index 838c4ae4..00000000 --- a/helper/proc/pipe.go +++ /dev/null @@ -1,110 +0,0 @@ -package proc - -import ( - "context" - "errors" - "io" - "os" - "runtime" -) - -// NewWriterTo returns a [File] that receives content from wt on fulfillment. -func NewWriterTo(wt io.WriterTo) File { return &writeToFile{wt: wt} } - -// writeToFile exports the read end of a pipe with data written by an [io.WriterTo]. -type writeToFile struct { - wt io.WriterTo - BaseFile -} - -func (f *writeToFile) ErrCount() int { return 3 } -func (f *writeToFile) Fulfill(ctx context.Context, dispatchErr func(error)) error { - r, w, err := os.Pipe() - if err != nil { - return err - } - f.Set(r) - - done := make(chan struct{}) - go func() { - _, err = f.wt.WriteTo(w) - dispatchErr(err) - dispatchErr(w.Close()) - close(done) - runtime.KeepAlive(r) - }() - go func() { - select { - case <-done: - dispatchErr(nil) - case <-ctx.Done(): - dispatchErr(w.Close()) // this aborts WriteTo with file already closed - runtime.KeepAlive(r) - } - }() - - return nil -} - -// NewStat returns a [File] implementing the behaviour -// of the receiving end of xdg-dbus-proxy stat fd. -func NewStat(s *io.Closer) File { return &statFile{s: s} } - -var ( - ErrStatFault = errors.New("generic stat fd fault") - ErrStatRead = errors.New("unexpected stat behaviour") -) - -// statFile implements xdg-dbus-proxy stat fd behaviour. -type statFile struct { - s *io.Closer - BaseFile -} - -func (f *statFile) ErrCount() int { return 2 } -func (f *statFile) Fulfill(ctx context.Context, dispatchErr func(error)) error { - r, w, err := os.Pipe() - if err != nil { - return err - } - f.Set(w) - - done := make(chan struct{}) - go func() { - defer close(done) - var n int - - n, err = r.Read(make([]byte, 1)) - switch n { - case -1: - if err == nil { - err = ErrStatFault - } - dispatchErr(err) - case 0: - if err == nil { - err = ErrStatRead - } - dispatchErr(err) - case 1: - dispatchErr(err) - default: - panic("unreachable") - } - runtime.KeepAlive(w) - }() - - go func() { - select { - case <-done: - dispatchErr(nil) - case <-ctx.Done(): - dispatchErr(r.Close()) // this aborts Read with file already closed - runtime.KeepAlive(w) - } - }() - - // this gets closed by the caller - *f.s = r - return nil -} |
