Skip to content
This repository was archived by the owner on Nov 20, 2018. It is now read-only.

Streamlining feature cache and object re-initialiation code paths #516

Merged
merged 6 commits into from
Dec 31, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions samples/SampleApp/PooledHttpContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Internal;

namespace SampleApp
{
public class PooledHttpContext : DefaultHttpContext
{
DefaultHttpRequest _pooledHttpRequest;
DefaultHttpResponse _pooledHttpResponse;

public PooledHttpContext(IFeatureCollection featureCollection) :
base(featureCollection)
{
}

protected override HttpRequest InitializeHttpRequest()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to override all of methods in order to properly pool right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a policy choice, really - the ConnectionInfo/WebSocket objects might be used infrequently enough it could be could be more efficient to let them be garbage collected. But yes, you would override all child object management methods to take full control

{
if (_pooledHttpRequest != null)
{
_pooledHttpRequest.Initialize(this, Features);
return _pooledHttpRequest;
}

return new DefaultHttpRequest(this, Features);
}

protected override void UninitializeHttpRequest(HttpRequest instance)
{
_pooledHttpRequest = instance as DefaultHttpRequest;
_pooledHttpRequest?.Uninitialize();
}

protected override HttpResponse InitializeHttpResponse()
{
if (_pooledHttpResponse != null)
{
_pooledHttpResponse.Initialize(this, Features);
return _pooledHttpResponse;
}

return new DefaultHttpResponse(this, Features);
}

protected override void UninitializeHttpResponse(HttpResponse instance)
{
_pooledHttpResponse = instance as DefaultHttpResponse;
_pooledHttpResponse?.Uninitialize();
}
}
}
65 changes: 65 additions & 0 deletions samples/SampleApp/PooledHttpContextFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features;

namespace SampleApp
{
public class PooledHttpContextFactory : IHttpContextFactory
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly Stack<PooledHttpContext> _pool = new Stack<PooledHttpContext>();

public PooledHttpContextFactory(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}

public HttpContext Create(IFeatureCollection featureCollection)
{
PooledHttpContext httpContext = null;
lock (_pool)
{
if (_pool.Count != 0)
{
httpContext = _pool.Pop();
}
}

if (httpContext == null)
{
httpContext = new PooledHttpContext(featureCollection);
}
else
{
httpContext.Initialize(featureCollection);
}

if (_httpContextAccessor != null)
{
_httpContextAccessor.HttpContext = httpContext;
}
return httpContext;
}

public void Dispose(HttpContext httpContext)
{
if (_httpContextAccessor != null)
{
_httpContextAccessor.HttpContext = null;
}

var pooled = httpContext as PooledHttpContext;
if (pooled != null)
{
pooled.Uninitialize();
lock (_pool)
{
_pool.Push(pooled);
}
}
}
}
}
62 changes: 62 additions & 0 deletions src/Microsoft.AspNet.Http.Features/FeatureReferences.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;

