Refactor hybrid accessors for async

This commit is contained in:
Stephan
2016-09-08 17:07:45 +02:00
parent 0597eea72b
commit 9fb9472193
4 changed files with 37 additions and 12 deletions

View File

@@ -1,16 +1,41 @@
using System;
using System.Runtime.Remoting.Messaging;
namespace Umbraco.Web
{
// fixme - must ensure that the ThreadStatic value is properly cleared!
internal abstract class HybridAccessorBase<T>
{
private readonly IHttpContextAccessor _httpContextAccessor;
protected abstract string HttpContextItemKey { get; }
protected abstract string ItemKey { get; }
[ThreadStatic]
private static T _value;
// read
// http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html
// http://stackoverflow.com/questions/14176028/why-does-logicalcallcontext-not-work-with-async
// http://stackoverflow.com/questions/854976/will-values-in-my-threadstatic-variables-still-be-there-when-cycled-via-threadpo
// https://msdn.microsoft.com/en-us/library/dd642243.aspx?f=255&MSPPError=-2147217396 ThreadLocal<T>
// http://stackoverflow.com/questions/29001266/cleaning-up-callcontext-in-tpl clearing call context
//
// anything that is ThreadStatic will stay with the thread and NOT flow in async threads
// the only thing that flows is the logical call context (safe in 4.5+)
// now...
// tests seem to show that either newing Thread or enqueuing in the ThreadPool both produce a thread
// with a clear logical call context, which would mean that it is somewhat safe to "fire and forget"
// because whatever is in the call context will be gone when the thread returns to the pool
// no!
//[ThreadStatic]
//private static T _value;
// yes! flows with async!
private T NonContextValue
{
get { return (T) CallContext.LogicalGetData(ItemKey); }
set
{
if (value == null) CallContext.FreeNamedDataSlot(ItemKey);
else CallContext.LogicalSetData(ItemKey, value);
}
}
protected HybridAccessorBase(IHttpContextAccessor httpContextAccessor)
{
@@ -22,8 +47,8 @@ namespace Umbraco.Web
get
{
var httpContext = _httpContextAccessor.HttpContext;
if (httpContext == null) return _value;
return (T) httpContext.Items[HttpContextItemKey];
if (httpContext == null) return NonContextValue;
return (T) httpContext.Items[ItemKey];
}
set
@@ -31,10 +56,10 @@ namespace Umbraco.Web
var httpContext = _httpContextAccessor.HttpContext;
if (httpContext == null)
{
_value = value;
NonContextValue = value;
return;
}
httpContext.Items[HttpContextItemKey] = value;
httpContext.Items[ItemKey] = value;
}
}
}

View File

@@ -4,7 +4,7 @@ namespace Umbraco.Web
{
internal class HybridEventMessagesAccessor : HybridAccessorBase<EventMessages>, IEventMessagesAccessor
{
protected override string HttpContextItemKey => "Umbraco.Core.Events.EventMessages";
protected override string ItemKey => "Umbraco.Core.Events.EventMessages";
public HybridEventMessagesAccessor(IHttpContextAccessor httpContextAccessor)
: base(httpContextAccessor)

View File

@@ -2,7 +2,7 @@
{
internal class HybridUmbracoContextAccessor : HybridAccessorBase<UmbracoContext>, IUmbracoContextAccessor
{
protected override string HttpContextItemKey => "Umbraco.Web.UmbracoContext";
protected override string ItemKey => "Umbraco.Web.UmbracoContext";
public HybridUmbracoContextAccessor(IHttpContextAccessor httpContextAccessor)
: base(httpContextAccessor)

View File

@@ -4,7 +4,7 @@ namespace Umbraco.Web
{
internal class HybridUmbracoDatabaseAccessor : HybridAccessorBase<UmbracoDatabase>, IUmbracoDatabaseAccessor
{
protected override string HttpContextItemKey => "Umbraco.Core.Persistence.UmbracoDatabase";
protected override string ItemKey => "Umbraco.Core.Persistence.UmbracoDatabase";
public HybridUmbracoDatabaseAccessor(IHttpContextAccessor httpContextAccessor)
: base(httpContextAccessor)