Пример #1
0
        public void TryAllocateTest()
        {
            IMemorySlab target = CreateIMemorySlab();

            //allocate 1 byte
            long         smallLength    = 1;
            IMemoryBlock allocatedBlock = null;
            bool         result1        = target.TryAllocate(smallLength, out allocatedBlock);

            Assert.AreEqual <long>(smallLength, allocatedBlock.Length);
            Assert.AreEqual <long>(0, allocatedBlock.StartLocation);
            Assert.AreEqual <bool>(true, result1);

            //allocate rest of slab
            long         restLength      = totalSize - 1;
            IMemoryBlock allocatedBlock2 = null;
            bool         result2         = target.TryAllocate(restLength, out allocatedBlock2);

            Assert.AreEqual <long>(restLength, allocatedBlock2.Length);
            Assert.AreEqual <long>(1, allocatedBlock2.StartLocation);
            Assert.AreEqual <bool>(true, result2);

            //Now try to allocate another byte and expect it to fail
            IMemoryBlock allocatedBlock3 = null;
            bool         result3         = target.TryAllocate(smallLength, out allocatedBlock3);

            Assert.AreEqual <bool>(false, result3);

            //Clean up
            target.Free(allocatedBlock);
            target.Free(allocatedBlock2);
        }
Пример #2
0
        private object sync_slabList = new object(); //synchronizes access to the array of slabs

        #endregion Fields

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the BufferPool class
        /// </summary>
        /// <param name="slabSize">Length, in bytes, of a slab in the BufferPool</param>
        /// <param name="initialSlabs">Number of slabs to create initially</param>
        /// <param name="subsequentSlabs">Number of additional slabs to create at a time</param>
        public BufferPool(long slabSize, int initialSlabs, int subsequentSlabs)
        {
            if (slabSize < 1) throw new ArgumentException("SlabSize must be equal to or greater than 1");
            if (initialSlabs < 1) throw new ArgumentException("InitialSlabs must be equal to or greater than 1");
            if (subsequentSlabs < 1) throw new ArgumentException("SubsequentSlabs must be equal to or greater than 1");

            this.slabSize = slabSize > MinimumSlabSize ? slabSize : MinimumSlabSize;
            this.initialSlabs = initialSlabs;
            this.subsequentSlabs = subsequentSlabs;

            lock (sync_slabList)
            {
                if (slabs.Count == 0)
                {
                    if (initialSlabs > 1)
                    {
                        Interlocked.Exchange(ref singleSlabPool, 0); //false
                    }
                    else
                    {
                        Interlocked.Exchange(ref singleSlabPool, -1); //true
                    }

                    for (int i = 0; i < initialSlabs; i++)
                    {
                        slabs.Add(new MemorySlab(slabSize, this));
                    }

                    firstSlab = slabs[0];
                }
            }
        }
Пример #3
0
        public void LargestFreeBlockSizeTest()
        {
            IMemorySlab target = CreateIMemorySlab();
            long        actual;

            //LargestFreeBlockSize should be initially the total size of the slab
            actual = target.LargestFreeBlockSize;
            Assert.AreEqual <long>(totalSize, actual);

            //Allocate a small block, LargestFreeBlockSize should be the difference from the total slab size
            IMemoryBlock allocatedBlock;

            target.TryAllocate(4321, out allocatedBlock);
            actual = target.LargestFreeBlockSize;
            Assert.AreEqual <long>(totalSize - 4321, actual);

            //Allocate a bigger block, LargestFreeBlockSize should decrease by that block size
            IMemoryBlock allocatedBlock2;

            target.TryAllocate(19500, out allocatedBlock2);
            actual = target.LargestFreeBlockSize;
            Assert.AreEqual <long>(totalSize - 4321 - 19500, actual);

            //Free the original small allocation
            target.Free(allocatedBlock);

            //LargestFreeBlockSize should be the greater of it's previous and the freed block
            Assert.AreEqual <long>(Math.Max(4321, (totalSize - 4321 - 19500)), actual);

            target.Free(allocatedBlock2);
        }
Пример #4
0
 /// <summary>
 /// Initializes a new instance of the ManagedBuffer class, specifying the slab to be associated with the ManagedBuffer.
 /// This constructor creates an empty (zero-length) buffer.
 /// </summary>
 /// <param name="slab">The Memory Slab to be associated with the ManagedBuffer</param>
 internal ManagedBuffer(IMemorySlab slab)
 {
     if (slab == null) throw new ArgumentNullException("slab");
     memoryBlocks = null;
     this.slabArray = slab.Array;
     size = 0;
 }
