File: System\Collections\Immutable\SecureObjectPool.cs
Web Access
Project: src\src\libraries\System.Collections.Immutable\src\System.Collections.Immutable.csproj (System.Collections.Immutable)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Threading;
 
namespace System.Collections.Immutable
{
    /// <summary>
    /// Object pooling utilities.
    /// </summary>
    internal static class SecureObjectPool
    {
        /// <summary>
        /// The ever-incrementing (and wrap-on-overflow) integer for owner id's.
        /// </summary>
        private static int s_poolUserIdCounter;
 
        /// <summary>
        /// The ID reserved for unassigned objects.
        /// </summary>
        internal const int UnassignedId = -1;
 
        /// <summary>
        /// Returns a new ID.
        /// </summary>
        internal static int NewId()
        {
            int result;
            do
            {
                result = Interlocked.Increment(ref s_poolUserIdCounter);
            }
            while (result == UnassignedId);
 
            return result;
        }
    }
 
    internal static class SecureObjectPool<T, TCaller>
        where TCaller : ISecurePooledObjectUser
    {
        public static void TryAdd(TCaller caller, SecurePooledObject<T> item)
        {
            // Only allow the caller to recycle this object if it is the current owner.
            if (caller.PoolUserId == item.Owner)
            {
                item.Owner = SecureObjectPool.UnassignedId;
                AllocFreeConcurrentStack<SecurePooledObject<T>>.TryAdd(item);
            }
        }
 
        public static bool TryTake(TCaller caller, out SecurePooledObject<T>? item)
        {
            if (caller.PoolUserId != SecureObjectPool.UnassignedId && AllocFreeConcurrentStack<SecurePooledObject<T>>.TryTake(out item))
            {
                item.Owner = caller.PoolUserId;
                return true;
            }
            else
            {
                item = null;
                return false;
            }
        }
 
        public static SecurePooledObject<T> PrepNew(TCaller caller, T newValue)
        {
            Requires.NotNullAllowStructs(newValue, nameof(newValue));
            var pooledObject = new SecurePooledObject<T>(newValue);
            pooledObject.Owner = caller.PoolUserId;
            return pooledObject;
        }
    }
 
    internal interface ISecurePooledObjectUser
    {
        int PoolUserId { get; }
    }
 
    internal sealed class SecurePooledObject<T>
    {
        private readonly T _value;
        private int _owner;
 
        internal SecurePooledObject(T newValue)
        {
            Requires.NotNullAllowStructs(newValue, nameof(newValue));
            _value = newValue;
        }
 
        /// <summary>
        /// Gets or sets the current owner of this recyclable object.
        /// </summary>
        internal int Owner
        {
            get { return _owner; }
            set { _owner = value; }
        }
 
        /// <summary>
        /// Returns the recyclable value if it hasn't been reclaimed already.
        /// </summary>
        /// <typeparam name="TCaller">The type of renter of the object.</typeparam>
        /// <param name="caller">The renter of the object.</param>
        /// <returns>The rented object.</returns>
        /// <exception cref="ObjectDisposedException">Thrown if <paramref name="caller"/> is no longer the renter of the value.</exception>
        internal T Use<TCaller>(ref TCaller caller)
            where TCaller : struct, ISecurePooledObjectUser
        {
            if (!IsOwned(ref caller))
                Requires.FailObjectDisposed(caller);
            return _value;
        }
 
        internal bool TryUse<TCaller>(ref TCaller caller, [MaybeNullWhen(false)] out T value)
            where TCaller : struct, ISecurePooledObjectUser
        {
            if (IsOwned(ref caller))
            {
                value = _value;
                return true;
            }
            else
            {
                value = default;
                return false;
            }
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal bool IsOwned<TCaller>(ref TCaller caller)
            where TCaller : struct, ISecurePooledObjectUser
        {
            return caller.PoolUserId == _owner;
        }
    }
}