protected RetainableMemory(AtomicCounter counter) { if (counter.IsValid) { if (counter.Count != 0) { ThrowHelper.ThrowArgumentException("counter.Count != 0"); } } Counter = counter; }
protected override void Dispose(bool disposing) { if (ExternallyOwned) { ThrowHelper.ThrowNotSupportedException(); } if (disposing) { var pool = Pool; if (pool != null) { pool.ReturnInternal(this, clearMemory: !TypeHelper <T> .IsPinnable); // pool calls Dispose(false) if a bucket is full return; } // not pooled, doing finalization work now GC.SuppressFinalize(this); } // Finalization AtomicCounter.Dispose(ref CounterRef); Debug.Assert(!_isPooled); _poolIdx = default; // we still could add this to the pool of free pinned slices that are backed by an existing slab var pooledToFreeSlicesPool = _slicesPool.Return(this); if (pooledToFreeSlicesPool) { return; } var array = Interlocked.Exchange(ref _array, null); if (array != null) { ClearAfterDispose(); Debug.Assert(_handle.IsAllocated); #pragma warning disable 618 _slab.Decrement(); _handle.Free(); #pragma warning restore 618 } else { ThrowDisposed <ArrayMemorySlice <T> >(); } Debug.Assert(!_handle.IsAllocated); }
internal void Return(RetainableMemory <T> memory) { // Check to see if the buffer is the correct size for this bucket if (memory.LengthPow2 != _bufferLength) { ThrowNotFromPool <RetainableMemory <T> >(); } // While holding the spin lock, if there's room available in the bucket, // put the buffer into the next available slot. Otherwise, we just drop it. // The try/finally is necessary to properly handle thread aborts on platforms // which have them. int disposed = 0; #if !NETCOREAPP try #endif { var spinner = new SpinWait(); while (0 != Interlocked.CompareExchange(ref _locker, 1, 0)) { spinner.SpinOnce(); } var pooled = _index != 0; if (pooled) { if ((disposed = AtomicCounter.TryDispose(ref memory.CounterRef)) == 0) { _buffers[--_index] = memory; Debug.Assert(AtomicCounter.GetIsDisposed(ref memory.CounterRef)); memory._isPooled = true; } } else { memory.DisposeFinalize(); } } #if !NETCOREAPP finally #endif { Volatile.Write(ref _locker, 0); } // after unlock if (disposed != 0) { AtomicCounter.ThrowNonZeroTryDispose(disposed); } }
private static ObjectPool <PrivateMemory <T> > CreateObjectPool() { var op = new ObjectPool <PrivateMemory <T> >(() => { var pm = new PrivateMemory <T>(); AtomicCounter.Dispose(ref pm.CounterRef); return(pm); }, Settings.PrivateMemoryPerCorePoolSize ?? PerCorePoolSize); // Need to touch these fields very early in a common not hot place for JIT static // readonly optimization even if tiered compilation is off. // Note single & to avoid short circuit. if (AdditionalCorrectnessChecks.Enabled & TypeHelper <T> .IsReferenceOrContainsReferences) { ThrowHelper.Assert(!op.IsDisposed); } return(op); }
public RetainableMemoryPool(Func <RetainableMemoryPool <T>, int, RetainableMemory <T> > factory, int minLength = DefaultMinBufferLength, int maxLength = DefaultMaxBufferLength, int maxBuffersPerBucketPerCore = DefaultMaxNumberOfBuffersPerBucketPerCore, int maxBucketsToTry = 2, bool rentAlwaysClean = false) { if (factory == null) { throw new ArgumentNullException(nameof(factory)); } lock (KnownPools) { // Start from 2, other code depends on the first 2 items being null. // pool idx == 0 is always null which means a buffer is not from pool // pool idx == 1 means a buffer is from default pool, e.g. static array pool for (int i = 2; i < KnownPools.Length; i++) { if (KnownPools[i] == null) { PoolIdx = checked ((byte)i); KnownPools[i] = this; break; } } if (PoolIdx == 0) { ThrowHelper.ThrowInvalidOperationException("KnownPools slots exhausted. 64 pools ought to be enough for anybody."); } } IsRentAlwaysClean = rentAlwaysClean; Factory = (length) => { var memory = factory(this, length); if (IsRentAlwaysClean) { memory.GetSpan().Clear(); } AtomicCounter.Dispose(ref memory.CounterRef); ThrowHelper.DebugAssert(memory.IsPooled); return(memory); }; if (minLength <= 16) { minLength = 16; } _minBufferLengthPow2 = 32 - BitUtil.NumberOfLeadingZeros(minLength - 1); MinBufferLength = 1 << _minBufferLengthPow2; if (maxBucketsToTry < 0) { maxBucketsToTry = 0; } MaxBucketsToTry = maxBucketsToTry; if (maxLength <= 0) { throw new ArgumentOutOfRangeException(nameof(maxLength)); } if (maxBuffersPerBucketPerCore < 0) { throw new ArgumentOutOfRangeException(nameof(maxBuffersPerBucketPerCore)); } // Our bucketing algorithm has a min length of 2^4 and a max length of 2^30. // Constrain the actual max used to those values. const int maximumBufferLength = 0x40000000; if (maxLength > maximumBufferLength) { maxLength = maximumBufferLength; } else if (maxLength < minLength) { maxLength = minLength; } MaxBufferLength = maxLength; // Create the buckets. int maxBuckets = SelectBucketIndex(maxLength); var buckets = new MemoryBucket[maxBuckets + 1]; for (int i = 0; i < buckets.Length; i++) { buckets[i] = new MemoryBucket(this, GetMaxSizeForBucket(i), maxBuffersPerBucketPerCore); } _buckets = buckets; }