< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Runtime.InternalBufferManager
Assembly: CoreWCF.WebHttp
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.WebHttp/src/CoreWCF/Runtime/InternalBufferManager.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 174
Coverable lines: 174
Total lines: 495
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 78
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor()100%110%
Create(...)0%220%
.ctor(...)0%10100%
Clear()0%220%
ChangeQuota(...)0%440%
DecreaseQuota(...)100%110%
FindMostExcessivePool()0%660%
FindMostStarvedPool()0%660%
FindPool(...)0%440%
IncreaseQuota(...)100%110%
ReturnBuffer(...)0%660%
TakeBuffer(...)0%880%
TuneQuotas()0%18180%
.ctor(...)100%110%
Clear()100%110%
DecrementCount()0%220%
IncrementCount()0%440%
CreatePool(...)0%220%
.ctor(...)100%110%
OnClear()100%110%
Take()100%110%
Return(...)100%110%
.ctor(...)100%110%
OnClear()100%110%
Take()0%220%
Return(...)0%220%
.ctor()100%110%
Clear()100%110%
TakeBuffer(...)100%110%
ReturnBuffer(...)100%110%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.WebHttp/src/CoreWCF/Runtime/InternalBufferManager.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Collections.Generic;
 5using System.Threading;
 6
 7namespace CoreWCF.Runtime
 8{
 9    internal abstract class InternalBufferManager
 10    {
 011        protected InternalBufferManager()
 12        {
 013        }
 14
 15        public abstract byte[] TakeBuffer(int bufferSize);
 16        public abstract void ReturnBuffer(byte[] buffer);
 17        public abstract void Clear();
 18
 19        public static InternalBufferManager Create(long maxBufferPoolSize, int maxBufferSize)
 20        {
 021            if (maxBufferPoolSize == 0)
 22            {
 023                return GCBufferManager.Value;
 24            }
 25            else
 26            {
 27                Fx.Assert(maxBufferPoolSize > 0 && maxBufferSize >= 0, "bad params, caller should verify");
 028                return new PooledBufferManager(maxBufferPoolSize, maxBufferSize);
 29            }
 30        }
 31
 32        private class PooledBufferManager : InternalBufferManager
 33        {
 34            private const int minBufferSize = 128;
 35            private const int maxMissesBeforeTuning = 8;
 36            private const int initialBufferCount = 1;
 37            private readonly object _tuningLock;
 38            private readonly int[] _bufferSizes;
 39            private readonly BufferPool[] _bufferPools;
 40            private readonly long _memoryLimit;
 41            private long _remainingMemory;
 42            private bool _areQuotasBeingTuned;
 43            private int _totalMisses;
 44
 045            public PooledBufferManager(long maxMemoryToPool, int maxBufferSize)
 46            {
 047                _tuningLock = new object();
 048                _memoryLimit = maxMemoryToPool;
 049                _remainingMemory = maxMemoryToPool;
 050                List<BufferPool> bufferPoolList = new List<BufferPool>();
 51
 052                for (int bufferSize = minBufferSize; ;)
 53                {
 054                    long bufferCountLong = _remainingMemory / bufferSize;
 55
 056                    int bufferCount = bufferCountLong > int.MaxValue ? int.MaxValue : (int)bufferCountLong;
 57
 058                    if (bufferCount > initialBufferCount)
 59                    {
 060                        bufferCount = initialBufferCount;
 61                    }
 62
 063                    bufferPoolList.Add(BufferPool.CreatePool(bufferSize, bufferCount));
 64
 065                    _remainingMemory -= (long)bufferCount * bufferSize;
 66
 067                    if (bufferSize >= maxBufferSize)
 68                    {
 69                        break;
 70                    }
 71
 072                    long newBufferSizeLong = (long)bufferSize * 2;
 73
 074                    if (newBufferSizeLong > (long)maxBufferSize)
 75                    {
 076                        bufferSize = maxBufferSize;
 77                    }
 78                    else
 79                    {
 080                        bufferSize = (int)newBufferSizeLong;
 81                    }
 82                }
 83
 084                _bufferPools = bufferPoolList.ToArray();
 085                _bufferSizes = new int[_bufferPools.Length];
 086                for (int i = 0; i < _bufferPools.Length; i++)
 87                {
 088                    _bufferSizes[i] = _bufferPools[i].BufferSize;
 89                }
 090            }
 91
 92            public override void Clear()
 93            {
 094                for (int i = 0; i < _bufferPools.Length; i++)
 95                {
 096                    BufferPool bufferPool = _bufferPools[i];
 097                    bufferPool.Clear();
 98                }
 099            }
 100
 101            private void ChangeQuota(ref BufferPool bufferPool, int delta)
 102            {
 103                //if (TraceCore.BufferPoolChangeQuotaIsEnabled(Fx.Trace))
 104                //{
 105                //    TraceCore.BufferPoolChangeQuota(Fx.Trace, bufferPool.BufferSize, delta);
 106                //}
 107
 0108                BufferPool oldBufferPool = bufferPool;
 0109                int newLimit = oldBufferPool.Limit + delta;
 0110                BufferPool newBufferPool = BufferPool.CreatePool(oldBufferPool.BufferSize, newLimit);
 0111                for (int i = 0; i < newLimit; i++)
 112                {
 0113                    byte[] buffer = oldBufferPool.Take();
 0114                    if (buffer == null)
 115                    {
 116                        break;
 117                    }
 0118                    newBufferPool.Return(buffer);
 0119                    newBufferPool.IncrementCount();
 120                }
 0121                _remainingMemory -= oldBufferPool.BufferSize * delta;
 0122                bufferPool = newBufferPool;
 0123            }
 124
 125            private void DecreaseQuota(ref BufferPool bufferPool)
 126            {
 0127                ChangeQuota(ref bufferPool, -1);
 0128            }
 129
 130            private int FindMostExcessivePool()
 131            {
 0132                long maxBytesInExcess = 0;
 0133                int index = -1;
 134
 0135                for (int i = 0; i < _bufferPools.Length; i++)
 136                {
 0137                    BufferPool bufferPool = _bufferPools[i];
 138
 0139                    if (bufferPool.Peak < bufferPool.Limit)
 140                    {
 0141                        long bytesInExcess = (bufferPool.Limit - bufferPool.Peak) * (long)bufferPool.BufferSize;
 142
 0143                        if (bytesInExcess > maxBytesInExcess)
 144                        {
 0145                            index = i;
 0146                            maxBytesInExcess = bytesInExcess;
 147                        }
 148                    }
 149                }
 150
 0151                return index;
 152            }
 153
 154            private int FindMostStarvedPool()
 155            {
 0156                long maxBytesMissed = 0;
 0157                int index = -1;
 158
 0159                for (int i = 0; i < _bufferPools.Length; i++)
 160                {
 0161                    BufferPool bufferPool = _bufferPools[i];
 162
 0163                    if (bufferPool.Peak == bufferPool.Limit)
 164                    {
 0165                        long bytesMissed = bufferPool.Misses * (long)bufferPool.BufferSize;
 166
 0167                        if (bytesMissed > maxBytesMissed)
 168                        {
 0169                            index = i;
 0170                            maxBytesMissed = bytesMissed;
 171                        }
 172                    }
 173                }
 174
 0175                return index;
 176            }
 177
 178            private BufferPool FindPool(int desiredBufferSize)
 179            {
 0180                for (int i = 0; i < _bufferSizes.Length; i++)
 181                {
 0182                    if (desiredBufferSize <= _bufferSizes[i])
 183                    {
 0184                        return _bufferPools[i];
 185                    }
 186                }
 187
 0188                return null;
 189            }
 190
 191            private void IncreaseQuota(ref BufferPool bufferPool)
 192            {
 0193                ChangeQuota(ref bufferPool, 1);
 0194            }
 195
 196            public override void ReturnBuffer(byte[] buffer)
 197            {
 198                Fx.Assert(buffer != null, "caller must verify");
 0199                BufferPool bufferPool = FindPool(buffer.Length);
 0200                if (bufferPool != null)
 201                {
 0202                    if (buffer.Length != bufferPool.BufferSize)
 203                    {
 0204                        throw Fx.Exception.Argument(nameof(buffer), SR.BufferIsNotRightSizeForBufferManager);
 205                    }
 206
 0207                    if (bufferPool.Return(buffer))
 208                    {
 0209                        bufferPool.IncrementCount();
 210                    }
 211                }
 0212            }
 213
 214            public override byte[] TakeBuffer(int bufferSize)
 215            {
 216                Fx.Assert(bufferSize >= 0, "caller must ensure a non-negative argument");
 217
 0218                BufferPool bufferPool = FindPool(bufferSize);
 219                byte[] returnValue;
 0220                if (bufferPool != null)
 221                {
 0222                    byte[] buffer = bufferPool.Take();
 0223                    if (buffer != null)
 224                    {
 0225                        bufferPool.DecrementCount();
 0226                        returnValue = buffer;
 227                    }
 228                    else
 229                    {
 0230                        if (bufferPool.Peak == bufferPool.Limit)
 231                        {
 0232                            bufferPool.Misses++;
 0233                            if (++_totalMisses >= maxMissesBeforeTuning)
 234                            {
 0235                                TuneQuotas();
 236                            }
 237                        }
 238
 239                        //if (TraceCore.BufferPoolAllocationIsEnabled(Fx.Trace))
 240                        //{
 241                        //    TraceCore.BufferPoolAllocation(Fx.Trace, bufferPool.BufferSize);
 242                        //}
 243
 0244                        returnValue = Fx.AllocateByteArray(bufferPool.BufferSize);
 245                    }
 246                }
 247                else
 248                {
 249                    //if (TraceCore.BufferPoolAllocationIsEnabled(Fx.Trace))
 250                    //{
 251                    //    TraceCore.BufferPoolAllocation(Fx.Trace, bufferSize);
 252                    //}
 253
 0254                    returnValue = Fx.AllocateByteArray(bufferSize);
 255                }
 256
 0257                return returnValue;
 258            }
 259
 260            private void TuneQuotas()
 261            {
 0262                if (_areQuotasBeingTuned)
 263                {
 0264                    return;
 265                }
 266
 0267                bool lockHeld = false;
 268                try
 269                {
 0270                    Monitor.TryEnter(_tuningLock, ref lockHeld);
 271
 272                    // Don't bother if another thread already has the lock
 0273                    if (!lockHeld || _areQuotasBeingTuned)
 274                    {
 0275                        return;
 276                    }
 277
 0278                    _areQuotasBeingTuned = true;
 0279                }
 280                finally
 281                {
 0282                    if (lockHeld)
 283                    {
 0284                        Monitor.Exit(_tuningLock);
 285                    }
 0286                }
 287
 288                // find the "poorest" pool
 0289                int starvedIndex = FindMostStarvedPool();
 0290                if (starvedIndex >= 0)
 291                {
 0292                    BufferPool starvedBufferPool = _bufferPools[starvedIndex];
 293
 0294                    if (_remainingMemory < starvedBufferPool.BufferSize)
 295                    {
 296                        // find the "richest" pool
 0297                        int excessiveIndex = FindMostExcessivePool();
 0298                        if (excessiveIndex >= 0)
 299                        {
 300                            // steal from the richest
 0301                            DecreaseQuota(ref _bufferPools[excessiveIndex]);
 302                        }
 303                    }
 304
 0305                    if (_remainingMemory >= starvedBufferPool.BufferSize)
 306                    {
 307                        // give to the poorest
 0308                        IncreaseQuota(ref _bufferPools[starvedIndex]);
 309                    }
 310                }
 311
 312                // reset statistics
 0313                for (int i = 0; i < _bufferPools.Length; i++)
 314                {
 0315                    BufferPool bufferPool = _bufferPools[i];
 0316                    bufferPool.Misses = 0;
 317                }
 318
 0319                _totalMisses = 0;
 0320                _areQuotasBeingTuned = false;
 0321            }
 322
 323            private abstract class BufferPool
 324            {
 325                private int _count;
 326
 0327                public BufferPool(int bufferSize, int limit)
 328                {
 0329                    BufferSize = bufferSize;
 0330                    Limit = limit;
 0331                }
 332
 0333                public int BufferSize { get; }
 334
 0335                public int Limit { get; }
 336
 0337                public int Misses { get; set; }
 338
 0339                public int Peak { get; private set; }
 340
 341                public void Clear()
 342                {
 0343                    OnClear();
 0344                    _count = 0;
 0345                }
 346
 347                public void DecrementCount()
 348                {
 0349                    int newValue = _count - 1;
 0350                    if (newValue >= 0)
 351                    {
 0352                        _count = newValue;
 353                    }
 0354                }
 355
 356                public void IncrementCount()
 357                {
 0358                    int newValue = _count + 1;
 0359                    if (newValue <= Limit)
 360                    {
 0361                        _count = newValue;
 0362                        if (newValue > Peak)
 363                        {
 0364                            Peak = newValue;
 365                        }
 366                    }
 0367                }
 368
 369                internal abstract byte[] Take();
 370                internal abstract bool Return(byte[] buffer);
 371                internal abstract void OnClear();
 372
 373                internal static BufferPool CreatePool(int bufferSize, int limit)
 374                {
 375                    // To avoid many buffer drops during training of large objects which
 376                    // get allocated on the LOH, we use the LargeBufferPool and for
 377                    // bufferSize < 85000, the SynchronizedPool. However if bufferSize < 85000
 378                    // and (bufferSize + array-overhead) > 85000, this would still use
 379                    // the SynchronizedPool even though it is allocated on the LOH.
 0380                    if (bufferSize < 85000)
 381                    {
 0382                        return new SynchronizedBufferPool(bufferSize, limit);
 383                    }
 384                    else
 385                    {
 0386                        return new LargeBufferPool(bufferSize, limit);
 387                    }
 388                }
 389
 390                private class SynchronizedBufferPool : BufferPool
 391                {
 392                    private readonly SynchronizedPool<byte[]> _innerPool;
 393
 394                    internal SynchronizedBufferPool(int bufferSize, int limit)
 0395                        : base(bufferSize, limit)
 396                    {
 0397                        _innerPool = new SynchronizedPool<byte[]>(limit);
 0398                    }
 399
 400                    internal override void OnClear()
 401                    {
 0402                        _innerPool.Clear();
 0403                    }
 404
 405                    internal override byte[] Take()
 406                    {
 0407                        return _innerPool.Take();
 408                    }
 409
 410                    internal override bool Return(byte[] buffer)
 411                    {
 0412                        return _innerPool.Return(buffer);
 413                    }
 414                }
 415
 416                private class LargeBufferPool : BufferPool
 417                {
 418                    private readonly Stack<byte[]> _items;
 419
 420                    internal LargeBufferPool(int bufferSize, int limit)
 0421                        : base(bufferSize, limit)
 422                    {
 0423                        _items = new Stack<byte[]>(limit);
 0424                    }
 425
 426                    private object ThisLock
 427                    {
 428                        get
 429                        {
 0430                            return _items;
 431                        }
 432                    }
 433
 434                    internal override void OnClear()
 435                    {
 0436                        lock (ThisLock)
 437                        {
 0438                            _items.Clear();
 0439                        }
 0440                    }
 441
 442                    internal override byte[] Take()
 443                    {
 0444                        lock (ThisLock)
 445                        {
 0446                            if (_items.Count > 0)
 447                            {
 0448                                return _items.Pop();
 449                            }
 0450                        }
 451
 0452                        return null;
 0453                    }
 454
 455                    internal override bool Return(byte[] buffer)
 456                    {
 0457                        lock (ThisLock)
 458                        {
 0459                            if (_items.Count < Limit)
 460                            {
 0461                                _items.Push(buffer);
 0462                                return true;
 463                            }
 0464                        }
 465
 0466                        return false;
 0467                    }
 468                }
 469            }
 470        }
 471
 472        private class GCBufferManager : InternalBufferManager
 473        {
 0474            private GCBufferManager()
 475            {
 0476            }
 477
 0478            public static GCBufferManager Value { get; } = new GCBufferManager();
 479
 480            public override void Clear()
 481            {
 0482            }
 483
 484            public override byte[] TakeBuffer(int bufferSize)
 485            {
 0486                return Fx.AllocateByteArray(bufferSize);
 487            }
 488
 489            public override void ReturnBuffer(byte[] buffer)
 490            {
 491                // do nothing, GC will reclaim this buffer
 0492            }
 493        }
 494    }
 495}