internal/pkg: loadavg target in container environment
Test / Create distribution (push) Successful in 56s
Test / Sandbox (push) Successful in 3m2s
Test / ShareFS (push) Successful in 3m58s
Test / Hakurei (push) Successful in 4m5s
Test / Sandbox (race detector) (push) Successful in 5m29s
Test / Hakurei (race detector) (push) Successful in 6m40s
Test / Flake checks (push) Successful in 1m4s

This exposes preferred loadavg target to the container initial process.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
2026-07-09 09:45:43 +09:00
parent 2f1534853a
commit 6cfd8fb934
3 changed files with 46 additions and 18 deletions
+17 -7
View File
@@ -30,10 +30,16 @@ import (
// AbsWork is the container pathname [TContext.GetWorkDir] is mounted on. // AbsWork is the container pathname [TContext.GetWorkDir] is mounted on.
var AbsWork = fhs.AbsRoot.Append("work/") var AbsWork = fhs.AbsRoot.Append("work/")
// EnvJobs is the name of the environment variable holding a decimal const (
// representation of the preferred job count. Its value must not affect cure // EnvJobs is the name of the environment variable holding a decimal
// outcome. // representation of the preferred job count. Its value must not affect cure
const EnvJobs = "CURE_JOBS" // 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 // ExecPath is a slice of [Artifact] and the [check.Absolute] pathname to make
// it available at under in the container. // it available at under in the container.
@@ -471,7 +477,7 @@ const SeccompPresets = std.PresetStrict &
func (a *execArtifact) makeContainer( func (a *execArtifact) makeContainer(
ctx context.Context, ctx context.Context,
msg message.Msg, msg message.Msg,
flags, jobs int, flags, jobs, load int,
hostNet bool, hostNet bool,
temp, work *check.Absolute, temp, work *check.Absolute,
getArtifact GetArtifactFunc, getArtifact GetArtifactFunc,
@@ -508,7 +514,10 @@ func (a *execArtifact) makeContainer(
z.Quiet = flags&CSuppressInit != 0 z.Quiet = flags&CSuppressInit != 0
z.Uid, z.Gid = (1<<10)-1, (1<<10)-1 z.Uid, z.Gid = (1<<10)-1, (1<<10)-1
z.Dir, z.Path, z.Args = a.dir, a.path, a.args 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) z.Grow(len(a.paths) + 4)
if a.arch != runtime.GOARCH { if a.arch != runtime.GOARCH {
@@ -651,6 +660,7 @@ func (c *Cache) EnterExec(
ctx, c.msg, ctx, c.msg,
c.attr.Flags, c.attr.Flags,
c.attr.Jobs, c.attr.Jobs,
c.attr.Load,
hostNet, hostNet,
temp, work, temp, work,
func(a Artifact) (*check.Absolute, unique.Handle[Checksum]) { 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() msg := f.GetMessage()
var z *container.Container var z *container.Container
if z, err = a.makeContainer( 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.GetTempDir(), f.GetWorkDir(),
f.GetArtifact, f.GetArtifact,
f.cache.Ident, f.cache.Ident,
+9 -1
View File
@@ -38,7 +38,15 @@ func main() {
} }
environ := slices.DeleteFunc(slices.Clone(os.Environ()), func(s string) bool { 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 var hostNet, layers, promote bool
+20 -10
View File
@@ -281,6 +281,10 @@ func (c *common) GetMessage() message.Msg { return c.cache.msg }
// value must not affect cure outcome. // value must not affect cure outcome.
func (c *common) GetJobs() int { return c.cache.attr.Jobs } 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 // 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] // 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. // and this pathname should not be directly referred to in the final contents.
@@ -2684,6 +2688,8 @@ type CacheAttr struct {
Flags int Flags int
// Preferred job count, when applicable. // Preferred job count, when applicable.
Jobs int Jobs int
// Preferred loadavg target, when applicable.
Load int
// Omit the [lockedfile] lock. // Omit the [lockedfile] lock.
skipLock bool skipLock bool
@@ -2716,15 +2722,19 @@ func Open(
panic("attempting to open cache with incomplete variant setup") panic("attempting to open cache with incomplete variant setup")
} }
if attr == nil { var a CacheAttr
attr = new(CacheAttr) if attr != nil {
a = *attr
} }
if attr.Cures < 1 { if a.Cures < 1 {
attr.Cures = runtime.NumCPU() a.Cures = runtime.NumCPU()
} }
if attr.Jobs < 1 { if a.Jobs < 1 {
attr.Jobs = runtime.NumCPU() a.Jobs = runtime.NumCPU()
}
if a.Load < 1 {
a.Load = runtime.NumCPU() + 2
} }
for _, name := range []string{ for _, name := range []string{
@@ -2746,8 +2756,8 @@ func Open(
c := Cache{ c := Cache{
parent: ctx, parent: ctx,
cures: make(chan struct{}, attr.Cures), cures: make(chan struct{}, a.Cures),
attr: *attr, attr: a,
msg: msg, msg: msg,
base: base, base: base,
@@ -2764,7 +2774,7 @@ func Open(
} }
c.toplevel.Store(newToplevel(ctx)) c.toplevel.Store(newToplevel(ctx))
if !attr.skipLock || !testing.Testing() { if !a.skipLock || !testing.Testing() {
if unlock, err := lockedfile.MutexAt( if unlock, err := lockedfile.MutexAt(
base.Append(fileLock).String(), base.Append(fileLock).String(),
).Lock(); err != nil { ).Lock(); err != nil {
@@ -2825,7 +2835,7 @@ func Open(
} }
} else if s := string(p); s == "" { } else if s := string(p); s == "" {
if extension != "" { if extension != "" {
if attr.Flags&CPromoteVariant == 0 { if a.Flags&CPromoteVariant == 0 {
c.unlock() c.unlock()
return nil, ErrWouldPromote return nil, ErrWouldPromote
} }