Пример #5
0
        public void ArrayTest()
        {
            IMemorySlab target = CreateIMemorySlab();

            byte[] actual;
            actual = target.Array;
            Assert.AreEqual <long>(totalSize, actual.LongLength);
        }
Пример #6
0
        public void SizeTest()
        {
            IMemorySlab target = CreateIMemorySlab();
            long        actual;

            actual = target.Size;
            Assert.AreEqual <long>(totalSize, actual);
        }
Пример #7
0
 /// <summary>
 /// Initializes a new instance of the ManagedBuffer class, specifying the slab to be associated with the ManagedBuffer.
 /// This constructor creates an empty (zero-length) buffer.
 /// </summary>
 /// <param name="slab">The Memory Slab to be associated with the ManagedBuffer</param>
 internal ManagedBuffer(IMemorySlab slab)
 {
     if (slab == null)
     {
         throw new ArgumentNullException("SlabArray");
     }
     memoryBlock    = null;
     this.slabArray = slab.Array;
 }
Пример #8
0
        public void TryAllocateTest3()
        {
            IMemorySlab target = CreateIMemorySlab();

            //allocate invalid length
            long         badLength      = -1;
            IMemoryBlock allocatedBlock = null;
            bool         result1        = target.TryAllocate(badLength, out allocatedBlock);
        }
Пример #9
0
        //Gets a single slab buffer
        private static ManagedBuffer GetNewBuffer(IMemorySlab Slab)
        {
            IMemoryBlock allocatedMemoryBlock;

            Slab.TryAllocate(blockSize, out allocatedMemoryBlock);

            ManagedBuffer target = new ManagedBuffer(new IMemoryBlock[] { allocatedMemoryBlock });

            return(target);
        }
Пример #10
0
        /// <summary>
        /// Initializes a new instance of the MemoryBlock class
        /// </summary>
        /// <param name="startLocation">Offset where memory block starts in slab</param>
        /// <param name="length">Length of memory block</param>
        /// <param name="slab">Slab to be associated with the memory block</param>
        internal MemoryBlock(long startLocation, long length, IMemorySlab slab)
        {
            if (startLocation < 0)
            {
                throw new ArgumentOutOfRangeException("startLocation", "StartLocation must be greater than 0");
            }

            startLoc = startLocation;

            if (length <= 0)
            {
                throw new ArgumentOutOfRangeException("length", "Length must be greater than 0");
            }

            endLoc = startLocation + length - 1;
            this.length = length;
            if (slab == null) throw new ArgumentNullException("slab");
            this.owner = slab;
        }
Пример #11
0
        public void FreeTest()
        {
            IMemorySlab  target = CreateIMemorySlab();
            IMemoryBlock block1, block2, block3, block4, block5, block6;

            target.TryAllocate(10, out block1);
            target.TryAllocate(10, out block2);
            target.TryAllocate(10, out block3);
            target.TryAllocate(10, out block4);
            target.TryAllocate(10, out block5);
            target.TryAllocate(target.LargestFreeBlockSize, out block6);
            //entire slab is now used up

            //Free block1
            target.Free(block1);
            //reassign block1 to be 4 bytes;
            target.TryAllocate(4, out block1);
            //Free space should be 6 bytes now;
            Assert.AreEqual <long>(6, target.LargestFreeBlockSize);
            //Free Block2
            target.Free(block2);
            //Free space should now be 16 bytes;
            Assert.AreEqual <long>(16, target.LargestFreeBlockSize);
            //Free Block5
            target.Free(block5);
            //Free Block4
            target.Free(block4);
            //Largest free space should now be 20 bytes;
            Assert.AreEqual <long>(20, target.LargestFreeBlockSize);
            //Free Block3
            target.Free(block3);
            //Largest free space should now be 20 + 10 + 16 = 46 bytes
            Assert.AreEqual <long>(46, target.LargestFreeBlockSize);
            //Free Block6
            target.Free(block6);
            //Largest free block should be slab size - block1 size
            Assert.AreEqual <long>(target.Size - block1.Length, target.LargestFreeBlockSize);
            //Free Block1
            target.Free(block1);
            //Largest free block should now be slab size
            Assert.AreEqual <long>(target.Size, target.LargestFreeBlockSize);
        }
