示例#1
0
        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();
        }
示例#2
0
        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);
                }
            }
        }
示例#3
0
        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();
        }
示例#4
0
            // 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();
            }
示例#5
0
            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?
            }
示例#6
0
        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);
        }
示例#7
0
        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?
            }
示例#11
0
        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);
        }
示例#12
0
            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();
            }
示例#13
0
            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?
            }
示例#14
0
        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);
        }
示例#15
0
        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);
        }
示例#16
0
        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);
        }
示例#17
0
        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);
        }
示例#19
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();
        }
示例#22
0
 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);
     }
 }
示例#24
0
        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);
        }
示例#25
0
 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();
            }
        }
示例#27
0
        /// <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);
        }
示例#28
0
            /**
             * 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();
                }
            }
        }
示例#30
0
        /// <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();
                }
            }
        }
示例#31
0
        protected override void DoWrite(ChannelOutboundBuffer input)
        {
            for (;;)
            {
                object msg = input.Current;
                if (msg == null)
                {
                    break;
                }

                ReferenceCountUtil.Retain(msg);
                this.OutboundMessages.Enqueue(msg);
                input.Remove();
            }
        }
示例#32
0
            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);
示例#34
0
        //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);
            }
        }
示例#35
0
        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)
 {
     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();
        }
示例#40
0
 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);
 }
示例#41
0
 /// <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;
                    }
                }
            }
        }