diff --git a/source/Handlebars/Arguments.cs b/source/Handlebars/Arguments.cs index 035a4b7a..e596308d 100644 --- a/source/Handlebars/Arguments.cs +++ b/source/Handlebars/Arguments.cs @@ -322,7 +322,7 @@ public void Dispose() private struct Policy : IInternalObjectPoolPolicy { public Enumerator Create() => new Enumerator(); - + public bool TryClaim(Enumerator item) => true; public bool Return(Enumerator item) { item.Reset(); diff --git a/source/Handlebars/BindingContext.cs b/source/Handlebars/BindingContext.cs index 0bc4d784..940f3eac 100644 --- a/source/Handlebars/BindingContext.cs +++ b/source/Handlebars/BindingContext.cs @@ -17,8 +17,10 @@ public sealed partial class BindingContext : IDisposable, IHelpersRegistry internal readonly DeferredValue Descriptor; - private BindingContext() + private BindingContext(bool claim) { + _claimed = claim ? 1 : 0; + InlinePartialTemplates = new CascadeIndex, StringEqualityComparer>(new StringEqualityComparer(StringComparison.OrdinalIgnoreCase)); Helpers = new CascadeIndex, StringEqualityComparer>(new StringEqualityComparer()); BlockHelpers = new CascadeIndex, StringEqualityComparer>(new StringEqualityComparer()); diff --git a/source/Handlebars/Collections/ImmutableStack.cs b/source/Handlebars/Collections/ImmutableStack.cs deleted file mode 100644 index 54adb899..00000000 --- a/source/Handlebars/Collections/ImmutableStack.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using HandlebarsDotNet.Pools; - -namespace HandlebarsDotNet.Collections -{ -#if NET451 || NET452 - [Serializable] -#endif - internal readonly struct ImmutableStack - { - private readonly Node _container; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ImmutableStack(T value, Node parent) - :this(Node.Create(value, parent)) - { - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ImmutableStack(Node container) => _container = container; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ImmutableStack Push(T value) => new ImmutableStack(value, _container); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T Peek() - { - return _container == null - ? default - : _container.Value; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ImmutableStack Pop(out T value) - { - if (_container == null) - { - value = default; - return this; - } - - value = _container.Value; - var parent = _container.Parent; - _container.Dispose(); - return new ImmutableStack(parent); - } - -#if NET451 || NET452 - [Serializable] -#endif - private sealed class Node : IDisposable - { - private static readonly InternalObjectPool Pool = new InternalObjectPool(new Policy()); - - public Node Parent; - public T Value; - - public static Node Create(T value = default, Node parent = null) - { - var item = Pool.Get(); - item.Value = value; - item.Parent = parent; - return item; - } - - private Node() { } - - private struct Policy : IInternalObjectPoolPolicy - { - public Node Create() => new Node(); - - public bool Return(Node item) - { - item.Parent = null; - item.Value = default; - return true; - } - } - - public void Dispose() => Pool.Return(this); - } - } -} \ No newline at end of file diff --git a/source/Handlebars/IO/EncodedTextWriterWrapper.cs b/source/Handlebars/IO/EncodedTextWriterWrapper.cs index 2fb77ca8..e08ab835 100644 --- a/source/Handlebars/IO/EncodedTextWriterWrapper.cs +++ b/source/Handlebars/IO/EncodedTextWriterWrapper.cs @@ -66,6 +66,7 @@ public override void Write(object value) private readonly struct Policy : IInternalObjectPoolPolicy { public EncodedTextWriterWrapper Create() => new EncodedTextWriterWrapper(); + public bool TryClaim(EncodedTextWriterWrapper item) => true; public bool Return(EncodedTextWriterWrapper obj) { diff --git a/source/Handlebars/IO/PolledStringWriter.cs b/source/Handlebars/IO/PolledStringWriter.cs index 5f3c2ff1..cc35b008 100644 --- a/source/Handlebars/IO/PolledStringWriter.cs +++ b/source/Handlebars/IO/PolledStringWriter.cs @@ -40,7 +40,7 @@ public Policy(int initialCapacity, int maximumRetainedCapacity = 4096) } public ReusableStringWriter Create() => new ReusableStringWriter(); - + public bool TryClaim(ReusableStringWriter item) => true; public bool Return(ReusableStringWriter item) => _policy.Return(item.GetStringBuilder()); } } diff --git a/source/Handlebars/IO/TextEncoderWrapper.cs b/source/Handlebars/IO/TextEncoderWrapper.cs index 726a2e7c..c1c561bb 100644 --- a/source/Handlebars/IO/TextEncoderWrapper.cs +++ b/source/Handlebars/IO/TextEncoderWrapper.cs @@ -66,7 +66,7 @@ public void Dispose() private struct Policy : IInternalObjectPoolPolicy { public TextEncoderWrapper Create() => new TextEncoderWrapper(); - + public bool TryClaim(TextEncoderWrapper item) => true; public bool Return(TextEncoderWrapper item) { item._enabled = true; diff --git a/source/Handlebars/Polyfills/AsyncLocal.cs b/source/Handlebars/Polyfills/AsyncLocal.cs deleted file mode 100644 index fe3a0903..00000000 --- a/source/Handlebars/Polyfills/AsyncLocal.cs +++ /dev/null @@ -1,35 +0,0 @@ -#if NET451 || NET452 -using System; -using System.Runtime.CompilerServices; -using System.Runtime.Remoting.Messaging; - -namespace HandlebarsDotNet.Polyfills -{ - public sealed class AsyncLocal - { - private const string Slot = "__AsyncLocalSlot"; - - public T Value - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => CallContext.LogicalGetData(Slot) is Container container - ? container.Value - : default; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set => CallContext.LogicalSetData(Slot, new Container(value)); - } - - [Serializable] - private class Container - { - public readonly T Value; - - public Container(T value) - { - Value = value; - } - } - } -} -#endif \ No newline at end of file diff --git a/source/Handlebars/Pools/BindingContext.Pool.cs b/source/Handlebars/Pools/BindingContext.Pool.cs index a5036505..da36c942 100644 --- a/source/Handlebars/Pools/BindingContext.Pool.cs +++ b/source/Handlebars/Pools/BindingContext.Pool.cs @@ -1,3 +1,4 @@ +using System.Threading; using HandlebarsDotNet.Compiler; using HandlebarsDotNet.Pools; @@ -17,6 +18,8 @@ internal static BindingContext Create(ICompiledHandlebarsConfiguration configura { return Pool.CreateContext(configuration, value, parent, partialBlockTemplate); } + + private int _claimed; public void Dispose() => Pool.Return(this); @@ -39,12 +42,23 @@ public BindingContext CreateContext(ICompiledHandlebarsConfiguration configurati return context; } - internal struct BindingContextPolicy : IInternalObjectPoolPolicy + internal readonly struct BindingContextPolicy : IInternalObjectPoolPolicy { - public BindingContext Create() => new BindingContext(); + public BindingContext Create() => new(claim: true); + + public bool TryClaim(BindingContext item) + { + return Interlocked + .CompareExchange(ref item._claimed, 1, 0) == 0; + } public bool Return(BindingContext item) { + var shouldReturn = Interlocked + .CompareExchange(ref item._claimed, 0, 1) == 1; + + if (!shouldReturn) return false; + item.Root = null; item.Value = null; item.ParentContext = null; diff --git a/source/Handlebars/Pools/ClosureBuilder.Pool.cs b/source/Handlebars/Pools/ClosureBuilder.Pool.cs index 8d7ee5ff..96ff0228 100644 --- a/source/Handlebars/Pools/ClosureBuilder.Pool.cs +++ b/source/Handlebars/Pools/ClosureBuilder.Pool.cs @@ -36,6 +36,7 @@ public ClosureBuilderPool(Policy policy) : base(policy) private readonly struct Policy : IInternalObjectPoolPolicy { public ClosureBuilder Create() => new (); + public bool TryClaim(ClosureBuilder item) => true; public bool Return(ClosureBuilder item) => true; } } diff --git a/source/Handlebars/Pools/GenericObjectPool.cs b/source/Handlebars/Pools/GenericObjectPool.cs index f7d0921e..4e40132a 100644 --- a/source/Handlebars/Pools/GenericObjectPool.cs +++ b/source/Handlebars/Pools/GenericObjectPool.cs @@ -10,6 +10,7 @@ private GenericObjectPool() : base(new Policy()) { } public readonly struct Policy : IInternalObjectPoolPolicy { public T Create() => new (); + public bool TryClaim(T item) => true; public bool Return(T item) => true; } } diff --git a/source/Handlebars/Pools/ObjectPool.cs b/source/Handlebars/Pools/ObjectPool.cs index a5f4702a..1e0870b0 100644 --- a/source/Handlebars/Pools/ObjectPool.cs +++ b/source/Handlebars/Pools/ObjectPool.cs @@ -18,11 +18,11 @@ public InternalObjectPool(TPolicy policy) public T Get() { - if (_queue.TryDequeue(out var item)) + if (_queue.TryDequeue(out var item) && _policy.TryClaim(item)) { return item; } - + return _policy.Create(); } @@ -36,6 +36,7 @@ public void Return(T obj) internal interface IInternalObjectPoolPolicy { T Create(); + bool TryClaim(T item); bool Return(T item); } } \ No newline at end of file diff --git a/source/Handlebars/Pools/StringBuilderPool.cs b/source/Handlebars/Pools/StringBuilderPool.cs index 60760828..10117a27 100644 --- a/source/Handlebars/Pools/StringBuilderPool.cs +++ b/source/Handlebars/Pools/StringBuilderPool.cs @@ -31,6 +31,8 @@ public StringBuilder Create() return new StringBuilder(InitialCapacity); } + public bool TryClaim(StringBuilder item) => true; + public bool Return(StringBuilder item) { if (item.Capacity > MaximumRetainedCapacity) diff --git a/source/Handlebars/Runtime/AmbientContext.cs b/source/Handlebars/Runtime/AmbientContext.cs index 561e6e80..c64c89c9 100644 --- a/source/Handlebars/Runtime/AmbientContext.cs +++ b/source/Handlebars/Runtime/AmbientContext.cs @@ -1,12 +1,6 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; -#if !NET451 && !NET452 -using System.Threading; -#else -using HandlebarsDotNet.Polyfills; -#endif -using HandlebarsDotNet.Collections; using HandlebarsDotNet.IO; using HandlebarsDotNet.ObjectDescriptors; using HandlebarsDotNet.PathStructure; @@ -16,14 +10,18 @@ namespace HandlebarsDotNet.Runtime { public sealed class AmbientContext : IDisposable { - private static readonly InternalObjectPool Pool = new InternalObjectPool(new Policy()); + private static readonly InternalObjectPool Pool = new(new Policy()); - private static readonly AsyncLocal> Local = new AsyncLocal>(); + [ThreadStatic] + private static Stack _local; + + private static Stack Local => + _local ??= new Stack(); public static AmbientContext Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => Local.Value.Peek(); + get => Local.Count > 0 ? Local.Peek() : null; } public static AmbientContext Create( @@ -67,12 +65,9 @@ public static AmbientContext Create( public static DisposableContainer Use(AmbientContext ambientContext) { - Local.Value = Local.Value.Push(ambientContext); + Local.Push(ambientContext); - return new DisposableContainer(() => - { - Local.Value = Local.Value.Pop(out _); - }); + return new DisposableContainer(() => Local.Pop()); } private AmbientContext() @@ -93,10 +88,9 @@ private AmbientContext() private struct Policy : IInternalObjectPoolPolicy { - public AmbientContext Create() - { - return new AmbientContext(); - } + public AmbientContext Create() => new(); + + public bool TryClaim(AmbientContext item) => true; public bool Return(AmbientContext item) {