Пример #12
0
        /// <summary>
        /// Initializes a new instance of the MemoryBlock class
        /// </summary>
        /// <param name="startLocation">Offset where memory block starts in slab</param>
        /// <param name="length">Length of memory block</param>
        /// <param name="slab">Slab to be associated with the memory block</param>
        internal MemoryBlock(long startLocation, long length, IMemorySlab slab)
        {
            if (startLocation < 0)
            {
                throw new ArgumentOutOfRangeException("startLocation", "StartLocation must be greater than 0");
            }

            startLoc = startLocation;

            if (length <= 0)
            {
                throw new ArgumentOutOfRangeException("length", "Length must be greater than 0");
            }

            this.length = length;
            if (slab == null) throw new ArgumentNullException("slab");
            this.owner = slab;

            //TODO: If this class is converted to a struct, consider implementing IComparer, IComparable -- first figure out what sorted dictionary uses those Comparer things for
        }
Пример #13
0
        private int singleSlabPool;                      //-1 or 0, used for faster access if only one slab is available

        /// <summary>
        /// Initializes a new instance of the BufferPool class
        /// </summary>
        /// <param name="slabSize">Length, in bytes, of a slab in the BufferPool</param>
        /// <param name="initialSlabs">Number of slabs to create initially</param>
        /// <param name="subsequentSlabs">Number of additional slabs to create at a time</param>
        public BufferPool(long slabSize, int initialSlabs, int subsequentSlabs)
        {
            if (slabSize < 1)
            {
                throw new ArgumentException("SlabSize must be equal to or greater than 1");
            }
            if (initialSlabs < 1)
            {
                throw new ArgumentException("InitialSlabs must be equal to or greater than 1");
            }
            if (subsequentSlabs < 1)
            {
                throw new ArgumentException("SubsequentSlabs must be equal to or greater than 1");
            }

            this.slabSize        = slabSize > MinimumSlabSize ? slabSize : MinimumSlabSize;
            this.initialSlabs    = initialSlabs;
            this.subsequentSlabs = subsequentSlabs;

            lock (sync_slabList)
            {
                if (slabs.Count == 0)
                {
                    if (initialSlabs > 1)
                    {
                        Interlocked.Exchange(ref singleSlabPool, 0); //false
                    }
                    else
                    {
                        Interlocked.Exchange(ref singleSlabPool, -1); //true
                    }

                    for (int i = 0; i < initialSlabs; i++)
                    {
                        slabs.Add(new MemorySlab(slabSize, this));
                    }

                    firstSlab = slabs[0];
                }
            }
        }
Пример #14
0
        private const int MAX_SEGMENTS_PER_BUFFER = 16;           //Maximum number of segments in a buffer.

        /// <summary>
        /// Initializes a new instance of the BufferPool class
        /// </summary>
        /// <param name="slabSize">Length, in bytes, of a slab in the BufferPool</param>
        /// <param name="initialSlabs">Number of slabs to create initially</param>
        /// <param name="subsequentSlabs">Number of additional slabs to create at a time</param>
        public BufferPool(long slabSize, int initialSlabs, int subsequentSlabs)
        {
            if (slabSize < 1)
            {
                throw new ArgumentException("slabSize must be equal to or greater than 1");
            }
            if (initialSlabs < 1)
            {
                throw new ArgumentException("initialSlabs must be equal to or greater than 1");
            }
            if (subsequentSlabs < 1)
            {
                throw new ArgumentException("subsequentSlabs must be equal to or greater than 1");
            }
            if (slabSize > MaximumSlabSize)
            {
                throw new ArgumentException("slabSize cannot be larger BufferPool.MaximumSlabSize");
            }

            this.slabSize        = slabSize > MinimumSlabSize ? slabSize : MinimumSlabSize;
            this.initialSlabs    = initialSlabs;
            this.subsequentSlabs = subsequentSlabs;

            // lock is unnecessary in this instance constructor
            //lock (syncSlabList)
            //{
            if (slabs.Count == 0)
            {
                SetSingleSlabPool(initialSlabs == 1);     //Assume for optimization reasons that it's a single slab pool if the number of initial slabs is 1

                for (int i = 0; i < initialSlabs; i++)
                {
                    slabs.Add(new MemorySlab(slabSize, this));
                }

                firstSlab = slabs[0];
            }
            //}
        }
