Skip to content

Commit 35e71ff

Browse files
committed
Wait 10 or more seconds before flushing last accessed date changes to disk in the disk cache - this drastically reduces race conditions where a SetLastAccessTimeUtc call prevents a cached file from being read.
1 parent 3793200 commit 35e71ff

File tree

1 file changed

+42
-5
lines changed

1 file changed

+42
-5
lines changed

src/Imazen.DiskCache/Cleanup/CleanupManager.cs

+42-5
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,43 @@
33
using System.Collections.Generic;
44
using System.Text;
55
using System.Threading;
6+
using System.Threading.Tasks;
67
using Imazen.Common.Extensibility.ClassicDiskCache;
78
using Imazen.Common.Issues;
89
using Microsoft.Extensions.Logging;
910

1011
namespace Imazen.DiskCache {
1112

12-
13+
internal static class TaskExtensions
14+
{
15+
// Allocate the async/await state machine only when needed for performance reason.
16+
// More info about the state machine: https://blogs.msdn.microsoft.com/seteplia/2017/11/30/dissecting-the-async-methods-in-c/
17+
static async Task ForgetAwaited(Task task)
18+
{
19+
try
20+
{
21+
// No need to resume on the original SynchronizationContext, so use ConfigureAwait(false)
22+
await task.ConfigureAwait(false);
23+
}
24+
catch
25+
{
26+
// Nothing to do here
27+
}
28+
}
29+
public static void Forget(this Task task)
30+
{
31+
// note: this code is inspired by a tweet from Ben Adams: https://twitter.com/ben_a_adams/status/1045060828700037125
32+
// Only care about tasks that may fault (not completed) or are faulted,
33+
// so fast-path for SuccessfullyCompleted and Canceled tasks.
34+
if (!task.IsCompleted || task.IsFaulted)
35+
{
36+
// use "_" (Discard operation) to remove the warning IDE0058: Because this call is not awaited, execution of the current method continues before the call is completed
37+
// https://docs.microsoft.com/en-us/dotnet/csharp/discards#a-standalone-discard
38+
_ = ForgetAwaited(task);
39+
}
40+
}
41+
}
42+
1343
internal class CleanupManager:IIssueProvider, IDisposable {
1444
private readonly ICleanableCache cache = null;
1545
private readonly CleanupStrategy cs = null;
@@ -81,10 +111,17 @@ public void CleanAll() {
81111
public void UsedFile(string relativePath, string physicalPath) {
82112
//Bump the date in memory
83113
cache.Index.bumpDateIfExists(relativePath);
84-
//Make sure the 'flush' job for the file is in the queue somewhere, so the access date will get written to disk.
85-
queue.QueueIfUnique(new CleanupWorkItem(CleanupWorkItem.Kind.FlushAccessedDate, relativePath, physicalPath));
86-
//In case it's paused
87-
worker.MayHaveWork();
114+
115+
Task.Run(async () =>
116+
{
117+
// Wait 10 seconds to give the reader thread time to open the file first.
118+
await Task.Delay(10000);
119+
//Make sure the 'flush' job for the file is in the queue somewhere, so the access date will get written to disk.
120+
queue.QueueIfUnique(new CleanupWorkItem(CleanupWorkItem.Kind.FlushAccessedDate, relativePath,
121+
physicalPath));
122+
//In case it's paused
123+
worker.MayHaveWork();
124+
}).Forget();
88125
}
89126

90127

0 commit comments

Comments
 (0)