From 3c6e9b3d0526a9338dc897a8e288647b2ec12b10 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Wed, 8 Jul 2026 14:19:17 +0900 Subject: internal/pkg: expose cure whence Useful for cure determinism validation of KindExec. Signed-off-by: Ophestra --- internal/pkg/pkg.go | 69 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 7 deletions(-) (limited to 'internal/pkg/pkg.go') diff --git a/internal/pkg/pkg.go b/internal/pkg/pkg.go index aadfa308..9d32231d 100644 --- a/internal/pkg/pkg.go +++ b/internal/pkg/pkg.go @@ -303,7 +303,7 @@ func (c *common) Open(a Artifact) (r io.ReadCloser, err error) { } var pathname *check.Absolute - if pathname, _, err = c.cache.cure(a, true); err != nil { + if pathname, _, _, err = c.cache.cure(a, true); err != nil { return } @@ -356,12 +356,12 @@ func (f *FContext) linkSubstitute(ids, substitutes string) (err error) { return } -// InvalidLookupError is the identifier of non-dependency [Artifact] looked up +// InvalidLookupError is the identifier of non-input [Artifact] looked up // via [FContext.GetArtifact] by a misbehaving [Artifact] implementation. type InvalidLookupError ID func (e InvalidLookupError) Error() string { - return "attempting to look up non-dependency artifact " + Encode(e) + return "attempting to look up non-input artifact " + Encode(e) } var _ error = InvalidLookupError{} @@ -602,7 +602,7 @@ type cureRes struct { checksum unique.Handle[Checksum] } -// A pendingArtifactDep is a dependency [Artifact] pending concurrent curing, +// A pendingArtifactDep is an input [Artifact] pending concurrent curing, // subject to the cures limit. Values pointed to by result addresses are safe // to access after the [sync.WaitGroup] associated with this pendingArtifactDep // is done. pendingArtifactDep must not be reused or modified after it is sent @@ -676,7 +676,7 @@ const ( // suppressed regardless of [message.Msg] state. CSuppressInit - // CIgnoreSubstitutes disables content-based dependency substitution. + // CIgnoreSubstitutes disables content-based input substitution. CIgnoreSubstitutes // CExternShallow arranges for only non-flood inputs to be fetched when @@ -733,7 +733,7 @@ type Cache struct { parent context.Context // For deriving curing context, must not be accessed directly. toplevel atomic.Pointer[toplevel] - // For waiting on dependency curing goroutines. + // For waiting on input curing goroutines. wg sync.WaitGroup // Reports new cures and passed to [Artifact]. msg message.Msg @@ -1633,6 +1633,24 @@ func (c *Cache) Cure(a Artifact) ( return } + pathname, checksum, _, err = c.cure(a, true) + return +} + +// CureWhence is like Cure, but returns the whence value. +func (c *Cache) CureWhence(a Artifact) ( + pathname *check.Absolute, + checksum unique.Handle[Checksum], + whence int, + err error, +) { + c.abortMu.RLock() + defer c.abortMu.RUnlock() + + if err = c.toplevel.Load().ctx.Err(); err != nil { + return + } + return c.cure(a, true) } @@ -1971,11 +1989,41 @@ func (e HangingInputError) Error() string { return Encode(unique.Handle[ID](e).Value()) + " is unavailable" } +const ( + // WNew indicates a cure entering the implementation. + WNew = iota + // WCache indicates an [Artifact] present in the cache. + WCache + // WSubstitute indicates an [Artifact] hitting a content-based input + // substitution. + WSubstitute + // WExternal indicates a cure entering the external cache. + WExternal +) + +// WhenceString returns a printable string for a whence value. +func WhenceString(whence int) string { + switch whence { + case WNew: + return "new" + case WCache: + return "cache" + case WSubstitute: + return "substitute" + case WExternal: + return "external" + + default: + return "invalid whence " + strconv.Itoa(whence) + } +} + // cure implements Cure without acquiring a read lock on abortMu. cure must not // be entered during Abort. func (c *Cache) cure(a Artifact, curesExempt bool) ( pathname *check.Absolute, checksum unique.Handle[Checksum], + whence int, err error, ) { id := c.Ident(a) @@ -2001,6 +2049,7 @@ func (c *Cache) cure(a Artifact, curesExempt bool) ( ) ctx, done, checksum, err = c.loadOrStoreIdent(id) if done == nil { + whence = WCache return } else { defer func() { c.finaliseIdent(done, id, checksum, err) }() @@ -2008,6 +2057,7 @@ func (c *Cache) cure(a Artifact, curesExempt bool) ( checksum, err = c.tryChecksum(pathname) if err == nil || !errors.Is(err, os.ErrNotExist) { + whence = WCache return } @@ -2077,6 +2127,7 @@ func (c *Cache) cure(a Artifact, curesExempt bool) ( } } + whence = WNew if c.msg.IsVerbose() { rn := reportName(a, id) c.msg.Verbosef("curing %s...", rn) @@ -2095,6 +2146,7 @@ func (c *Cache) cure(a Artifact, curesExempt bool) ( // cure FileArtifact outside type switch to skip TContext initialisation if f, ok := a.(FileArtifact); ok { if checksumFi != nil { + whence = WCache if !checksumFi.Mode().IsRegular() { // unreachable err = InvalidFileModeError(checksumFi.Mode()) @@ -2199,6 +2251,7 @@ func (c *Cache) cure(a Artifact, curesExempt bool) ( } if checksumFi != nil { + whence = WCache if !checksumFi.Mode().IsDir() { // unreachable err = InvalidFileModeError(checksumFi.Mode()) @@ -2308,6 +2361,7 @@ func (c *Cache) cure(a Artifact, curesExempt bool) ( return } if substituteChecksum != zeroChecksum { + whence = WSubstitute checksum = substituteChecksum checksums = Encode(checksum.Value()) checksumPathname = c.base.Append( @@ -2332,6 +2386,7 @@ func (c *Cache) cure(a Artifact, curesExempt bool) ( defer f.destroy(&err) if extern { + whence = WExternal if checksum != zeroChecksum && externChecksum != checksum { err = &ChecksumMismatchError{externChecksum.Value(), checksum.Value()} if c.msg.IsVerbose() { @@ -2471,7 +2526,7 @@ func (pending *pendingArtifactDep) cure(c *Cache) { defer pending.Done() var err error - pending.resP.pathname, pending.resP.checksum, err = c.cure(pending.a, false) + pending.resP.pathname, pending.resP.checksum, _, err = c.cure(pending.a, false) if err == nil { return } -- cgit v1.3.1