diff options
author | Miguel de Icaza <miguel@gnome.org> | 2010-04-08 02:16:02 +0400 |
---|---|---|
committer | Miguel de Icaza <miguel@gnome.org> | 2010-04-08 02:16:02 +0400 |
commit | 6061c13a4f84c153cc54b1b739e2cc379ce1be0a (patch) | |
tree | 5d8e1168948942ae6bb08462c8401ea97f06c5e3 /mcs/class | |
parent | b7d40582ca5767a3484548c5ae3e7af23453e7e0 (diff) |
Implement BufferManager.cs
svn path=/branches/mono-2-6/mcs/; revision=154998
Diffstat (limited to 'mcs/class')
-rw-r--r-- | mcs/class/System.ServiceModel/System.ServiceModel.Channels/BufferManager.cs | 172 |
1 files changed, 152 insertions, 20 deletions
diff --git a/mcs/class/System.ServiceModel/System.ServiceModel.Channels/BufferManager.cs b/mcs/class/System.ServiceModel/System.ServiceModel.Channels/BufferManager.cs index 9078b9d1cdd..ae295d08b0a 100644 --- a/mcs/class/System.ServiceModel/System.ServiceModel.Channels/BufferManager.cs +++ b/mcs/class/System.ServiceModel/System.ServiceModel.Channels/BufferManager.cs @@ -1,9 +1,38 @@ // -// BufferManager.cs +// BufferManager.cs: +// This class suffers from an engineering problem in its +// design: when this API is used to limit the total pool +// size it will throw, but no user code is designed to +// cope with that. // -// Author: Atsushi Enomoto (atsushi@ximian.com) +// Instead of the Microsoft strategy, we allow allocation +// to go as far as it wants to go and merely allow this +// to be a pool that can be used recycle buffers. // -// Copyright (C) 2005 Novell, Inc (http://www.novell.com) +// This still gives us the main benefit of this class, while +// avoiding the potential crashing scenarios and simplifies +// the implementation significantly from what has been +// document in the blogosphere. +// +// There are a few problems: for example, if we do not find +// a buffer of the proper size in the expected slot, say +// a 31k buffer in the slot for [32k-64k] values, we will +// allocate a new buffer, even if there might have been a +// buffer for 128k. +// +// A few considerations: +// +// The size of an empty array is either 16 on 32 bit systems +// and 32 bytes in 64 bit systems. +// +// We take this information into account for the minimum allocation +// pools. +// +// Authors: +// Atsushi Enomoto (atsushi@ximian.com) +// Miguel de Icaza (miguel@gnome.org) +// +// Copyright (C) 2005, 2010 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the @@ -25,6 +54,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; +using System.Collections.Generic; using System.IO; using System.Collections.ObjectModel; using System.ServiceModel; @@ -39,26 +69,71 @@ namespace System.ServiceModel.Channels public abstract void Clear (); - [MonoTODO] public static BufferManager CreateBufferManager ( long maxBufferPoolSize, int maxBufferSize) { - return new DefaultBufferManager (maxBufferPoolSize, - maxBufferSize); + return new DefaultBufferManager (maxBufferPoolSize, maxBufferSize); } public abstract void ReturnBuffer (byte[] buffer); public abstract byte[] TakeBuffer (int bufferSize); +#if DEBUG_BUFFER + internal abstract void DumpStats (); +#endif + class DefaultBufferManager : BufferManager { + const int log_min = 5; // Anything smaller than 1 << log_cut goes into the first bucket long max_pool_size; int max_size; - byte [] buffer; + List<byte []> [] buffers = new List<byte []> [32-log_min]; - public DefaultBufferManager (long maxBufferPoolSize, - int maxBufferSize) +#if DEBUG_BUFFER + internal override void DumpStats () + { + Console.WriteLine ("- hit={0} miss={1}-", hits, miss); + for (int i = 0; i < buffers.Length; i++){ + if (buffers [i] == null) + continue; + + Console.Write ("Slot {0} - {1} [", i, buffers [i].Count); + byte [][] arr = buffers [i].ToArray (); + + for (int j = 0; j < Math.Min (3, arr.Length); j++) + Console.Write ("{0} ", arr [j].Length); + Console.WriteLine ("]"); + } + } +#endif + + static int log2 (uint n) + { + int pos = 0; + if (n >= 1<<16) { + n >>= 16; + pos += 16; + } + if (n >= 1<< 8) { + n >>= 8; + pos += 8; + } + if (n >= 1<< 4) { + n >>= 4; + pos += 4; + } + if (n >= 1<< 2) { + n >>= 2; + pos += 2; + } + if (n >= 1<< 1) + pos += 1; + + return ((n == 0) ? (-1) : pos); + } + + public DefaultBufferManager (long maxBufferPoolSize, int maxBufferSize) { this.max_pool_size = maxBufferPoolSize; this.max_size = maxBufferSize; @@ -66,28 +141,85 @@ namespace System.ServiceModel.Channels public override void Clear () { - if (buffer != null) - Array.Clear (buffer, 0, buffer.Length); + foreach (var stack in buffers){ + if (stack == null) + continue; + stack.Clear (); + } + Array.Clear (buffers, 0, buffers.Length); } public override void ReturnBuffer (byte [] buffer) { - // is this correct? - - if (this.buffer == null) + if (buffer == null) return; - Array.Copy (this.buffer, buffer, this.buffer.Length); + + uint size = (uint) buffer.Length; + int l2 = log2 (size); + if (l2 > log_min) + l2 -= log_min; + + List<byte []> returned = buffers [l2]; + if (returned == null) + returned = buffers [l2] = new List<byte []> (); + + returned.Add (buffer); } + int hits, miss; + public override byte [] TakeBuffer (int bufferSize) { - if (bufferSize > max_size) + if (bufferSize < 0 || (max_size >= 0 && bufferSize > max_size)) throw new ArgumentOutOfRangeException (); - if (buffer == null || buffer.Length < bufferSize) - buffer = new byte [bufferSize]; - return buffer; + int l2 = log2 ((uint) bufferSize); + if (l2 > log_min) + l2 -= log_min; + + List<byte []> returned = buffers [l2]; + if (returned == null || returned.Count == 0) + return new byte [bufferSize]; + + foreach (var e in returned){ + if (e.Length >= bufferSize){ + hits++; + returned.Remove (e); + return e; + } + } + return new byte [bufferSize]; + } + } + } + +#if DEBUG_BUFFER + class Foo { + static void Main () + { + var a = BufferManager.CreateBufferManager (1024*1024, 1024*1024); + var rand = new Random (0); + + var buffs = new List<byte []> (); + for (int i = 0; i < 4096; i++){ + a.DumpStats (); + var request = rand.Next (1,1024*1024); + if ((i % 2) == 0) + request = rand.Next (1024, 4096); + + var x = a.TakeBuffer (request); + if (x.Length < request) + throw new Exception (); + Console.WriteLine ("Delta={2} Requested {0} got={1} bytes ", request, x.Length, x.Length-request); + if ((i % 3) == 0){ + Console.WriteLine ("Return: {0}", x.Length); + a.ReturnBuffer (x); + } + else + buffs.Add (x); } + a.DumpStats (); } } -} +#endif +}
\ No newline at end of file |