aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/pkg/pkg.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/pkg/pkg.go')
-rw-r--r--internal/pkg/pkg.go51
1 files changed, 41 insertions, 10 deletions
diff --git a/internal/pkg/pkg.go b/internal/pkg/pkg.go
index b3b08510..15114fd7 100644
--- a/internal/pkg/pkg.go
+++ b/internal/pkg/pkg.go
@@ -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
}