public TryAllocate ( long length, IMemoryBlock &allocatedBlock ) : bool | ||
length | long | Length, in bytes, of memory block |
allocatedBlock | IMemoryBlock | Allocated memory block |
return | bool |
/// <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; } }
/// <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); }
/// <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)); }
/// <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; } }