public void ConsumingComplete( MemoryPoolIterator consumed, MemoryPoolIterator examined) { MemoryPoolBlock returnStart = null; MemoryPoolBlock returnEnd = null; lock (_sync) { if (!_disposed) { if (!consumed.IsDefault) { // Compute lengthConsumed before modifying _head or consumed var lengthConsumed = 0; if (_bufferSizeControl != null) { lengthConsumed = new MemoryPoolIterator(_head).GetLength(consumed); } returnStart = _head; returnEnd = consumed.Block; _head = consumed.Block; _head.Start = consumed.Index; // Must call Subtract() after _head has been advanced, to avoid producer starting too early and growing // buffer beyond max length. _bufferSizeControl?.Subtract(lengthConsumed); } if (!examined.IsDefault && examined.IsEnd && RemoteIntakeFin == false && _awaitableError == null) { _manualResetEvent.Reset(); Interlocked.CompareExchange( ref _awaitableState, _awaitableIsNotCompleted, _awaitableIsCompleted); } } else { returnStart = _head; returnEnd = null; _head = null; _tail = null; } ReturnBlocks(returnStart, returnEnd); if (!_consuming) { throw new InvalidOperationException("No ongoing consuming operation to complete."); } _consuming = false; } }
private void ProducingCompleteNoPreComplete(MemoryPoolIterator end) { MemoryPoolBlock blockToReturn = null; lock (_returnLock) { // Both ProducingComplete and WriteAsync should not call this method // if _lastStart was not set. Debug.Assert(!_lastStart.IsDefault); // If the socket has been closed, return the produced blocks // instead of advancing the now non-existent tail. if (_tail != null) { _tail = end.Block; _tail.End = end.Index; } else { blockToReturn = _lastStart.Block; } _lastStart = default(MemoryPoolIterator); } if (blockToReturn != null) { ThreadPool.QueueUserWorkItem(_returnBlocks, blockToReturn); } }
public void IncomingData(byte[] buffer, int offset, int count) { lock (_sync) { // Must call Add() before bytes are available to consumer, to ensure that Length is >= 0 _bufferSizeControl?.Add(count); if (count > 0) { if (_tail == null) { _tail = _memory.Lease(); } var iterator = new MemoryPoolIterator(_tail, _tail.End); iterator.CopyFrom(buffer, offset, count); if (_head == null) { _head = _tail; } _tail = iterator.Block; } else { RemoteIntakeFin = true; } Complete(); } }
/// <summary> /// Save the data at the current location then move to the next available space. /// </summary> /// <param name="data">The byte to be saved.</param> /// <returns>true if the operation successes. false if can't find available space.</returns> public bool Put(byte data) { if (_block == null) { return(false); } var block = _block; var index = _index; while (true) { var wasLastBlock = block.Next == null; if (index < block.End) { _block = block; _index = index + 1; block.Array[index] = data; return(true); } else if (wasLastBlock) { return(false); } else { block = block.Next; index = block.Start; } } }
/// <summary> /// Internal method called when a block is requested and the pool is empty. It allocates one additional slab, creates all of the /// block tracking objects, and adds them all to the pool. /// </summary> private MemoryPoolBlock AllocateSlab() { var slab = MemoryPoolSlab.Create(_slabLength); _slabs.Push(slab); var basePtr = slab.ArrayPtr; var firstOffset = (int)((_blockStride - 1) - ((ulong)(basePtr + _blockStride - 1) % _blockStride)); var poolAllocationLength = _slabLength - _blockStride; var offset = firstOffset; for (; offset + _blockLength < poolAllocationLength; offset += _blockStride) { var block = MemoryPoolBlock.Create( new ArraySegment <byte>(slab.Array, offset, _blockLength), basePtr, this, slab); Return(block); } // return last block rather than adding to pool var newBlock = MemoryPoolBlock.Create( new ArraySegment <byte>(slab.Array, offset, _blockLength), basePtr, this, slab); return(newBlock); }
/// <summary> /// Called to return a block to the pool. Once Return has been called the memory no longer belongs to the caller, and /// Very Bad Things will happen if the memory is read of modified subsequently. If a caller fails to call Return and the /// block tracking object is garbage collected, the block tracking object's finalizer will automatically re-create and return /// a new tracking object into the pool. This will only happen if there is a bug in the server, however it is necessary to avoid /// leaving "dead zones" in the slab due to lost block tracking objects. /// </summary> /// <param name="block">The block to return. It must have been acquired by calling Lease on the same memory pool instance.</param> public void Return(MemoryPoolBlock block) { Debug.Assert(block.Pool == this, "Returned block was not leased from this pool"); if (block.Slab != null && block.Slab.IsActive) { block.Reset(); _blocks.Enqueue(block); } }
private static void ReturnBlocks(MemoryPoolBlock block, MemoryPoolBlock end) { while (block != end) { var returnBlock = block; block = block.Next; returnBlock.Pool.Return(returnBlock); } }
private static void ReturnWrittenBlocks(MemoryPoolBlock block) { while (block != null) { var returnBlock = block; block = block.Next; returnBlock.Pool.Return(returnBlock); } }
public void IncomingDeferred() { Debug.Assert(_pinned != null); if (_pinned != null) { if (_pinned != _tail) { _memory.Return(_pinned); } _pinned = null; } }
public void Dispose() { lock (_sync) { AbortAwaiting(); if (!_consuming) { ReturnBlocks(_head, null); _head = null; _tail = null; } _disposed = true; } }
public MemoryPoolBlock IncomingStart() { const int minimumSize = 2048; if (_tail != null && minimumSize <= _tail.Data.Offset + _tail.Data.Count - _tail.End) { _pinned = _tail; } else { _pinned = _memory.Lease(); } return(_pinned); }
public void CopyFrom(byte[] data, int offset, int count) { if (IsDefault) { return; } Debug.Assert(_block != null); Debug.Assert(_block.Next == null); Debug.Assert(_block.End == _index); var pool = _block.Pool; var block = _block; var blockIndex = _index; var bufferIndex = offset; var remaining = count; var bytesLeftInBlock = block.Data.Offset + block.Data.Count - blockIndex; while (remaining > 0) { if (bytesLeftInBlock == 0) { var nextBlock = pool.Lease(); block.End = blockIndex; Volatile.Write(ref block.Next, nextBlock); block = nextBlock; blockIndex = block.Data.Offset; bytesLeftInBlock = block.Data.Count; } var bytesToCopy = remaining < bytesLeftInBlock ? remaining : bytesLeftInBlock; Buffer.BlockCopy(data, bufferIndex, block.Array, blockIndex, bytesToCopy); blockIndex += bytesToCopy; bufferIndex += bytesToCopy; remaining -= bytesToCopy; bytesLeftInBlock -= bytesToCopy; } block.End = blockIndex; _block = block; _index = blockIndex; }
public void Skip(int bytesToSkip) { if (_block == null) { return; } var wasLastBlock = _block.Next == null; var following = _block.End - _index; if (following >= bytesToSkip) { _index += bytesToSkip; return; } var block = _block; var index = _index; while (true) { if (wasLastBlock) { throw new InvalidOperationException("Attempted to skip more bytes than available."); } else { bytesToSkip -= following; block = block.Next; index = block.Start; } wasLastBlock = block.Next == null; following = block.End - index; if (following >= bytesToSkip) { _block = block; _index = index + bytesToSkip; return; } } }
public SocketOutput( EventThread thread, UvStreamHandle socket, MemoryPool memory, Connection connection, ConnectionId connectionId, IThreadPool threadPool, Queue <UvWriteReq> writeReqPool) { _thread = thread; _socket = socket; _connection = connection; _connectionId = connectionId; _threadPool = threadPool; _tasksPending = new Queue <WaitingTask>(_initialTaskQueues); _writeContextPool = new Queue <WriteContext>(_maxPooledWriteContexts); _writeReqPool = writeReqPool; _head = memory.Lease(); _tail = _head; }
public int Take() { var block = _block; if (block == null) { return(-1); } var index = _index; var wasLastBlock = block.Next == null; if (index < block.End) { _index = index + 1; return(block.Array[index]); } do { if (wasLastBlock) { return(-1); } else { block = block.Next; index = block.Start; } wasLastBlock = block.Next == null; if (index < block.End) { _block = block; _index = index + 1; return(block.Array[index]); } } while (true); }
public void IncomingComplete(int count, Exception error) { lock (_sync) { // Must call Add() before bytes are available to consumer, to ensure that Length is >= 0 _bufferSizeControl?.Add(count); if (_pinned != null) { _pinned.End += count; if (_head == null) { _head = _tail = _pinned; } else if (_tail == _pinned) { // NO-OP: this was a read into unoccupied tail-space } else { Volatile.Write(ref _tail.Next, _pinned); _tail = _pinned; } _pinned = null; } if (count == 0) { RemoteIntakeFin = true; } if (error != null) { _awaitableError = error; } Complete(); } }
// This is called on the libuv event loop private void ReturnAllBlocks() { lock (_returnLock) { var block = _head; while (block != _tail) { var returnBlock = block; block = block.Next; returnBlock.Pool.Return(returnBlock); } // Only return the _tail if we aren't between ProducingStart/Complete calls if (_lastStart.IsDefault) { _tail.Pool.Return(_tail); } _head = null; _tail = null; } }
/// <summary> /// called when the block is returned to the pool. mutable values are re-assigned to their guaranteed initialized state. /// </summary> public void Reset() { Next = null; Start = Data.Offset; End = Data.Offset; }
public MemoryPoolIterator(MemoryPoolBlock block) { _block = block; _index = _block?.Start ?? 0; }
public MemoryPoolIterator(MemoryPoolBlock block, int index) { _block = block; _index = index; }
public unsafe int Seek(ref Vector <byte> byte0Vector, ref Vector <byte> byte1Vector, ref Vector <byte> byte2Vector) { if (IsDefault) { return(-1); } var block = _block; var index = _index; var wasLastBlock = block.Next == null; var following = block.End - index; byte[] array; int byte0Index = int.MaxValue; int byte1Index = int.MaxValue; int byte2Index = int.MaxValue; var byte0 = byte0Vector[0]; var byte1 = byte1Vector[0]; var byte2 = byte2Vector[0]; while (true) { while (following == 0) { if (wasLastBlock) { _block = block; _index = index; return(-1); } block = block.Next; index = block.Start; wasLastBlock = block.Next == null; following = block.End - index; } array = block.Array; while (following > 0) { // Need unit tests to test Vector path #if !DEBUG // Check will be Jitted away https://github.com/dotnet/coreclr/issues/1079 if (Vector.IsHardwareAccelerated) { #endif if (following >= _vectorSpan) { var data = new Vector <byte>(array, index); var byte0Equals = Vector.Equals(data, byte0Vector); var byte1Equals = Vector.Equals(data, byte1Vector); var byte2Equals = Vector.Equals(data, byte2Vector); if (!byte0Equals.Equals(Vector <byte> .Zero)) { byte0Index = FindFirstEqualByte(ref byte0Equals); } if (!byte1Equals.Equals(Vector <byte> .Zero)) { byte1Index = FindFirstEqualByte(ref byte1Equals); } if (!byte2Equals.Equals(Vector <byte> .Zero)) { byte2Index = FindFirstEqualByte(ref byte2Equals); } if (byte0Index == int.MaxValue && byte1Index == int.MaxValue && byte2Index == int.MaxValue) { following -= _vectorSpan; index += _vectorSpan; continue; } _block = block; int toReturn, toMove; if (byte0Index < byte1Index) { if (byte0Index < byte2Index) { toReturn = byte0; toMove = byte0Index; } else { toReturn = byte2; toMove = byte2Index; } } else { if (byte1Index < byte2Index) { toReturn = byte1; toMove = byte1Index; } else { toReturn = byte2; toMove = byte2Index; } } _index = index + toMove; return(toReturn); } // Need unit tests to test Vector path #if !DEBUG } #endif var pCurrent = (block.DataFixedPtr + index); var pEnd = pCurrent + following; do { if (*pCurrent == byte0) { _block = block; _index = index; return(byte0); } if (*pCurrent == byte1) { _block = block; _index = index; return(byte1); } if (*pCurrent == byte2) { _block = block; _index = index; return(byte2); } pCurrent++; index++; } while (pCurrent != pEnd); following = 0; break; } } }
public unsafe void CopyFromAscii(string data) { if (IsDefault) { return; } Debug.Assert(_block != null); Debug.Assert(_block.Next == null); Debug.Assert(_block.End == _index); var pool = _block.Pool; var block = _block; var blockIndex = _index; var length = data.Length; var bytesLeftInBlock = block.Data.Offset + block.Data.Count - blockIndex; var bytesLeftInBlockMinusSpan = bytesLeftInBlock - 3; fixed(char *pData = data) { var input = pData; var inputEnd = pData + length; var inputEndMinusSpan = inputEnd - 3; while (input < inputEnd) { if (bytesLeftInBlock == 0) { var nextBlock = pool.Lease(); block.End = blockIndex; Volatile.Write(ref block.Next, nextBlock); block = nextBlock; blockIndex = block.Data.Offset; bytesLeftInBlock = block.Data.Count; bytesLeftInBlockMinusSpan = bytesLeftInBlock - 3; } var output = (block.DataFixedPtr + block.End); var copied = 0; for (; input < inputEndMinusSpan && copied < bytesLeftInBlockMinusSpan; copied += 4) { *(output) = (byte)*(input); *(output + 1) = (byte)*(input + 1); *(output + 2) = (byte)*(input + 2); *(output + 3) = (byte)*(input + 3); output += 4; input += 4; } for (; input < inputEnd && copied < bytesLeftInBlock; copied++) { *(output++) = (byte)*(input++); } blockIndex += copied; bytesLeftInBlockMinusSpan -= copied; bytesLeftInBlock -= copied; } } block.End = blockIndex; _block = block; _index = blockIndex; }
public MemoryPoolIterator ProducingStart() { _producingBlock = _memory.Lease(); return(new MemoryPoolIterator(_producingBlock)); }