Пример #15
0
        /// <summary>
        /// Initializes a new instance of the MemoryBlock class
        /// </summary>
        /// <param name="startLocation">Offset where memory block starts in slab</param>
        /// <param name="length">Length of memory block</param>
        /// <param name="slab">Slab to be associated with the memory block</param>
        internal MemoryBlock(long startLocation, long length, IMemorySlab slab)
        {
            if (startLocation < 0)
            {
                throw new ArgumentOutOfRangeException("startLocation", "StartLocation must be greater than 0");
            }

            startLoc = startLocation;

            if (length <= 0)
            {
                throw new ArgumentOutOfRangeException("length", "Length must be greater than 0");
            }

            endLoc      = startLocation + length - 1;
            this.length = length;
            if (slab == null)
            {
                throw new ArgumentNullException("slab");
            }
            this.owner = slab;
        }
        /// <summary>
        /// Initializes a new instance of the MemoryBlock class
        /// </summary>
        /// <param name="startLocation">Offset where memory block starts in slab</param>
        /// <param name="length">Length of memory block</param>
        /// <param name="slab">Slab to be associated with the memory block</param>
        internal MemoryBlock(long startLocation, long length, IMemorySlab slab)
        {
            if (startLocation < 0)
            {
                throw new ArgumentOutOfRangeException("startLocation", "StartLocation must be greater than 0");
            }

            startLoc = startLocation;

            if (length <= 0)
            {
                throw new ArgumentOutOfRangeException("length", "Length must be greater than 0");
            }

            this.length = length;
            if (slab == null)
            {
                throw new ArgumentNullException("slab");
            }
            this.owner = slab;

            //TODO: If this class is converted to a struct, consider implementing IComparer, IComparable -- first figure out what sorted dictionary uses those Comparer things for
        }
Пример #17
0
        private static ManagedBuffer GetNewBuffer(IMemorySlab Slab)
        {
            IMemoryBlock allocatedMemoryBlock;
            Slab.TryAllocate(blockSize, out allocatedMemoryBlock);

            ManagedBuffer target = new ManagedBuffer(allocatedMemoryBlock);
            return target;
        }
Пример #18
0
 public void BadConstructionTest2()
 {
     IMemorySlab target = CreateInvalidMemorySlab2();
 }
