diff --git a/vfs/vfscache/cache.go b/vfs/vfscache/cache.go index eaa17b8a0..ca79f228b 100644 --- a/vfs/vfscache/cache.go +++ b/vfs/vfscache/cache.go @@ -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. */ diff --git a/vfs/vfscache/cache_test.go b/vfs/vfscache/cache_test.go index a86cb3c50..092999b27 100644 --- a/vfs/vfscache/cache_test.go +++ b/vfs/vfscache/cache_test.go @@ -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)