Fixing race in distributed cache tag helper

Fixes #4407
This commit is contained in:
Sébastien Ros 2016-04-06 09:42:24 -07:00
parent c66c408c15
commit efb8ef33e6
3 changed files with 13 additions and 13 deletions

View File

@ -95,7 +95,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers.Cache
var serializedKey = Encoding.UTF8.GetBytes(key.GenerateKey()); var serializedKey = Encoding.UTF8.GetBytes(key.GenerateKey());
var storageKey = key.GenerateHashedKey(); var storageKey = key.GenerateHashedKey();
var value = await _storage.GetAsync(storageKey); var value = await _storage.GetAsync(storageKey);
if (value == null) if (value == null)
{ {
// The value is not cached, we need to render the tag helper output // The value is not cached, we need to render the tag helper output
@ -126,7 +126,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers.Cache
// The value was found in the storage, decode and ensure // The value was found in the storage, decode and ensure
// there is no cache key hash collision // there is no cache key hash collision
byte[] decodedValue = Decode(value, serializedKey); byte[] decodedValue = Decode(value, serializedKey);
try try
{ {
if (decodedValue != null) if (decodedValue != null)
@ -147,21 +147,21 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers.Cache
} }
} }
} }
// Notify all other awaiters of the final content
tcs.TrySetResult(content);
} }
catch catch
{ {
// Notify all other awaiters to render the content content = null;
tcs.TrySetResult(null);
throw; throw;
} }
finally finally
{ {
// Remove the worker task from the in-memory cache // Remove the worker task before setting the result.
Task<IHtmlContent> worker; // If the result is null, other threads would potentially
_workers.TryRemove(key, out worker); // acquire it otherwise.
_workers.TryRemove(key, out result);
// Notify all other awaiters to render the content
tcs.TrySetResult(content);
} }
} }
else else

View File

@ -67,7 +67,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
IHtmlContent content = null; IHtmlContent content = null;
// Create a cancellation token that will be used // Create a cancellation token that will be used
// to release the task from the memory cache. // to release the task from the memory cache.
var tokenSource = new CancellationTokenSource(); var tokenSource = new CancellationTokenSource();
@ -109,6 +109,6 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
return options; return options;
} }
} }
} }

View File

@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
{ {
public class DistributedCacheTagHelperTest public class DistributedCacheTagHelperTest
{ {
[Fact] [Fact]
public async Task ProcessAsync_DoesNotCache_IfDisabled() public async Task ProcessAsync_DoesNotCache_IfDisabled()
{ {