Пример #19
0
        /// <summary>
        /// Creates a buffer of the specified size, filled with the contents of a specified byte array
        /// </summary>
        /// <param name="size">Buffer size, in bytes</param>
        /// <param name="filledWith">Byte array to copy to buffer</param>
        /// <returns>IBuffer object of requested size</returns>
        public IBuffer GetBuffer(long size, byte[] filledWith)
        {
            if (size < 0)
            {
                throw new ArgumentException("size must be greater than 0");
            }

            //TODO: If size is larger than 16 * SlabSize (or MaxNumberOfSegments * SlabSize) then throw exception saying you can't have a buffer greater than 16 times Slab size
            //Write test for this

            //Make sure filledWith can fit into the requested buffer, so that we do not allocate a buffer and then
            //an exception is thrown (when IBuffer.FillWith() is called) before the buffer is returned.
            if (filledWith != null)
            {
                if (filledWith.LongLength > size)
                {
                    throw new ArgumentException("Length of filledWith array cannot be larger than desired buffer size");
                }
                if (filledWith.LongLength == 0)
                {
                    filledWith = null;
                }

                //TODO: Write test that will test that IBuffer.FillWith() doesn't throw an exception (and that buffers aren't allocated) in this method
            }

            if (size == 0)
            {
                //Return an empty buffer
                return(new ManagedBuffer(firstSlab));
            }

            List <IMemoryBlock> allocatedBlocks = new List <IMemoryBlock>();

            //TODO: Consider the performance penalty involved in making the try-catch below a constrained region
            //RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                IMemorySlab[] slabArr;
                long          allocdLengthTally = 0;

                if (GetSingleSlabPool())
                {
                    //Optimization: Chances are that there'll be just one slab in a pool, so access it directly
                    //and avoid the lock statement involved while creating an array of slabs.

                    //Note that even if singleSlabPool is inaccurate, this method will still work properly.
                    //The optimization is effective because singleSlabPool will be accurate majority of the time.

                    slabArr           = new IMemorySlab[] { firstSlab };
                    allocdLengthTally = TryAllocateBlocksInSlabs(size, MAX_SEGMENTS_PER_BUFFER, slabArr, ref allocatedBlocks);

                    if (allocdLengthTally == size)
                    {
                        //We got the entire length we are looking for, so leave
                        return(GetFilledBuffer(allocatedBlocks, filledWith));
                    }

                    SetSingleSlabPool(false); // Slab count will soon be incremented
                }
                else
                {
                    lock (syncSlabList)
                    {
                        slabArr = slabs.ToArray();
                    }

                    allocdLengthTally = TryAllocateBlocksInSlabs(size, MAX_SEGMENTS_PER_BUFFER, slabArr, ref allocatedBlocks);

                    if (allocdLengthTally == size)
                    {
                        //We got the entire length we are looking for, so leave
                        return(GetFilledBuffer(allocatedBlocks, filledWith));
                    }
                }


                //Try to create new slab
                lock (syncNewSlab)
                {
                    //Look again for free block
                    lock (syncSlabList)
                    {
                        slabArr = slabs.ToArray();
                    }

                    allocdLengthTally += TryAllocateBlocksInSlabs(size - allocdLengthTally, MAX_SEGMENTS_PER_BUFFER - allocatedBlocks.Count, slabArr, ref allocatedBlocks);

                    if (allocdLengthTally == size)
                    {
                        //found it -- leave
                        return(GetFilledBuffer(allocatedBlocks, filledWith));
                    }

                    List <IMemorySlab> newSlabList = new List <IMemorySlab>();
                    do
                    {
                        //Unable to find available free space, so create new slab
                        MemorySlab newSlab = new MemorySlab(slabSize, this);

                        IMemoryBlock allocdBlk;
                        if (slabSize > size - allocdLengthTally)
                        {
                            //Allocate remnant
                            newSlab.TryAllocate(size - allocdLengthTally, out allocdBlk);
                        }
                        else
                        {
                            //Allocate entire slab
                            newSlab.TryAllocate(slabSize, out allocdBlk);
                        }

                        newSlabList.Add(newSlab);
                        allocatedBlocks.Add(allocdBlk);
                        allocdLengthTally += allocdBlk.Length;
                    }while (allocdLengthTally < size);

                    lock (syncSlabList)
                    {
                        //Add new slabs to collection
                        slabs.AddRange(newSlabList);

                        //Add extra slabs as requested in object properties
                        for (int i = 0; i < subsequentSlabs - 1; i++)
                        {
                            slabs.Add(new MemorySlab(slabSize, this));
                        }
                    }
                }

                return(GetFilledBuffer(allocatedBlocks, filledWith));
            }
            catch
            {
                //OOM, Thread abort exceptions and other ugly things can happen so roll back any allocated blocks.
                //This will prevent a limbo situation where those blocks are allocated but caller is unaware and can't deallocate them.

                //NOTE: This try-catch block should not be within a lock as it calls MemorySlab.Free which takes locks,
                //and in turn calls BufferPool.TryFreeSlabs which takes other locks and can lead to a dead-lock/race condition.

                //TODO: Write rollback test.

                for (int b = 0; b < allocatedBlocks.Count; b++)
                {
                    allocatedBlocks[b].Slab.Free(allocatedBlocks[b]);
                }

                throw;
            }
        }
Пример #20
0
        /// <summary>
        /// Helper method that searches for free block in an array of slabs and returns the allocated block
        /// </summary>
        /// <param name="length">Requested length of memory block</param>
        /// <param name="slabs">Array of slabs to search</param>
        /// <param name="allocatedBlock">Allocated memory block</param>
        /// <returns>True if memory block was successfully allocated. False, if otherwise</returns>
        private static bool TryAllocateBlockInSlabs(long length, IMemorySlab[] slabs, out IMemoryBlock allocatedBlock)
        {
            allocatedBlock = null;
            for (int i = 0; i < slabs.Length; i++)
            {
                if (slabs[i].LargestFreeBlockSize >= length)
                {
                    if (slabs[i].TryAllocate(length, out allocatedBlock))
                    {
                        return true;
                    }
                }
            }

            return false;
        }
