// Copyright (c) All contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Buffers; using System.Collections.Generic; using Nerdbank.Streams; namespace MessagePack { /// /// A thread-safe, alloc-free reusable object pool. /// internal class SequencePool { /// /// A thread-safe pool of reusable objects. /// /// /// We use a that allows every processor to be involved in messagepack serialization concurrently, /// plus one nested serialization per processor (since LZ4 and sometimes other nested serializations may exist). /// internal static readonly SequencePool Shared = new SequencePool(Environment.ProcessorCount * 2); /// /// The value to use for . /// /// /// Individual users that want a different value for this can modify the setting on the rented /// or by supplying their own . /// /// /// We use 32KB so that when LZ4Codec.MaximumOutputLength is used on this length it does not require a /// buffer that would require the Large Object Heap. /// private const int MinimumSpanLength = 32 * 1024; private readonly int maxSize; private readonly Stack> pool = new Stack>(); /// /// The array pool which we share with all objects created by this instance. /// /// /// We allow 100 arrays to be shared (instead of the default 50) and reduce the max array length from the default 1MB to something more reasonable for our expected use. /// private readonly ArrayPool arrayPool = ArrayPool.Create(80 * 1024, 100); /// /// Initializes a new instance of the class. /// /// The maximum size to allow the pool to grow. internal SequencePool(int maxSize) { this.maxSize = maxSize; } /// /// Gets an instance of /// This is taken from the recycled pool if one is available; otherwise a new one is created. /// /// The rental tracker that provides access to the object as well as a means to return it. internal Rental Rent() { lock (this.pool) { if (this.pool.Count > 0) { return new Rental(this, this.pool.Pop()); } } // Configure the newly created object to share a common array pool with the other instances, // otherwise each one will have its own ArrayPool which would likely waste a lot of memory. return new Rental(this, new Sequence(this.arrayPool) { MinimumSpanLength = MinimumSpanLength }); } private void Return(Sequence value) { value.Reset(); lock (this.pool) { if (this.pool.Count < this.maxSize) { // Reset to preferred settings in case the renter changed them. value.MinimumSpanLength = MinimumSpanLength; this.pool.Push(value); } } } internal struct Rental : IDisposable { private readonly SequencePool owner; internal Rental(SequencePool owner, Sequence value) { this.owner = owner; this.Value = value; } /// /// Gets the recyclable object. /// public Sequence Value { get; } /// /// Returns the recyclable object to the pool. /// /// /// The instance is cleaned first, if a clean delegate was provided. /// It is dropped instead of being returned to the pool if the pool is already at its maximum size. /// public void Dispose() { this.owner.Return(this.Value); } } } }