namespace Microsoft.AspNet.Http.Features
{
public struct FeatureReferences<TCache>
{
public FeatureReferences(IFeatureCollection collection)
{
Collection = collection;
Cache = default(TCache);
Revision = collection.Revision;
}

public IFeatureCollection Collection { get; private set; }
public int Revision { get; private set; }

// cache is a public field because the code calling Fetch must
// be able to pass ref values that "dot through" the TCache struct memory,
// if it was a Property then that getter would return a copy of the memory
// preventing the use of "ref"
public TCache Cache;

public TFeature Fetch<TFeature, TState>(
ref TFeature cached,
TState state,
Func<TState, TFeature> factory)
{
var cleared = false;
if (Revision != Collection.Revision)
{
cleared = true;
Cache = default(TCache);
Revision = Collection.Revision;
}

var feature = cached;
if (feature == null)
{
feature = Collection.Get<TFeature>();
if (feature == null)
{
feature = factory(state);

Collection.Set(feature);
if (!cleared)
{
Cache = default(TCache);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bit late; but not sure about this if statement? What's it doing?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be #524 ?

}
Revision = Collection.Revision;
}
cached = feature;
}
return feature;
}

public TFeature Fetch<TFeature>(ref TFeature cached, Func<IFeatureCollection, TFeature> factory) =>
Fetch(ref cached, Collection, factory);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,56 +12,27 @@

namespace Microsoft.AspNet.Http.Authentication.Internal
{
public class DefaultAuthenticationManager : AuthenticationManager, IFeatureCache
public class DefaultAuthenticationManager : AuthenticationManager
{
private IFeatureCollection _features;
private int _cachedFeaturesRevision = -1;

private IHttpAuthenticationFeature _authentication;
private FeatureReferences<IHttpAuthenticationFeature> _features;

public DefaultAuthenticationManager(IFeatureCollection features)
{
_features = features;
((IFeatureCache)this).SetFeaturesRevision();
}

void IFeatureCache.CheckFeaturesRevision()
{
if (_cachedFeaturesRevision != _features.Revision)
{
ResetFeatures();
}
}

void IFeatureCache.SetFeaturesRevision()
{
_cachedFeaturesRevision = _features.Revision;
Initialize(features);
}

public void UpdateFeatures(IFeatureCollection features)
public virtual void Initialize(IFeatureCollection features)
{
_features = features;
ResetFeatures();
_features = new FeatureReferences<IHttpAuthenticationFeature>(features);
}

private void ResetFeatures()
public virtual void Uninitialize()
{
_authentication = null;

((IFeatureCache)this).SetFeaturesRevision();
_features = default(FeatureReferences<IHttpAuthenticationFeature>);
}

private IHttpAuthenticationFeature HttpAuthenticationFeature
{
get
{
return FeatureHelpers.GetOrCreateAndCache(
this,
_features,
() => new HttpAuthenticationFeature(),
ref _authentication);
}
}
private IHttpAuthenticationFeature HttpAuthenticationFeature =>
_features.Fetch(ref _features.Cache, f => new HttpAuthenticationFeature());

public override IEnumerable<AuthenticationDescription> GetAuthenticationSchemes()
{
Expand Down
68 changes: 17 additions & 51 deletions src/Microsoft.AspNet.Http/DefaultConnectionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,70 +10,30 @@

namespace Microsoft.AspNet.Http.Internal
{
public class DefaultConnectionInfo : ConnectionInfo, IFeatureCache
public class DefaultConnectionInfo : ConnectionInfo
{
private IFeatureCollection _features;
private int _cachedFeaturesRevision = -1;

private IHttpConnectionFeature _connection;
private ITlsConnectionFeature _tlsConnection;
private FeatureReferences<FeatureInterfaces> _features;

public DefaultConnectionInfo(IFeatureCollection features)
{
_features = features;
((IFeatureCache)this).SetFeaturesRevision();
}

void IFeatureCache.CheckFeaturesRevision()
{
if (_cachedFeaturesRevision != _features.Revision)
{
ResetFeatures();
}
Initialize(features);
}

void IFeatureCache.SetFeaturesRevision()
public virtual void Initialize( IFeatureCollection features)
{
_cachedFeaturesRevision = _features.Revision;
_features = new FeatureReferences<FeatureInterfaces>(features);
}

public void UpdateFeatures(IFeatureCollection features)
public virtual void Uninitialize()
{
_features = features;
ResetFeatures();
_features = default(FeatureReferences<FeatureInterfaces>);
}

private void ResetFeatures()
{
_connection = null;
_tlsConnection = null;
private IHttpConnectionFeature HttpConnectionFeature =>
_features.Fetch(ref _features.Cache.Connection, f => new HttpConnectionFeature());

((IFeatureCache)this).SetFeaturesRevision();
}

private IHttpConnectionFeature HttpConnectionFeature
{
get
{
return FeatureHelpers.GetOrCreateAndCache(
this,
_features,
() => new HttpConnectionFeature(),
ref _connection);
}
}

private ITlsConnectionFeature TlsConnectionFeature
{
get
{
return FeatureHelpers.GetOrCreateAndCache(
this,
_features,
() => new TlsConnectionFeature(),
ref _tlsConnection);
}
}
private ITlsConnectionFeature TlsConnectionFeature=>
_features.Fetch(ref _features.Cache.TlsConnection, f => new TlsConnectionFeature());

public override IPAddress RemoteIpAddress
{
Expand Down Expand Up @@ -115,5 +75,11 @@ public override X509Certificate2 ClientCertificate
{
return TlsConnectionFeature.GetClientCertificateAsync(cancellationToken);
}

struct FeatureInterfaces
{
public IHttpConnectionFeature Connection;
public ITlsConnectionFeature TlsConnection;
}
}
}
Loading