< Summary - CoreWCF Coverage — PR #1766

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

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor()100%11100%
Create(...)100%22100%
.ctor(...)90%1010100%
Clear()0%220%
ChangeQuota(...)100%44100%
DecreaseQuota(...)100%11100%
FindMostExcessivePool()100%66100%
FindMostStarvedPool()100%66100%
FindPool(...)100%44100%
IncreaseQuota(...)100%11100%
ReturnBuffer(...)83.33%6685.71%
TakeBuffer(...)87.5%8892.3%
TuneQuotas()83.33%181892.3%
.ctor(...)100%11100%
Clear()100%110%
DecrementCount()100%22100%
IncrementCount()100%44100%
CreatePool(...)100%22100%
.ctor(...)100%11100%
OnClear()100%110%
Take()100%11100%
Return(...)100%11100%
.ctor(...)100%11100%
OnClear()100%110%
Take()100%22100%
Return(...)100%22100%
.ctor()100%11100%
Clear()100%110%
TakeBuffer(...)100%11100%
ReturnBuffer(...)100%110%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/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    {
 73511        protected InternalBufferManager()
 12        {
 73513        }
 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        {
 371121            if (maxBufferPoolSize == 0)
 22            {
 316023                return GCBufferManager.Value;
 24            }
 25            else
 26            {
 27                Fx.Assert(maxBufferPoolSize > 0 && maxBufferSize >= 0, "bad params, caller should verify");
 55128                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
 55145            public PooledBufferManager(long maxMemoryToPool, int maxBufferSize)
 46            {
 55147                _tuningLock = new object();
 55148                _memoryLimit = maxMemoryToPool;
 55149                _remainingMemory = maxMemoryToPool;
 55150                List<BufferPool> bufferPoolList = new List<BufferPool>();
 51
 55152                for (int bufferSize = minBufferSize; ;)
 53                {
 572754                    long bufferCountLong = _remainingMemory / bufferSize;
 55
 572756                    int bufferCount = bufferCountLong > int.MaxValue ? int.MaxValue : (int)bufferCountLong;
 57
 572758                    if (bufferCount > initialBufferCount)
 59                    {
 551960                        bufferCount = initialBufferCount;
 61                    }
 62
 572763                    bufferPoolList.Add(BufferPool.CreatePool(bufferSize, bufferCount));
 64
 572765                    _remainingMemory -= (long)bufferCount * bufferSize;
 66
 572767                    if (bufferSize >= maxBufferSize)
 68                    {
 69                        break;
 70                    }
 71
 517672                    long newBufferSizeLong = (long)bufferSize * 2;
 73
 517674                    if (newBufferSizeLong > (long)maxBufferSize)
 75                    {
 2476                        bufferSize = maxBufferSize;
 77                    }
 78                    else
 79                    {
 515280                        bufferSize = (int)newBufferSizeLong;
 81                    }
 82                }
 83
 55184                _bufferPools = bufferPoolList.ToArray();
 55185                _bufferSizes = new int[_bufferPools.Length];
 1255686                for (int i = 0; i < _bufferPools.Length; i++)
 87                {
 572788                    _bufferSizes[i] = _bufferPools[i].BufferSize;
 89                }
 55190            }
 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
 7108                BufferPool oldBufferPool = bufferPool;
 7109                int newLimit = oldBufferPool.Limit + delta;
 7110                BufferPool newBufferPool = BufferPool.CreatePool(oldBufferPool.BufferSize, newLimit);
 20111                for (int i = 0; i < newLimit; i++)
 112                {
 9113                    byte[] buffer = oldBufferPool.Take();
 9114                    if (buffer == null)
 115                    {
 116                        break;
 117                    }
 3118                    newBufferPool.Return(buffer);
 3119                    newBufferPool.IncrementCount();
 120                }
 7121                _remainingMemory -= oldBufferPool.BufferSize * delta;
 7122                bufferPool = newBufferPool;
 7123            }
 124
 125            private void DecreaseQuota(ref BufferPool bufferPool)
 126            {
 1127                ChangeQuota(ref bufferPool, -1);
 1128            }
 129
 130            private int FindMostExcessivePool()
 131            {
 1132                long maxBytesInExcess = 0;
 1133                int index = -1;
 134
 52135                for (int i = 0; i < _bufferPools.Length; i++)
 136                {
 25137                    BufferPool bufferPool = _bufferPools[i];
 138
 25139                    if (bufferPool.Peak < bufferPool.Limit)
 140                    {
 6141                        long bytesInExcess = (bufferPool.Limit - bufferPool.Peak) * (long)bufferPool.BufferSize;
 142
 6143                        if (bytesInExcess > maxBytesInExcess)
 144                        {
 6145                            index = i;
 6146                            maxBytesInExcess = bytesInExcess;
 147                        }
 148                    }
 149                }
 150
 1151                return index;
 152            }
 153
 154            private int FindMostStarvedPool()
 155            {
 8156                long maxBytesMissed = 0;
 8157                int index = -1;
 158
 206159                for (int i = 0; i < _bufferPools.Length; i++)
 160                {
 95161                    BufferPool bufferPool = _bufferPools[i];
 162
 95163                    if (bufferPool.Peak == bufferPool.Limit)
 164                    {
 30165                        long bytesMissed = bufferPool.Misses * (long)bufferPool.BufferSize;
 166
 30167                        if (bytesMissed > maxBytesMissed)
 168                        {
 11169                            index = i;
 11170                            maxBytesMissed = bytesMissed;
 171                        }
 172                    }
 173                }
 174
 8175                return index;
 176            }
 177
 178            private BufferPool FindPool(int desiredBufferSize)
 179            {
 44680180                for (int i = 0; i < _bufferSizes.Length; i++)
 181                {
 22339182                    if (desiredBufferSize <= _bufferSizes[i])
 183                    {
 4246184                        return _bufferPools[i];
 185                    }
 186                }
 187
 1188                return null;
 189            }
 190
 191            private void IncreaseQuota(ref BufferPool bufferPool)
 192            {
 6193                ChangeQuota(ref bufferPool, 1);
 6194            }
 195
 196            public override void ReturnBuffer(byte[] buffer)
 197            {
 198                Fx.Assert(buffer != null, "caller must verify");
 2063199                BufferPool bufferPool = FindPool(buffer.Length);
 2063200                if (bufferPool != null)
 201                {
 2062202                    if (buffer.Length != bufferPool.BufferSize)
 203                    {
 0204                        throw Fx.Exception.Argument(nameof(buffer), SR.BufferIsNotRightSizeForBufferManager);
 205                    }
 206
 2062207                    if (bufferPool.Return(buffer))
 208                    {
 1781209                        bufferPool.IncrementCount();
 210                    }
 211                }
 2063212            }
 213
 214            public override byte[] TakeBuffer(int bufferSize)
 215            {
 216                Fx.Assert(bufferSize >= 0, "caller must ensure a non-negative argument");
 217
 2184218                BufferPool bufferPool = FindPool(bufferSize);
 219                byte[] returnValue;
 2184220                if (bufferPool != null)
 221                {
 2184222                    byte[] buffer = bufferPool.Take();
 2184223                    if (buffer != null)
 224                    {
 894225                        bufferPool.DecrementCount();
 894226                        returnValue = buffer;
 227                    }
 228                    else
 229                    {
 1290230                        if (bufferPool.Peak == bufferPool.Limit)
 231                        {
 92232                            bufferPool.Misses++;
 92233                            if (++_totalMisses >= maxMissesBeforeTuning)
 234                            {
 8235                                TuneQuotas();
 236                            }
 237                        }
 238
 239                        //if (TraceCore.BufferPoolAllocationIsEnabled(Fx.Trace))
 240                        //{
 241                        //    TraceCore.BufferPoolAllocation(Fx.Trace, bufferPool.BufferSize);
 242                        //}
 243
 1290244                        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
 2184257                return returnValue;
 258            }
 259
 260            private void TuneQuotas()
 261            {
 8262                if (_areQuotasBeingTuned)
 263                {
 0264                    return;
 265                }
 266
 8267                bool lockHeld = false;
 268                try
 269                {
 8270                    Monitor.TryEnter(_tuningLock, ref lockHeld);
 271
 272                    // Don't bother if another thread already has the lock
 8273                    if (!lockHeld || _areQuotasBeingTuned)
 274                    {
 0275                        return;
 276                    }
 277
 8278                    _areQuotasBeingTuned = true;
 8279                }
 280                finally
 281                {
 8282                    if (lockHeld)
 283                    {
 8284                        Monitor.Exit(_tuningLock);
 285                    }
 8286                }
 287
 288                // find the "poorest" pool
 8289                int starvedIndex = FindMostStarvedPool();
 8290                if (starvedIndex >= 0)
 291                {
 7292                    BufferPool starvedBufferPool = _bufferPools[starvedIndex];
 293
 7294                    if (_remainingMemory < starvedBufferPool.BufferSize)
 295                    {
 296                        // find the "richest" pool
 1297                        int excessiveIndex = FindMostExcessivePool();
 1298                        if (excessiveIndex >= 0)
 299                        {
 300                            // steal from the richest
 1301                            DecreaseQuota(ref _bufferPools[excessiveIndex]);
 302                        }
 303                    }
 304
 7305                    if (_remainingMemory >= starvedBufferPool.BufferSize)
 306                    {
 307                        // give to the poorest
 6308                        IncreaseQuota(ref _bufferPools[starvedIndex]);
 309                    }
 310                }
 311
 312                // reset statistics
 206313                for (int i = 0; i < _bufferPools.Length; i++)
 314                {
 95315                    BufferPool bufferPool = _bufferPools[i];
 95316                    bufferPool.Misses = 0;
 317                }
 318
 8319                _totalMisses = 0;
 8320                _areQuotasBeingTuned = false;
 8321            }
 322
 323            private abstract class BufferPool
 324            {
 325                private int _count;
 326
 5734327                public BufferPool(int bufferSize, int limit)
 328                {
 5734329                    BufferSize = bufferSize;
 5734330                    Limit = limit;
 5734331                }
 332
 9176333                public int BufferSize { get; }
 334
 3502335                public int Limit { get; }
 336
 309337                public int Misses { get; set; }
 338
 4115339                public int Peak { get; private set; }
 340
 341                public void Clear()
 342                {
 0343                    OnClear();
 0344                    _count = 0;
 0345                }
 346
 347                public void DecrementCount()
 348                {
 894349                    int newValue = _count - 1;
 894350                    if (newValue >= 0)
 351                    {
 894352                        _count = newValue;
 353                    }
 894354                }
 355
 356                public void IncrementCount()
 357                {
 1784358                    int newValue = _count + 1;
 1784359                    if (newValue <= Limit)
 360                    {
 1784361                        _count = newValue;
 1784362                        if (newValue > Peak)
 363                        {
 915364                            Peak = newValue;
 365                        }
 366                    }
 1784367                }
 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.
 5734380                    if (bufferSize < 85000)
 381                    {
 5466382                        return new SynchronizedBufferPool(bufferSize, limit);
 383                    }
 384                    else
 385                    {
 268386                        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)
 5466395                        : base(bufferSize, limit)
 396                    {
 5466397                        _innerPool = new SynchronizedPool<byte[]>(limit);
 5466398                    }
 399
 400                    internal override void OnClear()
 401                    {
 0402                        _innerPool.Clear();
 0403                    }
 404
 405                    internal override byte[] Take()
 406                    {
 1894407                        return _innerPool.Take();
 408                    }
 409
 410                    internal override bool Return(byte[] buffer)
 411                    {
 1770412                        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)
 268421                        : base(bufferSize, limit)
 422                    {
 268423                        _items = new Stack<byte[]>(limit);
 268424                    }
 425
 426                    private object ThisLock
 427                    {
 428                        get
 429                        {
 594430                            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                    {
 299444                        lock (ThisLock)
 445                        {
 299446                            if (_items.Count > 0)
 447                            {
 258448                                return _items.Pop();
 449                            }
 41450                        }
 451
 41452                        return null;
 258453                    }
 454
 455                    internal override bool Return(byte[] buffer)
 456                    {
 295457                        lock (ThisLock)
 458                        {
 295459                            if (_items.Count < Limit)
 460                            {
 266461                                _items.Push(buffer);
 266462                                return true;
 463                            }
 29464                        }
 465
 29466                        return false;
 266467                    }
 468                }
 469            }
 470        }
 471
 472        private class GCBufferManager : InternalBufferManager
 473        {
 6474            private GCBufferManager()
 475            {
 6476            }
 477
 3166478            public static GCBufferManager Value { get; } = new GCBufferManager();
 479
 480            public override void Clear()
 481            {
 0482            }
 483
 484            public override byte[] TakeBuffer(int bufferSize)
 485            {
 4834486                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}