internal/pkg: exclusive artifacts
All checks were successful
Test / Create distribution (push) Successful in 50s
Test / Sandbox (push) Successful in 2m34s
Test / Hakurei (push) Successful in 3m46s
Test / ShareFS (push) Successful in 3m59s
Test / Hpkg (push) Successful in 4m32s
Test / Sandbox (race detector) (push) Successful in 5m0s
Test / Hakurei (race detector) (push) Successful in 6m8s
Test / Flake checks (push) Successful in 1m36s
All checks were successful
Test / Create distribution (push) Successful in 50s
Test / Sandbox (push) Successful in 2m34s
Test / Hakurei (push) Successful in 3m46s
Test / ShareFS (push) Successful in 3m59s
Test / Hpkg (push) Successful in 4m32s
Test / Sandbox (race detector) (push) Successful in 5m0s
Test / Hakurei (race detector) (push) Successful in 6m8s
Test / Flake checks (push) Successful in 1m36s
This alleviates scheduler overhead when curing many artifacts. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
@@ -253,6 +253,24 @@ type Artifact interface {
|
||||
//
|
||||
// Result must remain identical across multiple invocations.
|
||||
Dependencies() []Artifact
|
||||
|
||||
// IsExclusive returns whether the [Artifact] is exclusive. Exclusive
|
||||
// artifacts might not run in parallel with each other, and are still
|
||||
// subject to the cures limit.
|
||||
//
|
||||
// Some implementations may saturate the CPU for a nontrivial amount of
|
||||
// time. Curing multiple such implementations simultaneously causes
|
||||
// significant CPU scheduler overhead. An exclusive artifact will generally
|
||||
// not be cured alongside another exclusive artifact, thus alleviating this
|
||||
// overhead.
|
||||
//
|
||||
// Note that [Cache] reserves the right to still cure exclusive
|
||||
// artifacts concurrently as this is not a synchronisation primitive but
|
||||
// an optimisation one. Implementations are forbidden from accessing global
|
||||
// state regardless of exclusivity.
|
||||
//
|
||||
// Result must remain identical across multiple invocations.
|
||||
IsExclusive() bool
|
||||
}
|
||||
|
||||
// FloodArtifact refers to an [Artifact] requiring its entire dependency graph
|
||||
@@ -472,6 +490,8 @@ type Cache struct {
|
||||
// Synchronises access to ident and corresponding filesystem entries.
|
||||
identMu sync.RWMutex
|
||||
|
||||
// Synchronises entry into exclusive artifacts for the cure method.
|
||||
exclMu sync.Mutex
|
||||
// Buffered I/O free list, must not be accessed directly.
|
||||
bufioPool sync.Pool
|
||||
|
||||
@@ -1215,7 +1235,10 @@ func (e *DependencyCureError) Error() string {
|
||||
}
|
||||
|
||||
// enterCure must be called before entering an [Artifact] implementation.
|
||||
func (c *Cache) enterCure(curesExempt bool) error {
|
||||
func (c *Cache) enterCure(a Artifact, curesExempt bool) error {
|
||||
if a.IsExclusive() {
|
||||
c.exclMu.Lock()
|
||||
}
|
||||
if curesExempt {
|
||||
return nil
|
||||
}
|
||||
@@ -1225,15 +1248,23 @@ func (c *Cache) enterCure(curesExempt bool) error {
|
||||
return nil
|
||||
|
||||
case <-c.ctx.Done():
|
||||
if a.IsExclusive() {
|
||||
c.exclMu.Unlock()
|
||||
}
|
||||
return c.ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
// exitCure must be called after exiting an [Artifact] implementation.
|
||||
func (c *Cache) exitCure(curesExempt bool) {
|
||||
if !curesExempt {
|
||||
<-c.cures
|
||||
func (c *Cache) exitCure(a Artifact, curesExempt bool) {
|
||||
if a.IsExclusive() {
|
||||
c.exclMu.Unlock()
|
||||
}
|
||||
if curesExempt {
|
||||
return
|
||||
}
|
||||
|
||||
<-c.cures
|
||||
}
|
||||
|
||||
// getWriter is like [bufio.NewWriter] but for bufioPool.
|
||||
@@ -1456,7 +1487,7 @@ func (c *Cache) cure(a Artifact, curesExempt bool) (
|
||||
}()
|
||||
|
||||
var r io.ReadCloser
|
||||
if err = c.enterCure(curesExempt); err != nil {
|
||||
if err = c.enterCure(a, curesExempt); err != nil {
|
||||
return
|
||||
}
|
||||
r, err = f.Cure(&RContext{c})
|
||||
@@ -1505,7 +1536,7 @@ func (c *Cache) cure(a Artifact, curesExempt bool) (
|
||||
err = closeErr
|
||||
}
|
||||
}
|
||||
c.exitCure(curesExempt)
|
||||
c.exitCure(a, curesExempt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -1539,11 +1570,11 @@ func (c *Cache) cure(a Artifact, curesExempt bool) (
|
||||
switch ca := a.(type) {
|
||||
case TrivialArtifact:
|
||||
defer t.destroy(&err)
|
||||
if err = c.enterCure(curesExempt); err != nil {
|
||||
if err = c.enterCure(a, curesExempt); err != nil {
|
||||
return
|
||||
}
|
||||
err = ca.Cure(&t)
|
||||
c.exitCure(curesExempt)
|
||||
c.exitCure(a, curesExempt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -1573,11 +1604,11 @@ func (c *Cache) cure(a Artifact, curesExempt bool) (
|
||||
}
|
||||
|
||||
defer f.destroy(&err)
|
||||
if err = c.enterCure(curesExempt); err != nil {
|
||||
if err = c.enterCure(a, curesExempt); err != nil {
|
||||
return
|
||||
}
|
||||
err = ca.Cure(&f)
|
||||
c.exitCure(curesExempt)
|
||||
c.exitCure(a, curesExempt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user