2010-04-07 Miguel de Icaza <miguel@novell.com>
authorMiguel de Icaza <miguel@gnome.org>
Wed, 7 Apr 2010 22:17:32 +0000 (22:17 -0000)
committerMiguel de Icaza <miguel@gnome.org>
Wed, 7 Apr 2010 22:17:32 +0000 (22:17 -0000)
*  BufferManager.cs: Implement the BufferManager.

svn path=/trunk/mcs/; revision=154999

mcs/class/System.ServiceModel/System.ServiceModel.Channels/BufferManager.cs
mcs/class/System.ServiceModel/System.ServiceModel.Channels/ChangeLog

index 9078b9d1cdd368e02a8b65c222577d42b1791466..ae295d08b0ae930f88dd87961ac223d617294bdb 100644 (file)
@@ -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
index 55c2c2dfd923152258a2d7dff4f7057c1b46bdc6..28715c35cb2ef8a47070a0da74e582649b2f1669 100755 (executable)
@@ -1,3 +1,7 @@
+2010-04-07  Miguel de Icaza  <miguel@novell.com>
+
+       *  BufferManager.cs: Implement the BufferManager. 
+
 2010-04-07  Atsushi Enomoto  <atsushi@ximian.com>
 
        * WSSecurityMessageHeader.cs : add FIXME comment.