vfs/vfscache: fix hang when the cache cleaner is disabled
KickCleaner sets the out of space flag, kicks the cleaner and then waits for that flag to clear. Only the cleaner clears it, and the cleaner returns immediately when the cache poll interval is not positive, so when it is disabled nothing ever reads the kick or clears the flag and the caller waits forever. It now returns straight away in that case, under the same condition the cleaner itself uses to decide it is disabled. Callers already retry a bounded number of times and then report the error, which is the right outcome when nothing is going to free space.
This commit is contained in:
committed by
Nick Craig-Wood
parent
ca41db095b
commit
e724790620
@@ -563,7 +563,15 @@ func (c *Cache) reload(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// KickCleaner kicks cache cleaner upon out of space situation
|
||||
//
|
||||
// This does nothing when the cleaner is disabled. Only the cleaner clears the
|
||||
// out of space condition, so with no cleaner running the wait below would never
|
||||
// return.
|
||||
func (c *Cache) KickCleaner() {
|
||||
if c.opt.CachePollInterval <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
/* Use a separate kicker mutex for the kick to go through without waiting for the
|
||||
cache mutex to avoid letting a thread kick again after the clearer just
|
||||
finished cleaning and unlock the cache mutex. */
|
||||
|
||||
@@ -655,6 +655,34 @@ func TestCacheCleaner(t *testing.T) {
|
||||
assert.False(t, found)
|
||||
}
|
||||
|
||||
func TestCacheKickCleaner(t *testing.T) {
|
||||
// kickCleaner runs KickCleaner and reports whether it returned in time.
|
||||
kickCleaner := func(t *testing.T, c *Cache) bool {
|
||||
t.Helper()
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
c.KickCleaner()
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
return true
|
||||
case <-time.After(2 * time.Second):
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Only the cleaner clears the out of space condition, so with no cleaner
|
||||
// running a KickCleaner which waited for it would never return.
|
||||
t.Run("CleanerDisabled", func(t *testing.T) {
|
||||
opt := vfscommon.Opt
|
||||
opt.CachePollInterval = 0
|
||||
_, c := newTestCacheOpt(t, opt)
|
||||
|
||||
assert.True(t, kickCleaner(t, c), "KickCleaner did not return with the cleaner disabled")
|
||||
})
|
||||
}
|
||||
|
||||
func TestCacheSetModTime(t *testing.T) {
|
||||
_, c := newTestCache(t)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user