Пример #21
0
        /// <summary>
        /// Creates a buffer of the specified size
        /// </summary>
        /// <param name="size">Buffer size, in bytes</param>
        /// <returns>IBuffer object of requested size</returns>        
        public IBuffer GetBuffer(long size)
        {
            if (size < 0) throw new ArgumentException("Length must be greater than 0");

            if (size == 0) return new ManagedBuffer(firstSlab); //Return an empty buffer

            IMemoryBlock allocatedBlock;
            IMemorySlab[] slabArr;

            if (singleSlabPool == -1)
            {
                //Optimization: Chances are that there'll be just one slab in a pool, so access it directly
                //and avoid the lock statement involved while creating an array of slabs.

                //Note that even if singleSlabPool is inaccurate, this method will still work properly.
                //The optimization is effective because singleSlabPool will be accurate majority of the time.

                slabArr = new IMemorySlab[] { firstSlab };
                if (TryAllocateBlockInSlabs(size, slabArr, out allocatedBlock))
                {
                    return new ManagedBuffer(allocatedBlock);
                }

                Interlocked.Exchange(ref singleSlabPool, 0); // Slab count will soon be incremented
            }
            else
            {

                lock (sync_slabList)
                {
                    slabArr = slabs.ToArray();
                }

                if (TryAllocateBlockInSlabs(size, slabArr, out allocatedBlock))
                {
                    return new ManagedBuffer(allocatedBlock);
                }
            }

            lock (sync_newSlab)
            {
                //Look again for free block
                lock (sync_slabList)
                {
                    slabArr = slabs.ToArray();
                }

                if (TryAllocateBlockInSlabs(size, slabArr, out allocatedBlock))
                {
                    //found it -- leave
                    return new ManagedBuffer(allocatedBlock);
                }

                //Unable to find available free space, so create new slab
                MemorySlab newSlab = new MemorySlab(slabSize, this);

                newSlab.TryAllocate(size, out allocatedBlock);

                lock (sync_slabList)
                {
                    //Add new Slab to collection

                    slabs.Add(newSlab);

                    //Add extra slabs as requested in object properties
                    for (int i = 0; i < subsequentSlabs - 1; i++)
                    {
                        slabs.Add(new MemorySlab(slabSize, this));
                    }
                }

            }

            return new ManagedBuffer(allocatedBlock);
        }
Пример #22
0
        //Gets a multi slab buffer
        private static ManagedBuffer GetNewBuffer(IMemorySlab[] Slabs)
        {
            List<IMemoryBlock> blockList = new List<IMemoryBlock>();
            IMemoryBlock allocatedMemoryBlock;

            foreach (var slab in Slabs)
            {
                slab.TryAllocate(blockSize, out allocatedMemoryBlock);
                blockList.Add(allocatedMemoryBlock);
            }

            ManagedBuffer target = new ManagedBuffer(blockList);
            return target;
        }
Пример #23
0
        /// <summary>
        /// Creates a buffer of the specified size
        /// </summary>
        /// <param name="size">Buffer size, in bytes</param>
        /// <returns>IBuffer object of requested size</returns>
        public IBuffer GetBuffer(long size)
        {
            if (size < 0)
            {
                throw new ArgumentException("Length must be greater than 0");
            }

            if (size == 0)
            {
                return(new ManagedBuffer(firstSlab));           //Return an empty buffer
            }
            IMemoryBlock allocatedBlock;

            IMemorySlab[] slabArr;

            if (singleSlabPool == -1)
            {
                //Optimization: Chances are that there'll be just one slab in a pool, so access it directly
                //and avoid the lock statement involved while creating an array of slabs.

                //Note that even if singleSlabPool is inaccurate, this method will still work properly.
                //The optimization is effective because singleSlabPool will be accurate majority of the time.

                slabArr = new IMemorySlab[] { firstSlab };
                if (TryAllocateBlockInSlabs(size, slabArr, out allocatedBlock))
                {
                    return(new ManagedBuffer(allocatedBlock));
                }

                Interlocked.Exchange(ref singleSlabPool, 0); // Slab count will soon be incremented
            }
            else
            {
                lock (sync_slabList)
                {
                    slabArr = slabs.ToArray();
                }

                if (TryAllocateBlockInSlabs(size, slabArr, out allocatedBlock))
                {
                    return(new ManagedBuffer(allocatedBlock));
                }
            }


            lock (sync_newSlab)
            {
                //Look again for free block
                lock (sync_slabList)
                {
                    slabArr = slabs.ToArray();
                }

                if (TryAllocateBlockInSlabs(size, slabArr, out allocatedBlock))
                {
                    //found it -- leave
                    return(new ManagedBuffer(allocatedBlock));
                }

                //Unable to find available free space, so create new slab
                MemorySlab newSlab = new MemorySlab(slabSize, this);

                newSlab.TryAllocate(size, out allocatedBlock);

                lock (sync_slabList)
                {
                    //Add new Slab to collection

                    slabs.Add(newSlab);

                    //Add extra slabs as requested in object properties
                    for (int i = 0; i < subsequentSlabs - 1; i++)
                    {
                        slabs.Add(new MemorySlab(slabSize, this));
                    }
                }
            }

            return(new ManagedBuffer(allocatedBlock));
        }
