e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
426 lines
11 KiB
C#
426 lines
11 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="BufferAllocator.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
/*
|
|
* Buffer Allocators with recycling
|
|
*
|
|
* Copyright (c) 1999 Microsoft Corporation
|
|
*/
|
|
namespace System.Web {
|
|
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Globalization;
|
|
|
|
using System.Web.Util;
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Generic buffer recycling
|
|
|
|
internal interface IBufferAllocator {
|
|
object GetBuffer();
|
|
void ReuseBuffer(object buffer);
|
|
void ReleaseAllBuffers();
|
|
int BufferSize { get; }
|
|
}
|
|
|
|
internal interface IBufferAllocator<T> : IBufferAllocator {
|
|
new T[] GetBuffer();
|
|
T[] GetBuffer(int minSize);
|
|
void ReuseBuffer(T[] buffer);
|
|
}
|
|
|
|
internal interface IAllocatorProvider {
|
|
IBufferAllocator<char> CharBufferAllocator { get; }
|
|
IBufferAllocator<int> IntBufferAllocator { get; }
|
|
IBufferAllocator<IntPtr> IntPtrBufferAllocator { get; }
|
|
|
|
void TrimMemory();
|
|
}
|
|
|
|
/*
|
|
* Base class for allocator doing buffer recycling
|
|
*/
|
|
internal abstract class BufferAllocator : IBufferAllocator {
|
|
private int _maxFree;
|
|
private int _numFree;
|
|
private Stack _buffers;
|
|
|
|
private static int s_ProcsFudgeFactor;
|
|
|
|
static BufferAllocator() {
|
|
s_ProcsFudgeFactor = SystemInfo.GetNumProcessCPUs();
|
|
if (s_ProcsFudgeFactor < 1)
|
|
s_ProcsFudgeFactor = 1;
|
|
|
|
if (s_ProcsFudgeFactor > 4)
|
|
s_ProcsFudgeFactor = 4;
|
|
}
|
|
|
|
|
|
internal BufferAllocator(int maxFree) {
|
|
_buffers = new Stack();
|
|
_numFree = 0;
|
|
_maxFree = maxFree * s_ProcsFudgeFactor;
|
|
}
|
|
|
|
public void ReleaseAllBuffers() {
|
|
if (_numFree > 0) {
|
|
lock (this) {
|
|
_buffers.Clear();
|
|
_numFree = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
public object GetBuffer() {
|
|
Object buffer = null;
|
|
|
|
if (_numFree > 0) {
|
|
lock(this) {
|
|
if (_numFree > 0) {
|
|
buffer = _buffers.Pop();
|
|
_numFree--;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (buffer == null)
|
|
buffer = AllocBuffer();
|
|
|
|
return buffer;
|
|
}
|
|
|
|
public void ReuseBuffer(object buffer) {
|
|
if (_numFree < _maxFree) {
|
|
lock(this) {
|
|
if (_numFree < _maxFree) {
|
|
_buffers.Push(buffer);
|
|
_numFree++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* To be implemented by a derived class
|
|
*/
|
|
abstract protected Object AllocBuffer();
|
|
abstract public int BufferSize { get; }
|
|
}
|
|
|
|
/*
|
|
* Concrete allocator class for ubyte[] buffer recycling
|
|
*/
|
|
internal class UbyteBufferAllocator : BufferAllocator {
|
|
private int _bufferSize;
|
|
|
|
internal UbyteBufferAllocator(int bufferSize, int maxFree) : base(maxFree) {
|
|
_bufferSize = bufferSize;
|
|
}
|
|
|
|
protected override Object AllocBuffer() {
|
|
return new byte[_bufferSize];
|
|
}
|
|
|
|
public override int BufferSize {
|
|
get {
|
|
return _bufferSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Concrete allocator class for char[] buffer recycling
|
|
*/
|
|
internal class CharBufferAllocator : BufferAllocator {
|
|
private int _bufferSize;
|
|
|
|
internal CharBufferAllocator(int bufferSize, int maxFree)
|
|
|
|
: base(maxFree) {
|
|
_bufferSize = bufferSize;
|
|
}
|
|
|
|
protected override Object AllocBuffer() {
|
|
return new char[_bufferSize];
|
|
}
|
|
|
|
public override int BufferSize {
|
|
get {
|
|
return _bufferSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Concrete allocator class for int[] buffer recycling
|
|
*/
|
|
internal class IntegerArrayAllocator : BufferAllocator {
|
|
private int _arraySize;
|
|
|
|
internal IntegerArrayAllocator(int arraySize, int maxFree)
|
|
|
|
: base(maxFree) {
|
|
_arraySize = arraySize;
|
|
}
|
|
|
|
protected override Object AllocBuffer() {
|
|
return new int[_arraySize];
|
|
}
|
|
|
|
public override int BufferSize {
|
|
get {
|
|
return _arraySize;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Concrete allocator class for IntPtr[] buffer recycling
|
|
*/
|
|
internal class IntPtrArrayAllocator : BufferAllocator {
|
|
private int _arraySize;
|
|
|
|
internal IntPtrArrayAllocator(int arraySize, int maxFree)
|
|
|
|
: base(maxFree) {
|
|
_arraySize = arraySize;
|
|
}
|
|
|
|
protected override Object AllocBuffer() {
|
|
return new IntPtr[_arraySize];
|
|
}
|
|
|
|
public override int BufferSize {
|
|
get {
|
|
return _arraySize;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Simple Buffer Allocator - Reusable buffers pool
|
|
* Thread UNSAFE! Lock free. Caller must guarantee non-concurent access.
|
|
* Use as member of already pooled instances (like HttpApplication) that prohibit concurent access to avoid taking locks
|
|
*/
|
|
internal class SimpleBufferAllocator<T> : IBufferAllocator<T> {
|
|
private Stack<T[]> _buffers;
|
|
private readonly int _bufferSize;
|
|
|
|
public SimpleBufferAllocator(int bufferSize) {
|
|
if (bufferSize <= 0) {
|
|
throw new ArgumentOutOfRangeException("bufferSize");
|
|
}
|
|
|
|
_buffers = new Stack<T[]>();
|
|
_bufferSize = bufferSize;
|
|
}
|
|
|
|
public T[] GetBuffer() {
|
|
return GetBufferImpl();
|
|
}
|
|
|
|
public T[] GetBuffer(int minSize) {
|
|
if (minSize < 0) {
|
|
throw new ArgumentOutOfRangeException("minSize");
|
|
}
|
|
|
|
T[] buffer = null;
|
|
|
|
if (minSize <= BufferSize) {
|
|
// Get from the pool
|
|
buffer = GetBufferImpl();
|
|
}
|
|
else {
|
|
// Allocate a new buffer. It will not be reused later
|
|
buffer = AllocBuffer(minSize);
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
object IBufferAllocator.GetBuffer() {
|
|
return GetBufferImpl();
|
|
}
|
|
|
|
public void ReuseBuffer(T[] buffer) {
|
|
ReuseBufferImpl(buffer);
|
|
}
|
|
|
|
void IBufferAllocator.ReuseBuffer(object buffer) {
|
|
ReuseBufferImpl((T[]) buffer);
|
|
}
|
|
|
|
public void ReleaseAllBuffers() {
|
|
_buffers.Clear();
|
|
}
|
|
|
|
public int BufferSize {
|
|
get {
|
|
return _bufferSize;
|
|
}
|
|
}
|
|
|
|
private T[] GetBufferImpl() {
|
|
T[] buffer = null;
|
|
|
|
if (_buffers.Count > 0) {
|
|
// Get an exisitng buffer
|
|
buffer = _buffers.Pop();
|
|
}
|
|
else {
|
|
// Create a new buffer
|
|
buffer = AllocBuffer(BufferSize);
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
private void ReuseBufferImpl(T[] buffer) {
|
|
// Accept back only buffers that match the orirignal buffer size
|
|
if (buffer != null && buffer.Length == BufferSize) {
|
|
_buffers.Push(buffer);
|
|
}
|
|
}
|
|
|
|
private static T[] AllocBuffer(int size) {
|
|
return new T[size];
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Adapter to convert IBufferAllocator to generic IBufferAllocator<>
|
|
*/
|
|
class BufferAllocatorWrapper<T> : IBufferAllocator<T> {
|
|
private IBufferAllocator _allocator;
|
|
|
|
public BufferAllocatorWrapper(IBufferAllocator allocator) {
|
|
Debug.Assert(allocator != null);
|
|
|
|
_allocator = allocator;
|
|
}
|
|
|
|
public T[] GetBuffer() {
|
|
return (T[])_allocator.GetBuffer();
|
|
}
|
|
|
|
public T[] GetBuffer(int minSize) {
|
|
if (minSize < 0) {
|
|
throw new ArgumentOutOfRangeException("minSize");
|
|
}
|
|
|
|
T[] buffer = null;
|
|
|
|
if (minSize <= BufferSize) {
|
|
// Get from the allocator
|
|
buffer = (T[])_allocator.GetBuffer();
|
|
}
|
|
else {
|
|
// Allocate a new buffer. It will not be reused later
|
|
buffer = new T[minSize];
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
public void ReuseBuffer(T[] buffer) {
|
|
// Accept back only buffers that match the orirignal buffer size
|
|
if (buffer != null && buffer.Length == BufferSize) {
|
|
_allocator.ReuseBuffer(buffer);
|
|
}
|
|
}
|
|
|
|
object IBufferAllocator.GetBuffer() {
|
|
return _allocator.GetBuffer();
|
|
}
|
|
|
|
void IBufferAllocator.ReuseBuffer(object buffer) {
|
|
ReuseBuffer((T[])buffer);
|
|
}
|
|
|
|
public void ReleaseAllBuffers() {
|
|
_allocator.ReleaseAllBuffers();
|
|
}
|
|
|
|
public int BufferSize {
|
|
get {
|
|
return _allocator.BufferSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Provider for different buffer allocators
|
|
*/
|
|
internal class AllocatorProvider : IAllocatorProvider {
|
|
private IBufferAllocator<char> _charAllocator = null;
|
|
private IBufferAllocator<int> _intAllocator = null;
|
|
private IBufferAllocator<IntPtr> _intPtrAllocator = null;
|
|
|
|
public IBufferAllocator<char> CharBufferAllocator {
|
|
get {
|
|
return _charAllocator;
|
|
}
|
|
|
|
set {
|
|
if (value == null) {
|
|
throw new ArgumentNullException("value");
|
|
}
|
|
|
|
_charAllocator = value;
|
|
}
|
|
}
|
|
|
|
public IBufferAllocator<int> IntBufferAllocator {
|
|
get {
|
|
return _intAllocator;
|
|
}
|
|
|
|
set {
|
|
if (value == null) {
|
|
throw new ArgumentNullException("value");
|
|
}
|
|
|
|
_intAllocator = value;
|
|
}
|
|
}
|
|
|
|
public IBufferAllocator<IntPtr> IntPtrBufferAllocator {
|
|
get {
|
|
return _intPtrAllocator;
|
|
}
|
|
|
|
set {
|
|
if (value == null) {
|
|
throw new ArgumentNullException("value");
|
|
}
|
|
|
|
_intPtrAllocator = value;
|
|
}
|
|
}
|
|
|
|
public void TrimMemory() {
|
|
if (_charAllocator != null) {
|
|
_charAllocator.ReleaseAllBuffers();
|
|
}
|
|
|
|
if (_intAllocator != null) {
|
|
_intAllocator.ReleaseAllBuffers();
|
|
}
|
|
|
|
if (_intPtrAllocator != null) {
|
|
_intPtrAllocator.ReleaseAllBuffers();
|
|
}
|
|
}
|
|
}
|
|
}
|