diff --git a/internal/pkg/exec.go b/internal/pkg/exec.go index d9063c74..c7d8c30d 100644 --- a/internal/pkg/exec.go +++ b/internal/pkg/exec.go @@ -30,10 +30,16 @@ import ( // AbsWork is the container pathname [TContext.GetWorkDir] is mounted on. var AbsWork = fhs.AbsRoot.Append("work/") -// EnvJobs is the name of the environment variable holding a decimal -// representation of the preferred job count. Its value must not affect cure -// outcome. -const EnvJobs = "CURE_JOBS" +const ( + // EnvJobs is the name of the environment variable holding a decimal + // representation of the preferred job count. Its value must not affect cure + // outcome. + EnvJobs = "CURE_JOBS" + // EnvLoad is the name of the environment variable holding a decimal + // representation of the preferred loadavg target. Its value must not affect + // cure outcome. + EnvLoad = "CURE_LOAD" +) // ExecPath is a slice of [Artifact] and the [check.Absolute] pathname to make // it available at under in the container. @@ -471,7 +477,7 @@ const SeccompPresets = std.PresetStrict & func (a *execArtifact) makeContainer( ctx context.Context, msg message.Msg, - flags, jobs int, + flags, jobs, load int, hostNet bool, temp, work *check.Absolute, getArtifact GetArtifactFunc, @@ -508,7 +514,10 @@ func (a *execArtifact) makeContainer( z.Quiet = flags&CSuppressInit != 0 z.Uid, z.Gid = (1<<10)-1, (1<<10)-1 z.Dir, z.Path, z.Args = a.dir, a.path, a.args - z.Env = slices.Concat(a.env, []string{EnvJobs + "=" + strconv.Itoa(jobs)}) + z.Env = slices.Concat(a.env, []string{ + EnvJobs + "=" + strconv.Itoa(jobs), + EnvLoad + "=" + strconv.Itoa(load), + }) z.Grow(len(a.paths) + 4) if a.arch != runtime.GOARCH { @@ -651,6 +660,7 @@ func (c *Cache) EnterExec( ctx, c.msg, c.attr.Flags, c.attr.Jobs, + c.attr.Load, hostNet, temp, work, func(a Artifact) (*check.Absolute, unique.Handle[Checksum]) { @@ -693,7 +703,7 @@ func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) { msg := f.GetMessage() var z *container.Container if z, err = a.makeContainer( - ctx, msg, f.cache.attr.Flags, f.GetJobs(), hostNet, + ctx, msg, f.cache.attr.Flags, f.GetJobs(), f.GetLoad(), hostNet, f.GetTempDir(), f.GetWorkDir(), f.GetArtifact, f.cache.Ident, diff --git a/internal/pkg/internal/testtool/main.go b/internal/pkg/internal/testtool/main.go index cc8f9e49..96a85058 100644 --- a/internal/pkg/internal/testtool/main.go +++ b/internal/pkg/internal/testtool/main.go @@ -38,7 +38,15 @@ func main() { } environ := slices.DeleteFunc(slices.Clone(os.Environ()), func(s string) bool { - return s == "CURE_JOBS="+strconv.Itoa(runtime.NumCPU()) + for _, t := range []string{ + "CURE_JOBS=" + strconv.Itoa(runtime.NumCPU()), + "CURE_LOAD=" + strconv.Itoa(runtime.NumCPU()+2), + } { + if s == t { + return true + } + } + return false }) var hostNet, layers, promote bool diff --git a/internal/pkg/pkg.go b/internal/pkg/pkg.go index ea493d39..99a54462 100644 --- a/internal/pkg/pkg.go +++ b/internal/pkg/pkg.go @@ -281,6 +281,10 @@ func (c *common) GetMessage() message.Msg { return c.cache.msg } // value must not affect cure outcome. func (c *common) GetJobs() int { return c.cache.attr.Jobs } +// GetLoad returns the preferred load average target, when applicable. Its +// value must not affect cure outcome. +func (c *common) GetLoad() int { return c.cache.attr.Load } + // GetWorkDir returns a pathname to a directory which [Artifact] is expected to // write its output to. This is not the final resting place of the [Artifact] // and this pathname should not be directly referred to in the final contents. @@ -2684,6 +2688,8 @@ type CacheAttr struct { Flags int // Preferred job count, when applicable. Jobs int + // Preferred loadavg target, when applicable. + Load int // Omit the [lockedfile] lock. skipLock bool @@ -2716,15 +2722,19 @@ func Open( panic("attempting to open cache with incomplete variant setup") } - if attr == nil { - attr = new(CacheAttr) + var a CacheAttr + if attr != nil { + a = *attr } - if attr.Cures < 1 { - attr.Cures = runtime.NumCPU() + if a.Cures < 1 { + a.Cures = runtime.NumCPU() } - if attr.Jobs < 1 { - attr.Jobs = runtime.NumCPU() + if a.Jobs < 1 { + a.Jobs = runtime.NumCPU() + } + if a.Load < 1 { + a.Load = runtime.NumCPU() + 2 } for _, name := range []string{ @@ -2746,8 +2756,8 @@ func Open( c := Cache{ parent: ctx, - cures: make(chan struct{}, attr.Cures), - attr: *attr, + cures: make(chan struct{}, a.Cures), + attr: a, msg: msg, base: base, @@ -2764,7 +2774,7 @@ func Open( } c.toplevel.Store(newToplevel(ctx)) - if !attr.skipLock || !testing.Testing() { + if !a.skipLock || !testing.Testing() { if unlock, err := lockedfile.MutexAt( base.Append(fileLock).String(), ).Lock(); err != nil { @@ -2825,7 +2835,7 @@ func Open( } } else if s := string(p); s == "" { if extension != "" { - if attr.Flags&CPromoteVariant == 0 { + if a.Flags&CPromoteVariant == 0 { c.unlock() return nil, ErrWouldPromote }