Пример #24
0
        private const int MAX_SEGMENTS_PER_BUFFER = 16; //Maximum number of segments in a buffer.

        /// <summary>
        /// Initializes a new instance of the BufferPool class
        /// </summary>
        /// <param name="slabSize">Length, in bytes, of a slab in the BufferPool</param>
        /// <param name="initialSlabs">Number of slabs to create initially</param>
        /// <param name="subsequentSlabs">Number of additional slabs to create at a time</param>
        public BufferPool(long slabSize, int initialSlabs, int subsequentSlabs)
        {

            if (slabSize < 1) throw new ArgumentException("slabSize must be equal to or greater than 1");
            if (initialSlabs < 1) throw new ArgumentException("initialSlabs must be equal to or greater than 1");
            if (subsequentSlabs < 1) throw new ArgumentException("subsequentSlabs must be equal to or greater than 1");
            if (slabSize > MaximumSlabSize) throw new ArgumentException("slabSize cannot be larger BufferPool.MaximumSlabSize");

            this.slabSize = slabSize > MinimumSlabSize ? slabSize : MinimumSlabSize;
            this.initialSlabs = initialSlabs;
            this.subsequentSlabs = subsequentSlabs;

            // lock is unnecessary in this instance constructor
            //lock (syncSlabList)
            //{
                if (slabs.Count == 0)
                {
                    SetSingleSlabPool(initialSlabs == 1); //Assume for optimization reasons that it's a single slab pool if the number of initial slabs is 1

                    for (int i = 0; i < initialSlabs; i++)
                    {
                        slabs.Add(new MemorySlab(slabSize, this));
                    }

                    firstSlab = slabs[0];
                }
            //}
        }
Пример #25
0
        /// <summary>
        /// Creates a buffer of the specified size, filled with the contents of a specified byte array
        /// </summary>
        /// <param name="size">Buffer size, in bytes</param>
        /// <param name="filledWith">Byte array to copy to buffer</param>
        /// <returns>IBuffer object of requested size</returns>
        public IBuffer GetBuffer(long size, byte[] filledWith)
        {
            if (size < 0) throw new ArgumentException("size must be greater than 0");

            //TODO: If size is larger than 16 * SlabSize (or MaxNumberOfSegments * SlabSize) then throw exception saying you can't have a buffer greater than 16 times Slab size
            //Write test for this

            //Make sure filledWith can fit into the requested buffer, so that we do not allocate a buffer and then
            //an exception is thrown (when IBuffer.FillWith() is called) before the buffer is returned.
            if (filledWith != null)
            {
                if (filledWith.LongLength > size) throw new ArgumentException("Length of filledWith array cannot be larger than desired buffer size");
                if (filledWith.LongLength == 0) filledWith = null;

                //TODO: Write test that will test that IBuffer.FillWith() doesn't throw an exception (and that buffers aren't allocated) in this method
            }

            if (size == 0)
            {
                //Return an empty buffer
                return new ManagedBuffer(firstSlab);
            }

            List<IMemoryBlock> allocatedBlocks = new List<IMemoryBlock>();

            //TODO: Consider the performance penalty involved in making the try-catch below a constrained region
            //RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                IMemorySlab[] slabArr;
                long allocdLengthTally = 0;

                if (GetSingleSlabPool())
                {
                    //Optimization: Chances are that there'll be just one slab in a pool, so access it directly 
                    //and avoid the lock statement involved while creating an array of slabs.

                    //Note that even if singleSlabPool is inaccurate, this method will still work properly.
                    //The optimization is effective because singleSlabPool will be accurate majority of the time.

                    slabArr = new IMemorySlab[] { firstSlab };
                    allocdLengthTally = TryAllocateBlocksInSlabs(size, MAX_SEGMENTS_PER_BUFFER, slabArr, ref allocatedBlocks);

                    if (allocdLengthTally == size)
                    {
                        //We got the entire length we are looking for, so leave
                        return GetFilledBuffer(allocatedBlocks, filledWith);
                    }

                    SetSingleSlabPool(false); // Slab count will soon be incremented
                }
                else
                {
                    lock (syncSlabList)
                    {
                        slabArr = slabs.ToArray();
                    }

                    allocdLengthTally = TryAllocateBlocksInSlabs(size, MAX_SEGMENTS_PER_BUFFER, slabArr, ref allocatedBlocks);

                    if (allocdLengthTally == size)
                    {
                        //We got the entire length we are looking for, so leave
                        return GetFilledBuffer(allocatedBlocks, filledWith);
                    }
                }


                //Try to create new slab
                lock (syncNewSlab)
                {
                    //Look again for free block
                    lock (syncSlabList)
                    {
                        slabArr = slabs.ToArray();
                    }

                    allocdLengthTally += TryAllocateBlocksInSlabs(size - allocdLengthTally, MAX_SEGMENTS_PER_BUFFER - allocatedBlocks.Count, slabArr, ref allocatedBlocks);

                    if (allocdLengthTally == size)
                    {
                        //found it -- leave
                        return GetFilledBuffer(allocatedBlocks, filledWith);
                    }

                    List<IMemorySlab> newSlabList = new List<IMemorySlab>();
                    do
                    {
                        //Unable to find available free space, so create new slab
                        MemorySlab newSlab = new MemorySlab(slabSize, this);

                        IMemoryBlock allocdBlk;
                        if (slabSize > size - allocdLengthTally)
                        {
                            //Allocate remnant
                            newSlab.TryAllocate(size - allocdLengthTally, out allocdBlk);
                        }
                        else
                        {
                            //Allocate entire slab
                            newSlab.TryAllocate(slabSize, out allocdBlk);
                        }

                        newSlabList.Add(newSlab);
                        allocatedBlocks.Add(allocdBlk);
                        allocdLengthTally += allocdBlk.Length;
                    }
                    while (allocdLengthTally < size);

                    lock (syncSlabList)
                    {
                        //Add new slabs to collection
                        slabs.AddRange(newSlabList);

                        //Add extra slabs as requested in object properties
                        for (int i = 0; i < subsequentSlabs - 1; i++)
                        {
                            slabs.Add(new MemorySlab(slabSize, this));
                        }
                    }

                }

                return GetFilledBuffer(allocatedBlocks, filledWith);
            }
            catch
            {
                //OOM, Thread abort exceptions and other ugly things can happen so roll back any allocated blocks.
                //This will prevent a limbo situation where those blocks are allocated but caller is unaware and can't deallocate them.

                //NOTE: This try-catch block should not be within a lock as it calls MemorySlab.Free which takes locks,
                //and in turn calls BufferPool.TryFreeSlabs which takes other locks and can lead to a dead-lock/race condition.

                //TODO: Write rollback test.

                for (int b = 0; b < allocatedBlocks.Count; b++)
                {
                    allocatedBlocks[b].Slab.Free(allocatedBlocks[b]);
                }

                throw;
            }

        }
