public void TestNioBuffersExpand2() { TestChannel channel = new TestChannel(); ChannelOutboundBuffer buffer = new ChannelOutboundBuffer(channel); CompositeByteBuffer comp = Unpooled.CompositeBuffer(256); IByteBuffer buf = Unpooled.DirectBuffer().WriteBytes(Encoding.ASCII.GetBytes("buf1")); for (int i = 0; i < 65; i++) { comp.AddComponent(true, buf.Copy()); } buffer.AddMessage(comp, comp.ReadableBytes, channel.VoidPromise()); Assert.Equal(0, buffer.NioBufferCount); // Should still be 0 as not flushed yet buffer.AddFlush(); var buffers = buffer.GetSharedBufferList(); Assert.Equal(65, buffer.NioBufferCount); for (int i = 0; i < buffer.NioBufferCount; i++) { if (i < 65) { Assert.Equal(buffers[i], buf.GetIoBuffer(buf.ReaderIndex, buf.ReadableBytes)); } else { Assert.Null(buffers[i].Array); } } Release(buffer); buf.Release(); }
void Write(ChannelOutboundBuffer input) { while (true) { int size = input.Count; if (size == 0) { break; } List <ArraySegment <byte> > nioBuffers = input.GetSharedBufferList(); int nioBufferCnt = nioBuffers.Count; long expectedWrittenBytes = input.NioBufferSize; if (nioBufferCnt == 0) { this.WriteByteBuffers(input); return; } else { WriteRequest writeRequest = Recycler.Take(); writeRequest.Prepare((TcpChannelUnsafe)this.Unsafe, nioBuffers); this.tcp.Write(writeRequest); input.RemoveBytes(expectedWrittenBytes); } } }
public void TestNioBuffersMaxCount() { TestChannel channel = new TestChannel(); ChannelOutboundBuffer buffer = new ChannelOutboundBuffer(channel); CompositeByteBuffer comp = Unpooled.CompositeBuffer(256); IByteBuffer buf = Unpooled.DirectBuffer().WriteBytes(Encoding.ASCII.GetBytes("buf1")); for (int i = 0; i < 65; i++) { comp.AddComponent(true, buf.Copy()); } Assert.Equal(65, comp.IoBufferCount); buffer.AddMessage(comp, comp.ReadableBytes, channel.VoidPromise()); Assert.Equal(0, buffer.NioBufferCount); // Should still be 0 as not flushed yet buffer.AddFlush(); int maxCount = 10; // less than comp.nioBufferCount() var buffers = buffer.GetSharedBufferList(maxCount, int.MaxValue); Assert.True(buffer.NioBufferCount <= maxCount); // Should not be greater than maxCount for (int i = 0; i < buffer.NioBufferCount; i++) { Assert.Equal(buffers[i], buf.GetIoBuffer(buf.ReaderIndex, buf.ReadableBytes)); } Release(buffer); buf.Release(); }
// Write request callback from libuv thread void INativeUnsafe.FinishWrite(int bytesWritten, OperationException error) { var ch = _channel; bool resetWritePending = ch.TryResetState(StateFlags.WriteScheduled); Debug.Assert(resetWritePending); try { ChannelOutboundBuffer input = OutboundBuffer; if (error is object) { input.FailFlushed(error, true); _ = ch.Pipeline.FireExceptionCaught(error); Close(VoidPromise(), ThrowHelper.GetChannelException_FailedToWrite(error), WriteClosedChannelException, false); } else { if (bytesWritten > 0) { input.RemoveBytes(bytesWritten); } } } catch (Exception ex) { Close(VoidPromise(), ThrowHelper.GetClosedChannelException_FailedToWrite(ex), WriteClosedChannelException, false); } Flush0(); }
public void FinishWrite(SocketChannelAsyncOperation <TChannel, TUnsafe> operation) { var ch = _channel; bool resetWritePending = ch.TryResetState(StateFlags.WriteScheduled); Debug.Assert(resetWritePending); ChannelOutboundBuffer input = OutboundBuffer; try { operation.Validate(); int sent = operation.BytesTransferred; ch.ResetWriteOperation(); if (sent > 0) { input.RemoveBytes(sent); } } catch (Exception ex) { _ = ch.Pipeline.FireExceptionCaught(ex); Close(VoidPromise(), ThrowHelper.GetClosedChannelException_FailedToWrite(ex), WriteClosedChannelException, false); } // Double check if there's no pending flush // See https://github.com/Azure/DotNetty/issues/218 Flush0(); // todo: does it make sense now that we've actually written out everything that was flushed previously? concurrent flush handling? }
protected override void DoWrite(ChannelOutboundBuffer buffer) { switch (Volatile.Read(ref v_state)) { case State.Open: case State.Bound: ThrowHelper.ThrowNotYetConnectedException(); break; case State.Closed: ThrowHelper.ThrowDoWriteClosedChannelException(); break; case State.Connected: break; } var peer = Volatile.Read(ref v_peer); _ = Interlocked.Exchange(ref v_writeInProgress, SharedConstants.True); try { while (true) { object msg = buffer.Current; if (msg is null) { break; } try { // It is possible the peer could have closed while we are writing, and in this case we should // simulate real socket behavior and ensure the write operation is failed. if (Volatile.Read(ref peer.v_state) == State.Connected) { _ = peer._inboundBuffer.TryEnqueue(ReferenceCountUtil.Retain(msg)); _ = buffer.Remove(); } else { _ = buffer.Remove(DoWriteClosedChannelException); } } catch (Exception cause) { _ = buffer.Remove(cause); } } } finally { // The following situation may cause trouble: // 1. Write (with promise X) // 2. promise X is completed when in.remove() is called, and a listener on this promise calls close() // 3. Then the close event will be executed for the peer before the write events, when the write events // actually happened before the close event. _ = Interlocked.Exchange(ref v_writeInProgress, SharedConstants.False); } FinishPeerRead(peer); }
public Property ChannelOutboundBuffer_must_always_correctly_report_writability_and_PendingBytes( IByteBuf[] writes) { var writeable = true; var buffer = new ChannelOutboundBuffer(TestChannel.Instance, () => { writeable = !writeable; // toggle writeability }); foreach (var msg in writes) { var initialSize = buffer.TotalPendingWriteBytes; var nextSize = initialSize + msg.ReadableBytes; var currentBytesUntilUnwriteable = buffer.BytesBeforeUnwritable; var currentBytesUntilWriteable = buffer.BytesBeforeWritable; var currentWriteability = writeable; long nextBytesUntilWriteable, nextBytesUntilUnwriteable; bool nextWritability; if (writeable) { if (msg.ReadableBytes < currentBytesUntilUnwriteable) // should stay writable { nextWritability = writeable; nextBytesUntilUnwriteable = currentBytesUntilUnwriteable - msg.ReadableBytes; nextBytesUntilWriteable = 0; // already writable } else // should become unwriteable { nextWritability = !writeable; nextBytesUntilWriteable = nextSize - WriteLowWaterMark; nextBytesUntilUnwriteable = 0; } } else //already unwriteable { nextWritability = writeable; nextBytesUntilWriteable = nextSize - WriteLowWaterMark; nextBytesUntilUnwriteable = 0; } buffer.AddMessage(msg, msg.ReadableBytes, TaskCompletionSource.Void); var satisfiesGrowthPrediction = nextWritability == writeable && nextBytesUntilUnwriteable == buffer.BytesBeforeUnwritable && nextBytesUntilWriteable == buffer.BytesBeforeWritable && nextSize == buffer.TotalPendingWriteBytes; if (!satisfiesGrowthPrediction) { return (false.Label( $"Buffer failed to meet growth prediction for initial buffer size {initialSize} with next write of size {msg.ReadableBytes}." + $"TotalPendingWriteBytes(ex={nextSize}, " + $"ac={buffer.TotalPendingWriteBytes}), " + $"Writability(ex={nextWritability}, ac={writeable}), " + $"BytesUntilUnwriteable(ex={nextBytesUntilUnwriteable}, ac={buffer.BytesBeforeUnwritable}), " + $"BytesUntilWriteable(ex={nextBytesUntilWriteable}, ac={buffer.BytesBeforeWritable})")); } } return(true.ToProperty()); }
protected override void DoWrite(ChannelOutboundBuffer buffer) { switch (this.state) { case State.Open: case State.Bound: throw new NotYetConnectedException(); case State.Closed: throw DoWriteClosedChannelException; case State.Connected: break; } LocalChannel peer = this.peer; this.writeInProgress = true; try { for (;;) { object msg = buffer.Current; if (msg == null) { break; } try { // It is possible the peer could have closed while we are writing, and in this case we should // simulate real socket behavior and ensure the write operation is failed. if (peer.state == State.Connected) { peer.inboundBuffer.TryEnqueue(ReferenceCountUtil.Retain(msg)); buffer.Remove(); } else { buffer.Remove(DoWriteClosedChannelException); } } catch (Exception cause) { buffer.Remove(cause); } } } finally { // The following situation may cause trouble: // 1. Write (with promise X) // 2. promise X is completed when in.remove() is called, and a listener on this promise calls close() // 3. Then the close event will be executed for the peer before the write events, when the write events // actually happened before the close event. this.writeInProgress = false; } this.FinishPeerRead(peer); }
// Write request callback from libuv thread void INativeUnsafe.FinishWrite(int bytesWritten, OperationException error) { var ch = (NativeChannel)this.channel; bool resetWritePending = ch.TryResetState(StateFlags.WriteScheduled); Debug.Assert(resetWritePending); try { ChannelOutboundBuffer input = this.OutboundBuffer; if (error != null) { input.FailFlushed(error, true); CloseSafe(ch, this.CloseAsync(new ChannelException("Failed to write", error), false)); } else { if (bytesWritten > 0) { input.RemoveBytes(bytesWritten); } } } catch (Exception ex) { CloseSafe(ch, this.CloseAsync(new ClosedChannelException("Failed to write", ex), false)); } this.Flush0(); }
public void FinishWrite(SocketChannelAsyncOperation operation) { bool resetWritePending = this.Channel.TryResetState(StateFlags.WriteScheduled); Contract.Assert(resetWritePending); ChannelOutboundBuffer input = this.OutboundBuffer; try { operation.Validate(); int sent = operation.BytesTransferred; this.Channel.ResetWriteOperation(); if (sent > 0) { input.RemoveBytes(sent); } } catch (Exception ex) { input.FailFlushed(ex, true); throw; } // Double check if there's no pending flush // See https://github.com/Azure/DotNetty/issues/218 this.Flush0(); // todo: does it make sense now that we've actually written out everything that was flushed previously? concurrent flush handling? }
public void TestUserDefinedWritability() { StringBuilder buf = new StringBuilder(); EmbeddedChannel ch = new EmbeddedChannel(new ChannelInboundHandlerAdapter0(buf)); ch.Configuration.WriteBufferLowWaterMark = (128 + ChannelOutboundBuffer.ChannelOutboundBufferEntryOverhead); ch.Configuration.WriteBufferHighWaterMark = (256 + ChannelOutboundBuffer.ChannelOutboundBufferEntryOverhead); ChannelOutboundBuffer cob = ch.Unsafe.OutboundBuffer; // Ensure that the default value of a user-defined writability flag is true. for (int i = 1; i <= 30; i++) { Assert.True(cob.GetUserDefinedWritability(i)); } // Ensure that setting a user-defined writability flag to false affects channel.isWritable(); cob.SetUserDefinedWritability(1, false); ch.RunPendingTasks(); Assert.Equal("False ", buf.ToString()); // Ensure that setting a user-defined writability flag to true affects channel.isWritable(); cob.SetUserDefinedWritability(1, true); ch.RunPendingTasks(); Assert.Equal("False True ", buf.ToString()); SafeClose(ch); }
public void FinishWrite(SocketChannelAsyncOperation operation) { ChannelOutboundBuffer input = this.OutboundBuffer; try { operation.Validate(); int sent = operation.BytesTransferred; this.Channel.ResetWriteOperation(); if (sent > 0) { object msg = input.Current; var buffer = msg as IByteBuffer; if (buffer != null) { buffer.SetWriterIndex(buffer.WriterIndex + sent); } // todo: FileRegion support } } catch (Exception ex) { input.FailFlushed(ex, true); throw; } // directly call super.flush0() to force a flush now base.Flush0(); }
public void FinishWrite(SocketChannelAsyncOperation operation) { bool resetWritePending = this.Channel.TryResetState(StateFlags.WriteScheduled); Contract.Assert(resetWritePending); ChannelOutboundBuffer input = this.OutboundBuffer; try { operation.Validate(); int sent = operation.BytesTransferred; this.Channel.ResetWriteOperation(); if (sent > 0) { input.RemoveBytes(sent); } } catch (Exception ex) { input.FailFlushed(ex, true); throw; } // directly call base.Flush0() to force a flush now base.Flush0(); // todo: does it make sense now that we've actually written out everything that was flushed previously? concurrent flush handling? }
public void TestNioBuffersSingleBacked() { TestChannel channel = new TestChannel(); ChannelOutboundBuffer buffer = new ChannelOutboundBuffer(channel); Assert.Equal(0, buffer.NioBufferCount); var buf = Unpooled.CopiedBuffer("buf1", Encoding.ASCII); var nioBuf = buf.GetIoBuffer(buf.ReaderIndex, buf.ReadableBytes); buffer.AddMessage(buf, buf.ReadableBytes, channel.VoidPromise()); Assert.Equal(0, buffer.NioBufferCount); // Should still be 0 as not flushed yet buffer.AddFlush(); var buffers = buffer.GetSharedBufferList(); Assert.NotNull(buffers); Assert.Equal(1, buffer.NioBufferCount); // Should still be 0 as not flushed yet for (int i = 0; i < buffer.NioBufferCount; i++) { if (i == 0) { Assert.Equal(buffers[i], nioBuf); } else { Assert.Null(buffers[i].Array); } } Release(buffer); }
public void TestMixedWritability() { StringBuilder buf = new StringBuilder(); EmbeddedChannel ch = new EmbeddedChannel(new ChannelInboundHandlerAdapter0(buf)); ch.Configuration.WriteBufferLowWaterMark = 128; ch.Configuration.WriteBufferHighWaterMark = 256; ChannelOutboundBuffer cob = ch.Unsafe.OutboundBuffer; // Trigger channelWritabilityChanged() by writing a lot. ch.WriteAsync(Unpooled.Buffer().WriteZero(257)); Assert.Equal("False ", buf.ToString()); // Ensure that setting a user-defined writability flag to false does not trigger channelWritabilityChanged() cob.SetUserDefinedWritability(1, false); ch.RunPendingTasks(); Assert.Equal("False ", buf.ToString()); // Ensure reducing the totalPendingWriteBytes down to zero does not trigger channelWritabilityChanged() // because of the user-defined writability flag. ch.Flush(); Assert.Equal(0L, cob.TotalPendingWriteBytes); Assert.Equal("False ", buf.ToString()); // Ensure that setting all user-defined writability flags to true affects channel.isWritable() cob.SetUserDefinedWritability(1, true); ch.RunPendingTasks(); Assert.Equal("False True ", buf.ToString()); SafeClose(ch); }
public void TestUserDefinedWritability2() { StringBuilder buf = new StringBuilder(); EmbeddedChannel ch = new EmbeddedChannel(new ChannelInboundHandlerAdapter0(buf)); ch.Configuration.WriteBufferLowWaterMark = 128; ch.Configuration.WriteBufferHighWaterMark = 256; ChannelOutboundBuffer cob = ch.Unsafe.OutboundBuffer; // Ensure that setting a user-defined writability flag to false affects channel.isWritable() cob.SetUserDefinedWritability(1, false); ch.RunPendingTasks(); Assert.Equal("False ", buf.ToString()); // Ensure that setting another user-defined writability flag to false does not trigger // channelWritabilityChanged. cob.SetUserDefinedWritability(2, false); ch.RunPendingTasks(); Assert.Equal("False ", buf.ToString()); // Ensure that setting only one user-defined writability flag to true does not affect channel.isWritable() cob.SetUserDefinedWritability(1, true); ch.RunPendingTasks(); Assert.Equal("False ", buf.ToString()); // Ensure that setting all user-defined writability flags to true affects channel.isWritable() cob.SetUserDefinedWritability(2, true); ch.RunPendingTasks(); Assert.Equal("False True ", buf.ToString()); SafeClose(ch); }
public Property ChannelOutboundBuffer_must_dump_all_flushed_messages_upon_failure(IByteBuf[] writes) { var tasks = new List <Task>(); var writeable = true; var buffer = new ChannelOutboundBuffer(TestChannel.NewInstance(new ExceptionSupressor()), () => { writeable = !writeable; // toggle writeability }); // write foreach (var message in writes) { var tcs = new TaskCompletionSource(); tasks.Add(tcs.Task); buffer.AddMessage(message, message.ReadableBytes, tcs); } buffer.AddFlush(); // flush em var completion = Task.WhenAll(tasks); // err var exception = new ApplicationException("TEST"); buffer.FailFlushed(exception, true); return((completion.IsFaulted && buffer.IsWritable && tasks.All(x => x.IsFaulted && x.Exception.InnerExceptions.Contains(exception))) .When(writes.Length > 0) .Label( "Expected all flushed messages to be faulted with the same exception upon FailFlush, and for buffer to be writable.")); }
protected override bool DoWriteMessage(object msg, ChannelOutboundBuffer input) { EndPoint remoteAddress = null; IByteBuffer data = null; var envelope = msg as IAddressedEnvelope <IByteBuffer>; if (envelope != null) { remoteAddress = envelope.Recipient; data = envelope.Content; } else if (msg is IByteBuffer) { data = (IByteBuffer)msg; remoteAddress = this.RemoteAddressInternal; } if (data == null || remoteAddress == null) { return(false); } int length = data.ReadableBytes; if (length == 0) { return(true); } ArraySegment <byte> bytes = data.GetIoBuffer(data.ReaderIndex, length); int writtenBytes = this.Socket.SendTo(bytes.Array, bytes.Offset, bytes.Count, SocketFlags.None, remoteAddress); return(writtenBytes > 0); }
internal void DoWrite(INativeUnsafe channelUnsafe, ChannelOutboundBuffer input) { Debug.Assert(_nativeUnsafe is null); _nativeUnsafe = channelUnsafe; input.ForEachFlushedMessage(this); DoWrite(); }
internal void DoWrite(NativeChannel.INativeUnsafe channelUnsafe, ChannelOutboundBuffer input) { Debug.Assert(this.nativeUnsafe == null); this.nativeUnsafe = channelUnsafe; input.ForEachFlushedMessage(this); this.DoWrite(); }
public Property ChannelOutboundBuffer_must_always_correctly_report_writability_and_PendingBytes( IByteBuf[] writes) { var writeable = true; var buffer = new ChannelOutboundBuffer(TestChannel.Instance, () => { writeable = !writeable; // toggle writeability }); foreach (var msg in writes) { var initialSize = buffer.TotalPendingWriteBytes; var nextSize = initialSize + msg.ReadableBytes; var currentBytesUntilUnwriteable = buffer.BytesBeforeUnwritable; var currentBytesUntilWriteable = buffer.BytesBeforeWritable; var currentWriteability = writeable; long nextBytesUntilWriteable, nextBytesUntilUnwriteable; bool nextWritability; if (writeable) { if (msg.ReadableBytes < currentBytesUntilUnwriteable) // should stay writable { nextWritability = writeable; nextBytesUntilUnwriteable = currentBytesUntilUnwriteable - msg.ReadableBytes; nextBytesUntilWriteable = 0; // already writable } else // should become unwriteable { nextWritability = !writeable; nextBytesUntilWriteable = nextSize - WriteLowWaterMark; nextBytesUntilUnwriteable = 0; } } else //already unwriteable { nextWritability = writeable; nextBytesUntilWriteable = nextSize - WriteLowWaterMark; nextBytesUntilUnwriteable = 0; } buffer.AddMessage(msg, msg.ReadableBytes, TaskCompletionSource.Void); var satisfiesGrowthPrediction = nextWritability == writeable && nextBytesUntilUnwriteable == buffer.BytesBeforeUnwritable && nextBytesUntilWriteable == buffer.BytesBeforeWritable && nextSize == buffer.TotalPendingWriteBytes; if (!satisfiesGrowthPrediction) return false.Label( $"Buffer failed to meet growth prediction for initial buffer size {initialSize} with next write of size {msg.ReadableBytes}." + $"TotalPendingWriteBytes(ex={nextSize}, " + $"ac={buffer.TotalPendingWriteBytes}), " + $"Writability(ex={nextWritability}, ac={writeable}), " + $"BytesUntilUnwriteable(ex={nextBytesUntilUnwriteable}, ac={buffer.BytesBeforeUnwritable}), " + $"BytesUntilWriteable(ex={nextBytesUntilWriteable}, ac={buffer.BytesBeforeWritable})"); } return true.ToProperty(); }
private static void Release(ChannelOutboundBuffer buffer) { for (; ;) { if (!buffer.Remove()) { break; } } }
protected override void DoWrite(ChannelOutboundBuffer input) { if (input.Size > 0) { this.SetState(StateFlags.WriteScheduled); var loopExecutor = (LoopExecutor)this.EventLoop; WriteRequest request = loopExecutor.WriteRequestPool.Take(); request.DoWrite((TcpChannelUnsafe)this.Unsafe, input); } }
protected override void DoWrite(ChannelOutboundBuffer input) { switch (_state) { case State.Open: case State.Bound: throw NotYetConnectedException.Instance; case State.Closed: throw ClosedChannelException.Instance; case State.Connected: break; } var peer = _peer; _writeInProgress = true; try { while (true) { var msg = input.Current; if (msg == null) { break; } try { // It is possible the peer could have closed while we are writing, and in this case we should // simulate real socket behavior and ensure the write operation is failed. if (peer._state == State.Connected) { peer._inboundBuffer.Enqueue(ReferenceCountUtil.Retain(msg)); input.Remove(); } else { input.Remove(ClosedChannelException.Instance); } } catch (Exception ex) { input.Remove(ex); } } } finally { _writeInProgress = false; } FinishPeerRead(peer); }
protected override void DoWrite(ChannelOutboundBuffer input) { if (this.EventLoop.InEventLoop) { this.Write(input); } else { this.EventLoop.Execute(WriteAction, this, input); } }
protected override async void DoWrite(ChannelOutboundBuffer channelOutboundBuffer) { try { // // All data is collected into one array before being written out // byte[] allbytes = null; this.WriteInProgress = true; while (true) { object currentMessage = channelOutboundBuffer.Current; if (currentMessage == null) { // Wrote all messages break; } var byteBuffer = currentMessage as IByteBuffer; Debug.Assert(byteBuffer != null); if (byteBuffer.IsReadable()) { if (allbytes == null) { allbytes = new byte[byteBuffer.ReadableBytes]; byteBuffer.GetBytes(0, allbytes); } else { int oldLen = allbytes.Length; Array.Resize(ref allbytes, allbytes.Length + byteBuffer.ReadableBytes); byteBuffer.GetBytes(0, allbytes, oldLen, byteBuffer.ReadableBytes); } } channelOutboundBuffer.Remove(); } uint result = await this.streamSocket.OutputStream.WriteAsync(allbytes.AsBuffer()); this.WriteInProgress = false; } catch (Exception e) { // Since this method returns void, all exceptions must be handled here. this.WriteInProgress = false; this.Pipeline.FireExceptionCaught(e); await this.CloseAsync(); } }
/// <summary> /// Returns <c>true</c> if and only if the <see cref="IdleStateHandler.IdleStateHandler(bool, TimeSpan, TimeSpan, TimeSpan)"/> /// was constructed /// with <code>observeOutput</code> enabled and there has been an observed change in the /// <see cref="ChannelOutboundBuffer"/> between two consecutive calls of this method. /// https://github.com/netty/netty/issues/6150 /// </summary> /// <param name="ctx"></param> /// <param name="first"></param> /// <returns></returns> private bool HasOutputChanged(IChannelHandlerContext ctx, bool first) { if (_observeOutput) { // We can take this shortcut if the ChannelPromises that got passed into write() // appear to complete. It indicates "change" on message level and we simply assume // that there's change happening on byte level. If the user doesn't observe channel // writability events then they'll eventually OOME and there's clearly a different // problem and idleness is least of their concerns. if (_lastChangeCheckTimeStamp != _lastWriteTime) { _lastChangeCheckTimeStamp = _lastWriteTime; // But this applies only if it's the non-first call. if (!first) { return(true); } } ChannelOutboundBuffer buf = ctx.Channel.Unsafe.OutboundBuffer; if (buf is object) { int messageHashCode = RuntimeHelpers.GetHashCode(buf.Current); long pendingWriteBytes = buf.TotalPendingWriteBytes; if (messageHashCode != _lastMessageHashCode || pendingWriteBytes != _lastPendingWriteBytes) { _lastMessageHashCode = messageHashCode; _lastPendingWriteBytes = pendingWriteBytes; if (!first) { return(true); } } long flushProgress = buf.CurrentProgress(); if (flushProgress != _lastFlushProgress) { _lastFlushProgress = flushProgress; if (!first) { return(true); } } } } return(false); }
/** * Consume the part of a message. * * @param byteCount count of byte to be consumed * @return the message currently being consumed */ public object ConsumePart(int byteCount) { ChannelOutboundBuffer buf = this.Unsafe.OutboundBuffer; object msg = buf?.Current; if (msg != null) { ReferenceCountUtil.Retain(msg); buf.RemoveBytes(byteCount); return(msg); } return(null); }
/// <summary> /// <see cref="HasOutputChanged(IChannelHandlerContext, bool)"/> /// </summary> /// <param name="ctx"></param> private void InitOutputChanged(IChannelHandlerContext ctx) { if (observeOutput) { ChannelOutboundBuffer buf = ctx.Channel.Unsafe.OutboundBuffer; if (buf != null) { lastMessageHashCode = RuntimeHelpers.GetHashCode(buf.Current); lastPendingWriteBytes = buf.TotalPendingWriteBytes(); } } }
/// <summary> /// <see cref="HasOutputChanged(IChannelHandlerContext, bool)"/> /// </summary> /// <param name="ctx"></param> private void InitOutputChanged(IChannelHandlerContext ctx) { if (_observeOutput) { ChannelOutboundBuffer buf = ctx.Channel.Unsafe.OutboundBuffer; if (buf is object) { _lastMessageHashCode = RuntimeHelpers.GetHashCode(buf.Current); _lastPendingWriteBytes = buf.TotalPendingWriteBytes; _lastFlushProgress = buf.CurrentProgress(); } } }
protected override void DoWrite(ChannelOutboundBuffer input) { for (;;) { object msg = input.Current; if (msg == null) { break; } ReferenceCountUtil.Retain(msg); this.OutboundMessages.Enqueue(msg); input.Remove(); } }
public Task CloseAsync() //CancellationToken cancellationToken) { var promise = new TaskCompletionSource(); if (!promise.SetUncancellable()) { return promise.Task; } //if (cancellationToken.IsCancellationRequested) //{ // return TaskEx.Cancelled; //} if (this.outboundBuffer == null) { // Only needed if no VoidChannelPromise. if (promise != TaskCompletionSource.Void) { // This means close() was called before so we just register a listener and return return this._channel._closeTask.Task; } return promise.Task; } if (this._channel._closeTask.Task.IsCompleted) { // Closed already. PromiseUtil.SafeSetSuccess(promise, Logger); return promise.Task; } bool wasActive = this._channel.IsActive; ChannelOutboundBuffer buffer = this.outboundBuffer; this.outboundBuffer = null; // Disallow adding any messages and flushes to outboundBuffer. try { // Close the channel and fail the queued messages input all cases. this.DoClose0(promise); } finally { // Fail all the queued messages. buffer.FailFlushed(ClosedChannelException.Instance, false); buffer.Close(ClosedChannelException.Instance); } if (this.inFlush0) { this.InvokeLater(() => this.FireChannelInactiveAndDeregister(wasActive)); } else { this.FireChannelInactiveAndDeregister(wasActive); } return promise.Task; }
/// <summary> /// Write a message to the underlying {@link java.nio.channels.Channel}. /// /// @return {@code true} if and only if the message has been written /// </summary> protected abstract bool DoWriteMessage(object msg, ChannelOutboundBuffer input);
//protected long doWriteFileRegion(FileRegion region) //{ // long position = region.transfered(); // return region.transferTo(javaChannel(), position); //} protected override void DoWrite(ChannelOutboundBuffer input) { while (true) { int size = input.Count; if (size == 0) { // All written break; } long writtenBytes = 0; bool done = false; bool setOpWrite = false; // Ensure the pending writes are made of ByteBufs only. List<ArraySegment<byte>> nioBuffers = input.GetNioBuffers(); int nioBufferCnt = nioBuffers.Count; long expectedWrittenBytes = input.NioBufferSize; Socket socket = this.Socket; // Always us nioBuffers() to workaround data-corruption. // See https://github.com/netty/netty/issues/2761 switch (nioBufferCnt) { case 0: // We have something else beside ByteBuffers to write so fallback to normal writes. base.DoWrite(input); return; case 1: // Only one ByteBuf so use non-gathering write ArraySegment<byte> nioBuffer = nioBuffers[0]; for (int i = this.Configuration.WriteSpinCount - 1; i >= 0; i--) { SocketError errorCode; int localWrittenBytes = socket.Send(nioBuffer.Array, nioBuffer.Offset, nioBuffer.Count, SocketFlags.None, out errorCode); if (errorCode != SocketError.Success && errorCode != SocketError.WouldBlock) { throw new SocketException((int)errorCode); } if (localWrittenBytes == 0) { setOpWrite = true; break; } expectedWrittenBytes -= localWrittenBytes; writtenBytes += localWrittenBytes; if (expectedWrittenBytes == 0) { done = true; break; } } break; default: for (int i = this.Configuration.WriteSpinCount - 1; i >= 0; i--) { SocketError errorCode; long localWrittenBytes = socket.Send(nioBuffers, SocketFlags.None, out errorCode); if (errorCode != SocketError.Success && errorCode != SocketError.WouldBlock) { throw new SocketException((int)errorCode); } if (localWrittenBytes == 0) { setOpWrite = true; break; } expectedWrittenBytes -= localWrittenBytes; writtenBytes += localWrittenBytes; if (expectedWrittenBytes == 0) { done = true; break; } } break; } if (!done) { SocketChannelAsyncOperation asyncOperation = this.PrepareWriteOperation(nioBuffers); // Release the fully written buffers, and update the indexes of the partially written buffer. input.RemoveBytes(writtenBytes); // Did not write all buffers completely. this.IncompleteWrite(setOpWrite, asyncOperation); break; } // Release the fully written buffers, and update the indexes of the partially written buffer. input.RemoveBytes(writtenBytes); } }
protected override void DoWrite(ChannelOutboundBuffer input) { throw new NotSupportedException(); }
protected override void DoWrite(ChannelOutboundBuffer input) { var writeSpinCount = -1; while (true) { var msg = input.Current; if (msg == null) { // Wrote all messages. break; } if (msg is IByteBuf) { var buf = (IByteBuf) msg; var readableBytes = buf.ReadableBytes; if (readableBytes == 0) { input.Remove(); continue; } var scheduleAsync = false; var done = false; long flushedAmount = 0; if (writeSpinCount == -1) { writeSpinCount = Configuration.WriteSpinCount; } for (var i = writeSpinCount - 1; i >= 0; i--) { var localFlushedAmount = DoWriteBytes(buf); if (localFlushedAmount == 0) // todo: check for "sent less than attempted bytes" to avoid unnecessary extra doWriteBytes call? { scheduleAsync = true; break; } flushedAmount += localFlushedAmount; if (!buf.IsReadable()) { done = true; break; } } //input.Progress(flushedAmount); // todo: support progress reports on ChannelOutboundBuffer if (done) { input.Remove(); } else { IncompleteWrite(scheduleAsync, PrepareWriteOperation(buf.GetIoBuffer())); break; } } else { // Should not reach here. throw new InvalidOperationException(); } } }
public Property ChannelOutboundBuffer_must_complete_all_promises_successfully_on_read_regardless_of_flush_order( IByteBuf[] writes) { var tasks = new List<Task>(); var writeable = true; var buffer = new ChannelOutboundBuffer(TestChannel.Instance, () => { writeable = !writeable; // toggle writeability }); // write + flush loop foreach (var message in writes) { var tcs = new TaskCompletionSource(); tasks.Add(tcs.Task); buffer.AddMessage(message, message.ReadableBytes, tcs); if (ThreadLocalRandom.Current.Next(0, 1) == 0) // just doing this to guarantee we don't screw up the linked list buffer.AddFlush(); } buffer.AddFlush(); // add a final flush in case RNG hasn't let us do one in a while // sanity check if (buffer.Count != writes.Length) return false.Label( $"Expected buffer to have {writes.Length} writes waiting for processing; found {buffer.Count} instead."); var completion = Task.WhenAll(tasks); if (!tasks.TrueForAll(x => x.IsCompleted == false)) return false.Label( "None of the messages have been processed, but still found Tasks reporting successful completion"); do { // process all of the messages } while (buffer.Remove()); return completion.IsCompleted.Label("Expected all underlying tasks to be completed."); }
public Property ChannelOutboundBuffer_must_always_process_pending_writes_in_FIFO_order(IByteBuf[] writes) { if (writes.Length == 0) // skip any zero-length results return true.ToProperty(); var writeable = true; var buffer = new ChannelOutboundBuffer(TestChannel.Instance, () => { writeable = !writeable; // toggle writeability }); foreach (var message in writes) { buffer.AddMessage(message, message.ReadableBytes, TaskCompletionSource.Void); } buffer.AddFlush(); // have to flush in order to put messages into a readable state var comparer = BufferOperations.Comparer; var iterator = writes.GetEnumerator(); iterator.MoveNext(); var position = 0; do { var read = buffer.Current as IByteBuf; var match = comparer.Equals(read, iterator.Current); if (!match) return false.Label( $"ChannelOutboundBuffer item at position {position} did not match the expected value."); position++; } while (buffer.Remove() && iterator.MoveNext()); return true.ToProperty(); }
protected AbstractUnsafe(AbstractChannel channel) { this._channel = channel; InvokeWritabilityChanged = () => { if (channel.EventLoop.InEventLoop) { channel.Pipeline.FireChannelWritabilityChanged(); } else { channel.EventLoop.Execute(InvokeWritabilityChangedUnsafe, channel); } }; this.outboundBuffer = new ChannelOutboundBuffer(channel, InvokeWritabilityChanged); }
/// <summary> /// Flush the content of the given buffer to the remote peer. /// </summary> protected abstract void DoWrite(ChannelOutboundBuffer input);
public Property ChannelOutboundBuffer_must_dump_all_flushed_messages_upon_failure(IByteBuf[] writes) { var tasks = new List<Task>(); var writeable = true; var buffer = new ChannelOutboundBuffer(TestChannel.NewInstance(new ExceptionSupressor()), () => { writeable = !writeable; // toggle writeability }); // write foreach (var message in writes) { var tcs = new TaskCompletionSource(); tasks.Add(tcs.Task); buffer.AddMessage(message, message.ReadableBytes, tcs); } buffer.AddFlush(); // flush em var completion = Task.WhenAll(tasks); // err var exception = new ApplicationException("TEST"); buffer.FailFlushed(exception, true); return (completion.IsFaulted && buffer.IsWritable && tasks.All(x => x.IsFaulted && x.Exception.InnerExceptions.Contains(exception))) .When(writes.Length > 0) .Label( "Expected all flushed messages to be faulted with the same exception upon FailFlush, and for buffer to be writable."); }
protected override void DoWrite(ChannelOutboundBuffer input) { int writeSpinCount = -1; while (true) { object msg = input.Current; if (msg == null) { // Wrote all messages. break; } if (msg is IByteBuffer) { var buf = (IByteBuffer)msg; int readableBytes = buf.ReadableBytes; if (readableBytes == 0) { input.Remove(); continue; } bool scheduleAsync = false; bool done = false; long flushedAmount = 0; if (writeSpinCount == -1) { writeSpinCount = this.Configuration.WriteSpinCount; } for (int i = writeSpinCount - 1; i >= 0; i--) { int localFlushedAmount = this.DoWriteBytes(buf); if (localFlushedAmount == 0) // todo: check for "sent less than attempted bytes" to avoid unnecessary extra doWriteBytes call? { scheduleAsync = true; break; } flushedAmount += localFlushedAmount; if (!buf.IsReadable()) { done = true; break; } } input.Progress(flushedAmount); if (done) { input.Remove(); } else { this.IncompleteWrite(scheduleAsync, buf); break; } } /*else if (msg is FileRegion) { todo: FileRegion support FileRegion region = (FileRegion) msg; bool done = region.transfered() >= region.count(); bool scheduleAsync = false; if (!done) { long flushedAmount = 0; if (writeSpinCount == -1) { writeSpinCount = config().getWriteSpinCount(); } for (int i = writeSpinCount - 1; i >= 0; i--) { long localFlushedAmount = doWriteFileRegion(region); if (localFlushedAmount == 0) { scheduleAsync = true; break; } flushedAmount += localFlushedAmount; if (region.transfered() >= region.count()) { done = true; break; } } input.progress(flushedAmount); } if (done) { input.remove(); } else { incompleteWrite(scheduleAsync); break; } }*/ else { // Should not reach here. throw new InvalidOperationException(); } } }
protected override void DoWrite(ChannelOutboundBuffer input) { while (true) { object msg = input.Current; if (msg == null) { break; } try { bool done = false; for (int i = this.Configuration.WriteSpinCount - 1; i >= 0; i--) { if (this.DoWriteMessage(msg, input)) { done = true; break; } } if (done) { input.Remove(); } else { // Did not write all messages. this.ScheduleMessageWrite(msg); break; } } catch (SocketException e) { if (this.ContinueOnWriteError) { input.Remove(e); } else { throw; } } } }