aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/pkg
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-04-18 02:59:44 +0900
committerOphestra <cat@gensokyo.uk>2026-04-18 02:59:44 +0900
commit9e18de1dc2fb195872b8d0d39e2924b110c43054 (patch)
tree5177ca592b182b9920a654b45d1307ee9574d6bb /internal/pkg
parentb80ea91a42c994b1bcb74d8bcecefc150b899136 (diff)
internal/pkg: flush cached errors on abort
This avoids disabling the artifact until cache is reopened. The same has to be implemented for Cancel in a future change. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/pkg')
-rw-r--r--internal/pkg/pkg.go30
1 files changed, 25 insertions, 5 deletions
diff --git a/internal/pkg/pkg.go b/internal/pkg/pkg.go
index d7191907..36aa7a91 100644
--- a/internal/pkg/pkg.go
+++ b/internal/pkg/pkg.go
@@ -219,7 +219,7 @@ func (c *common) Open(a Artifact) (r io.ReadCloser, err error) {
}
var pathname *check.Absolute
- if pathname, _, err = c.cache.Cure(a); err != nil {
+ if pathname, _, err = c.cache.cure(a, true); err != nil {
return
}
@@ -601,6 +601,8 @@ type Cache struct {
identPending map[unique.Handle[ID]]*pendingCure
// Synchronises access to ident and corresponding filesystem entries.
identMu sync.RWMutex
+ // Synchronises entry into Abort and Cure.
+ abortMu sync.RWMutex
// Synchronises entry into exclusive artifacts for the cure method.
exclMu sync.Mutex
@@ -1036,6 +1038,9 @@ func (c *Cache) Scrub(checks int) error {
// loadOrStoreIdent attempts to load a cached [Artifact] by its identifier or
// wait for a pending [Artifact] to cure. If neither is possible, the current
// identifier is stored in identPending and a non-nil channel is returned.
+//
+// Since identErr is treated as grow-only, loadOrStoreIdent must not be entered
+// without holding a read lock on abortMu.
func (c *Cache) loadOrStoreIdent(id unique.Handle[ID]) (
ctx context.Context,
done chan<- struct{},
@@ -1069,6 +1074,7 @@ func (c *Cache) loadOrStoreIdent(id unique.Handle[ID]) (
d := make(chan struct{})
pending = &pendingCure{done: d}
ctx, pending.cancel = context.WithCancel(c.toplevel.Load().ctx)
+ c.wg.Add(1)
c.identPending[id] = pending
c.identMu.Unlock()
done = d
@@ -1091,6 +1097,7 @@ func (c *Cache) finaliseIdent(
}
delete(c.identPending, id)
c.identMu.Unlock()
+ c.wg.Done()
close(done)
}
@@ -1310,6 +1317,9 @@ func (c *Cache) Cure(a Artifact) (
checksum unique.Handle[Checksum],
err error,
) {
+ c.abortMu.RLock()
+ defer c.abortMu.RUnlock()
+
if err = c.toplevel.Load().ctx.Err(); err != nil {
return
}
@@ -1504,7 +1514,8 @@ func (r *RContext) NewMeasuredReader(
return r.cache.newMeasuredReader(rc, checksum)
}
-// cure implements Cure without checking the full dependency graph.
+// 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],
@@ -1871,8 +1882,8 @@ func (c *Cache) OpenStatus(a Artifact) (r io.ReadSeekCloser, err error) {
return
}
-// Abort cancels all pending cures but does not close the store. Abort does not
-// wait for cures to complete.
+// Abort cancels all pending cures and waits for them to clean up, but does not
+// close the cache.
func (c *Cache) Abort() {
c.closeMu.Lock()
defer c.closeMu.Unlock()
@@ -1881,7 +1892,16 @@ func (c *Cache) Abort() {
return
}
- c.toplevel.Swap(newToplevel(c.parent)).cancel()
+ c.toplevel.Load().cancel()
+ c.abortMu.Lock()
+ defer c.abortMu.Unlock()
+
+ // holding abortMu, identPending stays empty
+ c.wg.Wait()
+ c.identMu.Lock()
+ c.toplevel.Store(newToplevel(c.parent))
+ clear(c.identErr)
+ c.identMu.Unlock()
}
// Close cancels all pending cures and waits for them to clean up.