Skip to content

Commit

Permalink
Improve object pool safety
Browse files Browse the repository at this point in the history
  • Loading branch information
oformaniuk committed Mar 4, 2022
1 parent d193100 commit 300f2f6
Show file tree
Hide file tree
Showing 13 changed files with 42 additions and 145 deletions.
2 changes: 1 addition & 1 deletion source/Handlebars/Arguments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ public void Dispose()
private struct Policy : IInternalObjectPoolPolicy<Enumerator>
{
public Enumerator Create() => new Enumerator();

public bool TryClaim(Enumerator item) => true;
public bool Return(Enumerator item)
{
item.Reset();
Expand Down
4 changes: 3 additions & 1 deletion source/Handlebars/BindingContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ public sealed partial class BindingContext : IDisposable, IHelpersRegistry

internal readonly DeferredValue<BindingContext, ObjectDescriptor> Descriptor;

private BindingContext()
private BindingContext(bool claim)
{
_claimed = claim ? 1 : 0;

InlinePartialTemplates = new CascadeIndex<string, Action<EncodedTextWriter, BindingContext>, StringEqualityComparer>(new StringEqualityComparer(StringComparison.OrdinalIgnoreCase));
Helpers = new CascadeIndex<string, IHelperDescriptor<HelperOptions>, StringEqualityComparer>(new StringEqualityComparer());
BlockHelpers = new CascadeIndex<string, IHelperDescriptor<BlockHelperOptions>, StringEqualityComparer>(new StringEqualityComparer());
Expand Down
84 changes: 0 additions & 84 deletions source/Handlebars/Collections/ImmutableStack.cs

This file was deleted.

1 change: 1 addition & 0 deletions source/Handlebars/IO/EncodedTextWriterWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public override void Write(object value)
private readonly struct Policy : IInternalObjectPoolPolicy<EncodedTextWriterWrapper>
{
public EncodedTextWriterWrapper Create() => new EncodedTextWriterWrapper();
public bool TryClaim(EncodedTextWriterWrapper item) => true;

public bool Return(EncodedTextWriterWrapper obj)
{
Expand Down
2 changes: 1 addition & 1 deletion source/Handlebars/IO/PolledStringWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
Expand Down
2 changes: 1 addition & 1 deletion source/Handlebars/IO/TextEncoderWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public void Dispose()
private struct Policy : IInternalObjectPoolPolicy<TextEncoderWrapper>
{
public TextEncoderWrapper Create() => new TextEncoderWrapper();

public bool TryClaim(TextEncoderWrapper item) => true;
public bool Return(TextEncoderWrapper item)
{
item._enabled = true;
Expand Down
35 changes: 0 additions & 35 deletions source/Handlebars/Polyfills/AsyncLocal.cs

This file was deleted.

18 changes: 16 additions & 2 deletions source/Handlebars/Pools/BindingContext.Pool.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Threading;
using HandlebarsDotNet.Compiler;
using HandlebarsDotNet.Pools;

Expand All @@ -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);

Expand All @@ -39,12 +42,23 @@ public BindingContext CreateContext(ICompiledHandlebarsConfiguration configurati
return context;
}

internal struct BindingContextPolicy : IInternalObjectPoolPolicy<BindingContext>
internal readonly struct BindingContextPolicy : IInternalObjectPoolPolicy<BindingContext>
{
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;
Expand Down
1 change: 1 addition & 0 deletions source/Handlebars/Pools/ClosureBuilder.Pool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public ClosureBuilderPool(Policy policy) : base(policy)
private readonly struct Policy : IInternalObjectPoolPolicy<ClosureBuilder>
{
public ClosureBuilder Create() => new ();
public bool TryClaim(ClosureBuilder item) => true;
public bool Return(ClosureBuilder item) => true;
}
}
Expand Down
1 change: 1 addition & 0 deletions source/Handlebars/Pools/GenericObjectPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ private GenericObjectPool() : base(new Policy()) { }
public readonly struct Policy : IInternalObjectPoolPolicy<T>
{
public T Create() => new ();
public bool TryClaim(T item) => true;
public bool Return(T item) => true;
}
}
Expand Down
5 changes: 3 additions & 2 deletions source/Handlebars/Pools/ObjectPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand All @@ -36,6 +36,7 @@ public void Return(T obj)
internal interface IInternalObjectPoolPolicy<T>
{
T Create();
bool TryClaim(T item);
bool Return(T item);
}
}
2 changes: 2 additions & 0 deletions source/Handlebars/Pools/StringBuilderPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
30 changes: 12 additions & 18 deletions source/Handlebars/Runtime/AmbientContext.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -16,14 +10,18 @@ namespace HandlebarsDotNet.Runtime
{
public sealed class AmbientContext : IDisposable
{
private static readonly InternalObjectPool<AmbientContext, Policy> Pool = new InternalObjectPool<AmbientContext, Policy>(new Policy());
private static readonly InternalObjectPool<AmbientContext, Policy> Pool = new(new Policy());

private static readonly AsyncLocal<ImmutableStack<AmbientContext>> Local = new AsyncLocal<ImmutableStack<AmbientContext>>();
[ThreadStatic]
private static Stack<AmbientContext> _local;

private static Stack<AmbientContext> Local =>
_local ??= new Stack<AmbientContext>();

public static AmbientContext Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Local.Value.Peek();
get => Local.Count > 0 ? Local.Peek() : null;
}

public static AmbientContext Create(
Expand Down Expand Up @@ -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()
Expand All @@ -93,10 +88,9 @@ private AmbientContext()

private struct Policy : IInternalObjectPoolPolicy<AmbientContext>
{
public AmbientContext Create()
{
return new AmbientContext();
}
public AmbientContext Create() => new();

public bool TryClaim(AmbientContext item) => true;

public bool Return(AmbientContext item)
{
Expand Down

0 comments on commit 300f2f6

Please sign in to comment.