/// <summary> /// Read the percent-encoding and try unescape it. /// /// The operation first peek at the character the <paramref name="scan"/> /// iterator points at. If it is % the <paramref name="scan"/> is then /// moved on to scan the following to characters. If the two following /// characters are hexadecimal literals they will be unescaped and the /// value will be returned. /// /// If the first character is not % the <paramref name="scan"/> iterator /// will be removed beyond the location of % and -1 will be returned. /// /// If the following two characters can't be successfully unescaped the /// <paramref name="scan"/> iterator will be move behind the % and -1 /// will be returned. /// </summary> /// <param name="scan">The value to read</param> /// <param name="end">The end of the sequence</param> /// <returns>The unescaped byte if success. Otherwise return -1.</returns> private static int UnescapePercentEncoding(ref MemoryPoolIterator scan, MemoryPoolIterator end) { if (scan.Take() != '%') { return(-1); } var probe = scan; int value1 = ReadHex(ref probe, end); if (value1 == -1) { return(-1); } int value2 = ReadHex(ref probe, end); if (value2 == -1) { return(-1); } if (SkipUnescape(value1, value2)) { return(-1); } scan = probe; return((value1 << 4) + value2); }
private void ProducingCompleteNoPreComplete(MemoryPoolIterator end) { MemoryPoolBlock blockToReturn = null; lock (_returnLock) { 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); } }
private static void Copy(MemoryPoolIterator head, MemoryPoolIterator tail, ref MemoryPoolIterator writer) { while (!CompareIterators(ref head, ref tail)) { writer.Put((byte)head.Take()); } }
public static int WriteBeginChunkBytes(ref MemoryPoolIterator start, int dataCount) { var chunkSegment = BeginChunkBytes(dataCount); start.CopyFrom(chunkSegment); return(chunkSegment.Count); }
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; } }
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(); } }
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.UnsafeRun(_returnBlocks, blockToReturn); } }
public void IncomingData(byte[] buffer, int offset, int count) { lock (_sync) { 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(); } }
public void ConsumingOutOfOrderFailsGracefully() { var defultIter = new MemoryPoolIterator(); // Calling ConsumingComplete without a preceding calling to ConsumingStart fails using (var socketInput = new SocketInput(null, null)) { Assert.Throws <InvalidOperationException>(() => socketInput.ConsumingComplete(defultIter, defultIter)); } // Calling ConsumingComplete twice in a row fails using (var socketInput = new SocketInput(null, null)) { socketInput.ConsumingStart(); socketInput.ConsumingComplete(defultIter, defultIter); Assert.Throws <InvalidOperationException>(() => socketInput.ConsumingComplete(defultIter, defultIter)); } // Calling ConsumingStart twice in a row fails using (var socketInput = new SocketInput(null, null)) { socketInput.ConsumingStart(); Assert.Throws <InvalidOperationException>(() => socketInput.ConsumingStart()); } }
/// <summary> /// Read the next char and convert it into hexadecimal value. /// /// The <paramref name="scan"/> iterator will be moved to the next /// byte no matter no matter whether the operation successes. /// </summary> /// <param name="scan">The value to read</param> /// <param name="end">The end of the sequence</param> /// <returns>The hexadecimal value if successes, otherwise -1.</returns> private static int ReadHex(ref MemoryPoolIterator scan, MemoryPoolIterator end) { if (CompareIterators(ref scan, ref end)) { return(-1); } var value = scan.Take(); var isHead = (((value >= '0') && (value <= '9')) || ((value >= 'A') && (value <= 'F')) || ((value >= 'a') && (value <= 'f'))); if (!isHead) { return(-1); } if (value <= '9') { return(value - '0'); } else if (value <= 'F') { return((value - 'A') + 10); } else // a - f { return((value - 'a') + 10); } }
public unsafe void Write( UvStreamHandle handle, MemoryPoolIterator start, MemoryPoolIterator end, int nBuffers, Action <UvWriteReq, int, Exception, object> callback, object state) { try { // add GCHandle to keeps this SafeHandle alive while request processing _pins.Add(GCHandle.Alloc(this, GCHandleType.Normal)); var pBuffers = (Libuv.uv_buf_t *)_bufs; if (nBuffers > BUFFER_COUNT) { // create and pin buffer array when it's larger than the pre-allocated one var bufArray = new Libuv.uv_buf_t[nBuffers]; var gcHandle = GCHandle.Alloc(bufArray, GCHandleType.Pinned); _pins.Add(gcHandle); pBuffers = (Libuv.uv_buf_t *)gcHandle.AddrOfPinnedObject(); } var block = start.Block; for (var index = 0; index < nBuffers; index++) { var blockStart = block == start.Block ? start.Index : block.Data.Offset; var blockEnd = block == end.Block ? end.Index : block.Data.Offset + block.Data.Count; // create and pin each segment being written pBuffers[index] = Libuv.buf_init( block.Pin() + blockStart, blockEnd - blockStart); block = block.Next; } _callback = callback; _state = state; _uv.write(this, handle, pBuffers, nBuffers, _uv_write_cb); } catch { _callback = null; _state = null; Unpin(this); var block = start.Block; for (var index = 0; index < nBuffers; index++) { block.Unpin(); block = block.Next; } throw; } }
private MemoryPoolIterator GetIterator(MemoryPoolIterator begin, int displacement) { var result = begin; for (int i = 0; i < displacement; ++i) { result.Take(); } return(result); }
public void Reset() { _lockedStart = default(MemoryPoolIterator); _lockedEnd = default(MemoryPoolIterator); _bufferCount = 0; ByteCount = 0; SocketShutdownSend = false; SocketDisconnect = false; WriteStatus = 0; WriteError = null; }
public void ProducingStartAndProducingCompleteCanBeUsedDirectly() { int nBuffers = 0; var nBufferWh = new ManualResetEventSlim(); var mockLibuv = new MockLibuv { OnWrite = (socket, buffers, triggerCompleted) => { nBuffers = buffers; nBufferWh.Set(); triggerCompleted(0); return(0); } }; using (var memory = new MemoryPool()) using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(mockLibuv, kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); var socketOutput = new SocketOutput(kestrelThread, socket, memory, new MockConnection(), "0", trace, ltp, new Queue <UvWriteReq>()); // block 1 var start = socketOutput.ProducingStart(); start.Block.End = start.Block.Data.Offset + start.Block.Data.Count; // block 2 var block2 = memory.Lease(); block2.End = block2.Data.Offset + block2.Data.Count; start.Block.Next = block2; var end = new MemoryPoolIterator(block2, block2.End); socketOutput.ProducingComplete(end); // A call to Write is required to ensure a write is scheduled socketOutput.WriteAsync(default(ArraySegment <byte>), default(CancellationToken)); Assert.True(nBufferWh.Wait(1000)); Assert.Equal(2, nBuffers); // Cleanup var cleanupTask = socketOutput.WriteAsync( default(ArraySegment <byte>), default(CancellationToken), socketDisconnect: true); } }
public void ProducingComplete(MemoryPoolIterator end) { Debug.Assert(!_lastStart.IsDefault); int bytesProduced, buffersIncluded; BytesBetween(_lastStart, end, out bytesProduced, out buffersIncluded); lock (_contextLock) { _numBytesPreCompleted += bytesProduced; } ProducingCompleteNoPreComplete(end); }
public MemoryPoolIterator ProducingStart() { lock (_returnLock) { Debug.Assert(_lastStart.IsDefault); if (_tail == null) { return(default(MemoryPoolIterator)); } _lastStart = new MemoryPoolIterator(_tail, _tail.End); return(_lastStart); } }
private void LockWrite() { var head = Self._head; var tail = Self._tail; if (head == null || tail == null) { // ReturnAllBlocks has already bee called. Nothing to do here. // Write will no-op since _byteCount will remain 0. return; } _lockedStart = new MemoryPoolIterator(head, head.Start); _lockedEnd = new MemoryPoolIterator(tail, tail.End); BytesBetween(_lockedStart, _lockedEnd, out ByteCount, out _bufferCount); }
public void FindFirstEqualByteSlow() { var bytes = Enumerable.Repeat <byte>(0xff, Vector <byte> .Count).ToArray(); for (int i = 0; i < Vector <byte> .Count; i++) { Vector <byte> vector = new Vector <byte>(bytes); Assert.Equal(i, MemoryPoolIterator.FindFirstEqualByteSlow(ref vector)); bytes[i] = 0; } for (int i = 0; i < Vector <byte> .Count; i++) { bytes[i] = 1; Vector <byte> vector = new Vector <byte>(bytes); Assert.Equal(i, MemoryPoolIterator.FindFirstEqualByteSlow(ref vector)); bytes[i] = 0; } }
public void ConsumingComplete( MemoryPoolIterator consumed, MemoryPoolIterator examined) { lock (_sync) { MemoryPoolBlock returnStart = null; MemoryPoolBlock returnEnd = null; if (!consumed.IsDefault) { returnStart = _head; returnEnd = consumed.Block; _head = consumed.Block; _head.Start = consumed.Index; } if (!examined.IsDefault && examined.IsEnd && RemoteIntakeFin == false && _awaitableError == null) { _manualResetEvent.Reset(); Interlocked.CompareExchange( ref _awaitableState, _awaitableIsNotCompleted, _awaitableIsCompleted); } while (returnStart != returnEnd) { var returnBlock = returnStart; returnStart = returnStart.Next; returnBlock.Pool.Return(returnBlock); } if (Interlocked.CompareExchange(ref _consumingState, 0, 1) != 1) { throw new InvalidOperationException("No ongoing consuming operation to complete."); } } }
public void CopyTo(ref MemoryPoolIterator output) { CopyToFast(ref output); if (MaybeUnknown != null) { foreach (var kv in MaybeUnknown) { foreach (var value in kv.Value) { if (value != null) { output.CopyFrom(_CrLf, 0, 2); output.CopyFromAscii(kv.Key); output.CopyFrom(_colonSpace, 0, 2); output.CopyFromAscii(value); } } } } }
private static void BytesBetween(MemoryPoolIterator start, MemoryPoolIterator end, out int bytes, out int buffers) { if (start.Block == end.Block) { bytes = end.Index - start.Index; buffers = 1; return; } bytes = start.Block.Data.Offset + start.Block.Data.Count - start.Index; buffers = 1; for (var block = start.Block.Next; block != end.Block; block = block.Next) { bytes += block.Data.Count; buffers++; } bytes += end.Index - end.Block.Data.Offset; buffers++; }
public void ProducingComplete(MemoryPoolIterator end) { var block = _producingBlock; while (block != end.Block) { // If we don't handle an exception from _outputStream.Write() here, we'll leak memory blocks. if (_canWrite) { try { _outputStream.Write(block.Data.Array, block.Data.Offset, block.Data.Count); } catch (Exception ex) { _canWrite = false; _logger.ConnectionError(_connectionId, ex); } } var returnBlock = block; block = block.Next; returnBlock.Pool.Return(returnBlock); } if (_canWrite) { try { _outputStream.Write(end.Block.Array, end.Block.Data.Offset, end.Index - end.Block.Data.Offset); } catch (Exception ex) { _canWrite = false; _logger.ConnectionError(_connectionId, ex); } } end.Block.Pool.Return(end.Block); }
public MemoryPoolIterator ProducingStart() { lock (_returnLock) { Debug.Assert(_lastStart.IsDefault); if (_closed) { return(default(MemoryPoolIterator)); } if (_tail == null) { _head = _thread.Memory.Lease(); _tail = _head; } _lastStart = new MemoryPoolIterator(_tail, _tail.End); return(_lastStart); } }
/// <summary> /// Unescapes the string between given memory iterators in place. /// </summary> /// <param name="start">The iterator points to the beginning of the sequence.</param> /// <param name="end">The iterator points to the byte behind the end of the sequence.</param> /// <returns>The iterator points to the byte behind the end of the processed sequence.</returns> public static MemoryPoolIterator Unescape(MemoryPoolIterator start, MemoryPoolIterator end) { // the slot to read the input var reader = start; // the slot to write the unescaped byte var writer = reader; while (true) { if (CompareIterators(ref reader, ref end)) { return(writer); } if (reader.Peek() == '%') { var decodeReader = reader; // If decoding process succeeds, the writer iterator will be moved // to the next write-ready location. On the other hand if the scanned // percent-encodings cannot be interpreted as sequence of UTF-8 octets, // these bytes should be copied to output as is. // The decodeReader iterator is always moved to the first byte not yet // be scanned after the process. A failed decoding means the chars // between the reader and decodeReader can be copied to output untouched. if (!DecodeCore(ref decodeReader, ref writer, end)) { Copy(reader, decodeReader, ref writer); } reader = decodeReader; } else { writer.Put((byte)reader.Take()); } } }
public async Task SocketCanReadAndWrite() { var loop = new UvLoopHandle(_logger); loop.Init(_uv); var tcp = new UvTcpHandle(_logger); tcp.Init(loop, (a, b) => { }); var address = ServerAddress.FromUrl($"http://127.0.0.1:0/"); tcp.Bind(address); var port = tcp.GetSockIPEndPoint().Port; tcp.Listen(10, (_, status, error, state) => { var tcp2 = new UvTcpHandle(_logger); tcp2.Init(loop, (a, b) => { }); tcp.Accept(tcp2); var data = Marshal.AllocCoTaskMem(500); tcp2.ReadStart( (a, b, c) => tcp2.Libuv.buf_init(data, 500), (__, nread, state2) => { if (nread <= 0) { tcp2.Dispose(); } else { for (var x = 0; x < 2; x++) { var req = new UvWriteReq(new KestrelTrace(new TestKestrelTrace())); req.Init(loop); var pool = new MemoryPool(); var block = pool.Lease(); block.GetIterator().CopyFrom(new ArraySegment <byte>(new byte[] { 65, 66, 67, 68, 69 })); var start = new MemoryPoolIterator(block, 0); var end = new MemoryPoolIterator(block, block.Data.Count); req.Write( tcp2, start, end, 1, (_1, _2, _3, _4) => { pool.Return(block); pool.Dispose(); }, null); } } }, null); tcp.Dispose(); }, null); var t = Task.Run(async() => { var socket = TestConnection.CreateConnectedLoopbackSocket(port); #if NET451 await Task.Factory.FromAsync( socket.BeginSend, socket.EndSend, new[] { new ArraySegment <byte>(new byte[] { 1, 2, 3, 4, 5 }) }, SocketFlags.None, null, TaskCreationOptions.None); #else await socket.SendAsync(new[] { new ArraySegment <byte>(new byte[] { 1, 2, 3, 4, 5 }) }, SocketFlags.None); #endif socket.Shutdown(SocketShutdown.Send); var buffer = new ArraySegment <byte>(new byte[2048]); while (true) { #if NET451 var count = await Task.Factory.FromAsync( socket.BeginReceive, socket.EndReceive, new[] { buffer }, SocketFlags.None, null, TaskCreationOptions.None); #else var count = await socket.ReceiveAsync(new[] { buffer }, SocketFlags.None); #endif if (count <= 0) { break; } } socket.Dispose(); }); loop.Run(); loop.Dispose(); await t; }
public void ConsumingComplete( MemoryPoolIterator consumed, MemoryPoolIterator examined) { bool isConsuming; 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; var consumedAll = !consumed.IsDefault && consumed.IsEnd; if (consumedAll && _pinned != _tail) { // Everything has been consumed and no data is being written to the // _tail block, so return all blocks between _head and _tail inclusive. _head = null; _tail = null; } else { 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 _head is null, everything has been consumed and examined. var examinedAll = (!examined.IsDefault && examined.IsEnd) || _head == null; if (examinedAll && ReadingInput) { _manualResetEvent.Reset(); Interlocked.CompareExchange( ref _awaitableState, _awaitableIsNotCompleted, _awaitableIsCompleted); } } else { // Dispose won't have returned the blocks if we were consuming, so return them now returnStart = _head; _head = null; _tail = null; } isConsuming = _consuming; _consuming = false; } ReturnBlocks(returnStart, returnEnd); if (!isConsuming) { throw new InvalidOperationException("No ongoing consuming operation to complete."); } }
public static MemoryPoolIterator Add(this MemoryPoolIterator iterator, int count) { int actual; return(iterator.CopyTo(new byte[count], 0, count, out actual)); }
/// <summary> /// Unescape the percent-encodings /// </summary> /// <param name="reader">The iterator point to the first % char</param> /// <param name="writer">The place to write to</param> /// <param name="end">The end of the sequence</param> private static bool DecodeCore(ref MemoryPoolIterator reader, ref MemoryPoolIterator writer, MemoryPoolIterator end) { // preserves the original head. if the percent-encodings cannot be interpreted as sequence of UTF-8 octets, // bytes from this till the last scanned one will be copied to the memory pointed by writer. var byte1 = UnescapePercentEncoding(ref reader, end); if (byte1 == 0) { throw BadHttpRequestException.GetException(RequestRejectionReason.PathContainsNullCharacters); } if (byte1 == -1) { return(false); } if (byte1 <= 0x7F) { // first byte < U+007f, it is a single byte ASCII writer.Put((byte)byte1); return(true); } int byte2 = 0, byte3 = 0, byte4 = 0; // anticipate more bytes var currentDecodeBits = 0; var byteCount = 1; var expectValueMin = 0; if ((byte1 & 0xE0) == 0xC0) { // 110x xxxx, expect one more byte currentDecodeBits = byte1 & 0x1F; byteCount = 2; expectValueMin = 0x80; } else if ((byte1 & 0xF0) == 0xE0) { // 1110 xxxx, expect two more bytes currentDecodeBits = byte1 & 0x0F; byteCount = 3; expectValueMin = 0x800; } else if ((byte1 & 0xF8) == 0xF0) { // 1111 0xxx, expect three more bytes currentDecodeBits = byte1 & 0x07; byteCount = 4; expectValueMin = 0x10000; } else { // invalid first byte return(false); } var remainingBytes = byteCount - 1; while (remainingBytes > 0) { // read following three chars if (CompareIterators(ref reader, ref end)) { return(false); } var nextItr = reader; var nextByte = UnescapePercentEncoding(ref nextItr, end); if (nextByte == -1) { return(false); } if ((nextByte & 0xC0) != 0x80) { // the follow up byte is not in form of 10xx xxxx return(false); } currentDecodeBits = (currentDecodeBits << 6) | (nextByte & 0x3F); remainingBytes--; if (remainingBytes == 1 && currentDecodeBits >= 0x360 && currentDecodeBits <= 0x37F) { // this is going to end up in the range of 0xD800-0xDFFF UTF-16 surrogates that // are not allowed in UTF-8; return(false); } if (remainingBytes == 2 && currentDecodeBits >= 0x110) { // this is going to be out of the upper Unicode bound 0x10FFFF. return(false); } reader = nextItr; if (byteCount - remainingBytes == 2) { byte2 = nextByte; } else if (byteCount - remainingBytes == 3) { byte3 = nextByte; } else if (byteCount - remainingBytes == 4) { byte4 = nextByte; } } if (currentDecodeBits < expectValueMin) { // overlong encoding (e.g. using 2 bytes to encode something that only needed 1). return(false); } // all bytes are verified, write to the output if (byteCount > 0) { writer.Put((byte)byte1); } if (byteCount > 1) { writer.Put((byte)byte2); } if (byteCount > 2) { writer.Put((byte)byte3); } if (byteCount > 3) { writer.Put((byte)byte4); } return(true); }
public void ServerPipeListenForConnections() { const string pipeName = @"\\.\pipe\ServerPipeListenForConnections"; var loop = new UvLoopHandle(_logger); var serverListenPipe = new UvPipeHandle(_logger); loop.Init(_uv); serverListenPipe.Init(loop, (a, b) => { }, false); serverListenPipe.Bind(pipeName); serverListenPipe.Listen(128, (backlog, status, error, state) => { var serverConnectionPipe = new UvPipeHandle(_logger); serverConnectionPipe.Init(loop, (a, b) => { }, true); try { serverListenPipe.Accept(serverConnectionPipe); } catch (Exception) { serverConnectionPipe.Dispose(); return; } var writeRequest = new UvWriteReq(new KestrelTrace(new TestKestrelTrace())); writeRequest.Init(loop); var pool = new MemoryPool(); var block = pool.Lease(); block.GetIterator().CopyFrom(new ArraySegment <byte>(new byte[] { 1, 2, 3, 4 })); var start = new MemoryPoolIterator(block, 0); var end = new MemoryPoolIterator(block, block.Data.Count); writeRequest.Write( serverConnectionPipe, start, end, 1, (handle, status2, error2, state2) => { writeRequest.Dispose(); serverConnectionPipe.Dispose(); serverListenPipe.Dispose(); pool.Return(block); pool.Dispose(); }, null); }, null); var worker = new Thread(() => { var loop2 = new UvLoopHandle(_logger); var clientConnectionPipe = new UvPipeHandle(_logger); var connect = new UvConnectRequest(new KestrelTrace(new TestKestrelTrace())); loop2.Init(_uv); clientConnectionPipe.Init(loop2, (a, b) => { }, true); connect.Init(loop2); connect.Connect(clientConnectionPipe, pipeName, (handle, status, error, state) => { var buf = loop2.Libuv.buf_init(Marshal.AllocHGlobal(8192), 8192); connect.Dispose(); clientConnectionPipe.ReadStart( (handle2, cb, state2) => buf, (handle2, status2, state2) => { if (status2 == Constants.EOF) { clientConnectionPipe.Dispose(); } }, null); }, null); loop2.Run(); loop2.Dispose(); }); worker.Start(); loop.Run(); loop.Dispose(); worker.Join(); }
private static bool CompareIterators(ref MemoryPoolIterator lhs, ref MemoryPoolIterator rhs) { // uses ref parameter to save cost of copying return((lhs.Block == rhs.Block) && (lhs.Index == rhs.Index)); }