aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/pkg/exec.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-01-09 01:51:39 +0900
committerOphestra <cat@gensokyo.uk>2026-01-09 05:20:34 +0900
commitf7124667145d1f7c614d9fc644bc07bb6174454a (patch)
tree9585573da6d016a27ce7fd066b795f611d1ce877 /internal/pkg/exec.go
parentf2430b5f5ef7ac0361d1c4976b7ec7bebaa15f63 (diff)
internal/pkg: move dependency flooding to cache
This imposes a hard upper limit to concurrency during dependency satisfaction and moves all dependency-related code out of individual implementations of Artifact. This change also includes ctx and msg as part of Cache. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/pkg/exec.go')
-rw-r--r--internal/pkg/exec.go142
1 files changed, 27 insertions, 115 deletions
diff --git a/internal/pkg/exec.go b/internal/pkg/exec.go
index e3b6b437..db0cf9f0 100644
--- a/internal/pkg/exec.go
+++ b/internal/pkg/exec.go
@@ -5,7 +5,6 @@ import (
"context"
"errors"
"os"
- "runtime"
"slices"
"strconv"
"syscall"
@@ -15,7 +14,6 @@ import (
"hakurei.app/container/check"
"hakurei.app/container/fhs"
"hakurei.app/container/std"
- "hakurei.app/message"
)
// AbsWork is the container pathname [CureContext.GetWorkDir] is mounted on.
@@ -30,7 +28,7 @@ type ExecPath struct {
// If there are multiple entries or W is true, P is set up as an overlay
// mount, and entries of A must not implement [File].
A []Artifact
- // Whether to make the mount point writable via an invisible tmpfs upperdir.
+ // Whether to make the mount point writable via the temp directory.
W bool
}
@@ -50,17 +48,8 @@ func MustPath(pathname string, writable bool, a ...Artifact) ExecPath {
// Methods of execArtifact does not modify any struct field or underlying arrays
// referred to by slices.
type execArtifact struct {
- // Caller-supplied context.
- ctx context.Context
// Caller-supplied inner mount points.
paths []ExecPath
- // Caller-supplied logging facility, passed through to [container] and used
- // internally to produce verbose output.
- msg message.Msg
-
- // Number of [Artifact] to concurrently cure. A value of 0 or lower is
- // equivalent to the value returned by [runtime.NumCPU].
- cures int
// Passed through to [container.Params].
dir *check.Absolute
@@ -93,10 +82,10 @@ func (a *execNetArtifact) Params() []byte {
return slices.Concat(a.checksum[:], a.execArtifact.Params())
}
-// Cure cures the [Artifact] by curing all its dependencies then running the
-// container described by the caller. The container retains host networking.
-func (a *execNetArtifact) Cure(c *CureContext) error {
- return a.cure(c, true)
+// Cure cures the [Artifact] in the container described by the caller. The
+// container retains host networking.
+func (a *execNetArtifact) Cure(f *FContext) error {
+ return a.cure(f, true)
}
// NewExec returns a new [Artifact] bounded by ctx, it cures all [Artifact]
@@ -114,13 +103,7 @@ func (a *execNetArtifact) Cure(c *CureContext) error {
//
// If checksum is non-nil, the resulting [Artifact] implements [KnownChecksum]
// and its container runs in the host net namespace.
-//
-// A cures value of 0 or lower is equivalent to the value returned by
-// [runtime.NumCPU].
func NewExec(
- ctx context.Context,
- msg message.Msg,
- cures int,
checksum *Checksum,
dir *check.Absolute,
@@ -130,7 +113,7 @@ func NewExec(
paths ...ExecPath,
) Artifact {
- a := execArtifact{ctx, paths, msg, cures, dir, env, path, args}
+ a := execArtifact{paths, dir, env, path, args}
if checksum == nil {
return &a
}
@@ -186,10 +169,9 @@ func (a *execArtifact) Dependencies() []Artifact {
return slices.Concat(artifacts...)
}
-// Cure cures the [Artifact] by curing all its dependencies then running the
-// container described by the caller.
-func (a *execArtifact) Cure(c *CureContext) (err error) {
- return a.cure(c, false)
+// Cure cures the [Artifact] in the container described by the caller.
+func (a *execArtifact) Cure(f *FContext) (err error) {
+ return a.cure(f, false)
}
const (
@@ -199,20 +181,8 @@ const (
// cure is like Cure but allows optional host net namespace. This is used for
// the [KnownChecksum] variant where networking is allowed.
-func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) {
- cures := a.cures
- if cures < 1 {
- cures = runtime.NumCPU()
- }
-
+func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
overlayWorkIndex := -1
- type curePath struct {
- // Copied from ExecPath.P.
- dst *check.Absolute
- // Cured from ExecPath.A.
- src []*check.Absolute
- }
- paths := make([]curePath, len(a.paths))
for i, p := range a.paths {
if p.P == nil || len(p.A) == 0 {
return os.ErrInvalid
@@ -220,8 +190,6 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) {
if p.P.Is(AbsWork) {
overlayWorkIndex = i
}
- paths[i].dst = p.P
- paths[i].src = make([]*check.Absolute, len(p.A))
}
var artifactCount int
@@ -229,71 +197,10 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) {
artifactCount += len(p.A)
}
- if len(paths) > 0 {
- type cureArtifact struct {
- // Index of pending Artifact in paths.
- index [2]int
- // Pending artifact.
- a Artifact
- }
- ac := make(chan cureArtifact, artifactCount)
- for i, p := range a.paths {
- for j, d := range p.A {
- ac <- cureArtifact{[2]int{i, j}, d}
- }
- }
-
- type cureRes struct {
- // Index of result in paths.
- index [2]int
- // Cured pathname.
- pathname *check.Absolute
- // Error returned by c.
- err error
- }
- res := make(chan cureRes)
-
- for i := 0; i < cures; i++ {
- go func() {
- for d := range ac {
- // computing and encoding identifier is expensive
- if a.msg.IsVerbose() {
- a.msg.Verbosef("curing %s...", Encode(Ident(d.a)))
- }
-
- var cr cureRes
- cr.index = d.index
- cr.pathname, _, cr.err = c.Cure(d.a)
- res <- cr
- }
- }()
- }
-
- var count int
- errs := make([]error, 0, artifactCount)
- for cr := range res {
- count++
-
- if cr.err != nil {
- errs = append(errs, cr.err)
- } else {
- paths[cr.index[0]].src[cr.index[1]] = cr.pathname
- }
-
- if count == artifactCount {
- break
- }
- }
- close(ac)
- if err = errors.Join(errs...); err != nil {
- return
- }
- }
-
- ctx, cancel := context.WithCancel(a.ctx)
+ ctx, cancel := context.WithCancel(f.Unwrap())
defer cancel()
- z := container.New(ctx, a.msg)
+ z := container.New(ctx, f.GetMessage())
z.WaitDelay = execWaitDelay
z.SeccompPresets |= std.PresetStrict
z.ParentPerm = 0700
@@ -303,15 +210,20 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) {
z.Hostname = "cure-net"
}
z.Uid, z.Gid = (1<<10)-1, (1<<10)-1
- if a.msg.IsVerbose() {
+ if f.GetMessage().IsVerbose() {
z.Stdout, z.Stderr = os.Stdout, os.Stderr
}
z.Dir, z.Env, z.Path, z.Args = a.dir, a.env, a.path, a.args
- z.Grow(len(paths) + 4)
+ z.Grow(len(a.paths) + 4)
+
+ temp, work := f.GetTempDir(), f.GetWorkDir()
+ for i, b := range a.paths {
+ layers := make([]*check.Absolute, len(b.A))
+ for j, d := range b.A {
+ layers[j] = f.Pathname(d)
+ }
- temp, work := c.GetTempDir(), c.GetWorkDir()
- for i, b := range paths {
if i == overlayWorkIndex {
if err = os.MkdirAll(work.String(), 0700); err != nil {
return
@@ -324,7 +236,7 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) {
AbsWork,
work,
tempWork,
- b.src...,
+ layers...,
)
continue
}
@@ -341,11 +253,11 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) {
if err = os.MkdirAll(tempWork.String(), 0700); err != nil {
return
}
- z.Overlay(b.dst, tempUpper, tempWork, b.src...)
- } else if len(b.src) == 1 {
- z.Bind(b.src[0], b.dst, 0)
+ z.Overlay(b.P, tempUpper, tempWork, layers...)
+ } else if len(layers) == 1 {
+ z.Bind(layers[0], b.P, 0)
} else {
- z.OverlayReadonly(b.dst, b.src...)
+ z.OverlayReadonly(b.P, layers...)
}
}
if overlayWorkIndex < 0 {
@@ -356,7 +268,7 @@ func (a *execArtifact) cure(c *CureContext, hostNet bool) (err error) {
)
}
z.Bind(
- c.GetTempDir(),
+ f.GetTempDir(),
fhs.AbsTmp,
std.BindWritable|std.BindEnsure,
)