File: System\Runtime\InteropServices\HandleCollector.cs
Web Access
Project: src\src\libraries\System.Runtime.InteropServices\src\System.Runtime.InteropServices.csproj (System.Runtime.InteropServices)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Threading;
 
namespace System.Runtime.InteropServices
{
    public sealed class HandleCollector
    {
        // Used for increasing the threshold.
        private const int DeltaPercent = 10;
        private int _threshold;
        private int _handleCount;
 
        private readonly int[] _gcCounts = new int[3];
        private int _gcGeneration;
 
        public HandleCollector(string? name, int initialThreshold) :
            this(name, initialThreshold, int.MaxValue)
        {
        }
 
        public HandleCollector(string? name, int initialThreshold, int maximumThreshold)
        {
            if (initialThreshold < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(initialThreshold), initialThreshold, SR.Arg_NeedNonNegNumRequired);
            }
            if (maximumThreshold < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(maximumThreshold), maximumThreshold, SR.Arg_NeedNonNegNumRequired);
            }
            if (initialThreshold > maximumThreshold)
            {
                throw new ArgumentException(SR.Arg_InvalidThreshold, nameof(initialThreshold));
            }
 
            Name = name ?? string.Empty;
            InitialThreshold = initialThreshold;
            MaximumThreshold = maximumThreshold;
            _threshold = initialThreshold;
            _handleCount = 0;
            _gcGeneration = 0;
        }
 
        public int Count => _handleCount;
 
        public int InitialThreshold { get; }
 
        public int MaximumThreshold { get; }
 
        public string Name { get; }
 
        public void Add()
        {
            int collectionGeneration = -1;
            Interlocked.Increment(ref _handleCount);
            if (_handleCount < 0)
            {
                throw new InvalidOperationException(SR.InvalidOperation_HCCountOverflow);
            }
 
            if (_handleCount > _threshold)
            {
                lock (this)
                {
                    _threshold = _handleCount + (_handleCount / DeltaPercent);
                    collectionGeneration = _gcGeneration;
                    if (_gcGeneration < 2)
                    {
                        _gcGeneration++;
                    }
                }
            }
 
            if ((collectionGeneration >= 0) &&
                    ((collectionGeneration == 0) ||
                    (_gcCounts[collectionGeneration] == GC.CollectionCount(collectionGeneration))))
            {
                GC.Collect(collectionGeneration);
                Thread.Sleep(10 * collectionGeneration);
            }
 
            // Don't bother with gen0.
            for (int i = 1; i < 3; i++)
            {
                _gcCounts[i] = GC.CollectionCount(i);
            }
        }
 
        public void Remove()
        {
            Interlocked.Decrement(ref _handleCount);
            if (_handleCount < 0)
            {
                throw new InvalidOperationException(SR.InvalidOperation_HCCountOverflow);
            }
 
            int newThreshold = _handleCount + _handleCount / DeltaPercent;
            if (newThreshold < (_threshold - _threshold / DeltaPercent))
            {
                lock (this)
                {
                    if (newThreshold > InitialThreshold)
                    {
                        _threshold = newThreshold;
                    }
                    else
                    {
                        _threshold = InitialThreshold;
                    }
                    _gcGeneration = 0;
                }
            }
 
            for (int i = 1; i < 3; i++)
            {
                _gcCounts[i] = GC.CollectionCount(i);
            }
        }
    }
}