Пример #26
0
        /// <summary>
        /// Helper method that searches for free blocks in an array of slabs and returns allocated blocks
        /// </summary>
        /// <param name="totalLength">Requested total length of all memory blocks</param>
        /// <param name="maxBlocks">Maximum number of memory blocks to allocate</param>
        /// <param name="slabs">Array of slabs to search</param>
        /// <param name="allocatedBlocks">List of allocated memory block</param>
        /// <returns>True if memory block was successfully allocated. False, if otherwise</returns>
        private static long TryAllocateBlocksInSlabs(long totalLength, int maxBlocks, IMemorySlab[] slabs, ref List<IMemoryBlock> allocatedBlocks)
        {
            allocatedBlocks = new List<IMemoryBlock>();

            long minBlockSize;
            long allocatedSizeTally = 0;

            long largest;
            long reqLength;
            IMemoryBlock allocdBlock;
            int allocdCount = 0;
            //TODO: Figure out how to do this math without involving floating point arithmetic
            minBlockSize = (long)Math.Ceiling(totalLength / (float)maxBlocks);
            do
            {
                allocdBlock = null;
                for (int i = 0; i < slabs.Length; i++)
                {
                    largest = slabs[i].LargestFreeBlockSize;
                    if (largest >= minBlockSize)
                    {
                        //Figure out what length to request for
                        reqLength = slabs[i].TryAllocate(minBlockSize, totalLength - allocatedSizeTally, out allocdBlock);

                        if (reqLength > 0)
                        {
                            allocatedBlocks.Add(allocdBlock);
                            allocatedSizeTally += reqLength;
                            allocdCount++;
                            if (allocatedSizeTally == totalLength) return allocatedSizeTally;

                            //Calculate the new minimum block size
                            //TODO: Figure out how to do this math without involving floating point arithmetic
                            minBlockSize = (long)Math.Ceiling((totalLength - allocatedSizeTally) / (float)(maxBlocks - allocdCount));

                            //Scan again from start because there is a chance the smaller minimum block size exists in previously skipped slabs
                            break;
                        }
                    }

                }
            } while (allocdBlock != null);

            return allocatedSizeTally;
        }