internal class PoolManager<T>
    : IAsyncDisposable
{
   private readonly Func<Task<T>> _createAction;
   private readonly Func<Func<Task<T>>, T, ValueTask<T>> _resetAction;
   private readonly Func<T, ValueTask> _disposeAction;
   private readonly bool _canCreateNew;
   private readonly Channel<T> _values;

   #region

   private PoolManager(
        Func<Task<T>> createAction,
        Func<Func<Task<T>>, T, ValueTask<T>> resetAction,
        Func<T, ValueTask> disposeAction,
       int? fixedSize,
        IEnumerable<T>? values = null
        )
    {
        _createAction = createAction;
        _resetAction = resetAction;
        _disposeAction = disposeAction;
        _canCreateNew = !fixedSize.HasValue;

       if (fixedSize.HasValue)
        {
            _canCreateNew = false;
            _values = Channel.CreateBounded<T>(fixedSize.Value);
        }
       else
        {
            _canCreateNew = true;
            _values = Channel.CreateUnbounded<T>();
        }

       if (values != null)
        {
           foreach (var elem in values)
            {
               if (!_values.Writer.TryWrite(elem))
                {
                   throw new Exception();
                }
            }
        }
    }


   public static PoolManager<T> Create(
        Func<Task<T>> createAction,
        Func<Func<Task<T>>, T, ValueTask<T>> resetAction,
        Func<T, ValueTask> disposeAction
        )
    {
       return new PoolManager<T>(
            createAction,
            resetAction,
            disposeAction,
            fixedSize: null
            );
    }

   public static async Task<PoolManager<T>> CreateFixedAsync(
        Func<Task<T>> createAction,
        Func<Func<Task<T>>, T, ValueTask<T>> resetAction,
        Func<T, ValueTask> disposeAction,
       int initCount
        )
    {
       var createTasks = Enumerable.Repeat(true, initCount)
            .Select(e => createAction())
            .ToArray();
       await Task.WhenAll(createTasks);

       return new PoolManager<T>(
            createAction,
            resetAction,
            disposeAction,
            fixedSize: initCount,
            values: createTasks.Select(e => e.Result)
            );
    }

   #endregion


   #region

   public async Task<PoolItem> GetAsync()
    {
        T value;
       if (_canCreateNew)
        {
           if (!_values.Reader.TryRead(out value!))
            {
               value = await _createAction();
            }
        }
       else
        {
           value = await _values.Reader.ReadAsync();
        }

       return new PoolManager<T>.PoolItem(
           this,
           value
            );
    }

   public async ValueTask DisposeAsync()
    {
        List<Task> disposeTasks = new List<Task>(5);
       while (_values.Reader.TryRead(out var elem))
        {
            disposeTasks.Add(
                _disposeAction(elem).AsTask()
                );
        }

       await Task.WhenAll(disposeTasks);
    }

   #endregion


   #region

   public record PoolItem
        : IAsyncDisposable
    {
       private readonly PoolManager<T> _poolManager;
       public T Value { get; }


       public PoolItem(
            PoolManager<T> poolManager,
            T value
            )
        {
            _poolManager = poolManager;
            Value = value;
        }

       public async ValueTask DisposeAsync()
        {
           var value = await _poolManager._resetAction(_poolManager._createAction, Value);
           if (!_poolManager._values.Writer.TryWrite(value))
            {
               throw new Exception();
            }
        }
    }

   #endregion
}
 System. Threading. Channels
  

 

Теги: