117 lines
3.7 KiB
C#
117 lines
3.7 KiB
C#
|
//------------------------------------------------------------
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//------------------------------------------------------------
|
||
|
namespace System.ServiceModel.Channels
|
||
|
{
|
||
|
using System.Collections.Generic;
|
||
|
using System.Runtime;
|
||
|
|
||
|
// This is the base object pool class which manages objects in a FIFO queue. The objects are
|
||
|
// created through the provided Func<T> createObjectFunc. The main purpose for this class is
|
||
|
// to get better memory usage for Garbage Collection (GC) when part or all of an object is
|
||
|
// regularly pinned. Constantly creating such objects can cause large Gen0 Heap fragmentation
|
||
|
// and thus high memory usage pressure. The pooled objects are first created in Gen0 heaps and
|
||
|
// would be eventually moved to a more stable segment which would prevent the fragmentation
|
||
|
// to happen.
|
||
|
//
|
||
|
// The objects are created in batches for better localization of the objects. Here are the
|
||
|
// parameters that control the behavior of creation/removal:
|
||
|
//
|
||
|
// batchAllocCount: number of objects to be created at the same time when new objects are needed
|
||
|
//
|
||
|
// createObjectFunc: func delegate that is used to create objects by sub-classes.
|
||
|
//
|
||
|
// maxFreeCount: max number of free objects the queue can store. This is to make sure the memory
|
||
|
// usage is bounded.
|
||
|
//
|
||
|
abstract class QueuedObjectPool<T>
|
||
|
{
|
||
|
Queue<T> objectQueue;
|
||
|
bool isClosed;
|
||
|
int batchAllocCount;
|
||
|
int maxFreeCount;
|
||
|
|
||
|
protected void Initialize(int batchAllocCount, int maxFreeCount)
|
||
|
{
|
||
|
if (batchAllocCount <= 0)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("batchAllocCount"));
|
||
|
}
|
||
|
|
||
|
Fx.Assert(batchAllocCount <= maxFreeCount, "batchAllocCount cannot be greater than maxFreeCount");
|
||
|
this.batchAllocCount = batchAllocCount;
|
||
|
this.maxFreeCount = maxFreeCount;
|
||
|
this.objectQueue = new Queue<T>(batchAllocCount);
|
||
|
}
|
||
|
|
||
|
object ThisLock
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this.objectQueue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public virtual bool Return(T value)
|
||
|
{
|
||
|
lock (ThisLock)
|
||
|
{
|
||
|
if (this.objectQueue.Count < this.maxFreeCount && ! this.isClosed)
|
||
|
{
|
||
|
this.objectQueue.Enqueue(value);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public T Take()
|
||
|
{
|
||
|
lock (ThisLock)
|
||
|
{
|
||
|
Fx.Assert(!this.isClosed, "Cannot take an item from closed QueuedObjectPool");
|
||
|
|
||
|
if (this.objectQueue.Count == 0)
|
||
|
{
|
||
|
AllocObjects();
|
||
|
}
|
||
|
|
||
|
return this.objectQueue.Dequeue();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Close()
|
||
|
{
|
||
|
lock (ThisLock)
|
||
|
{
|
||
|
foreach (T item in this.objectQueue)
|
||
|
{
|
||
|
if (item != null)
|
||
|
{
|
||
|
this.CleanupItem(item);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.objectQueue.Clear();
|
||
|
this.isClosed = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected virtual void CleanupItem(T item)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
protected abstract T Create();
|
||
|
|
||
|
void AllocObjects()
|
||
|
{
|
||
|
Fx.Assert(this.objectQueue.Count == 0, "The object queue must be empty for new allocations");
|
||
|
for (int i = 0; i < batchAllocCount; i++)
|
||
|
{
|
||
|
this.objectQueue.Enqueue(Create());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|