void RunFinishPeerReadTask(LocalChannel peer)
 {
     // If the peer is writing, we must wait until after reads are completed for that peer before we can read. So
     // we keep track of the task, and coordinate later that our read can't happen until the peer is done.
     try
     {
         if (peer.writeInProgress)
         {
             peer.finishReadFuture = peer.EventLoop.SubmitAsync(
                 () =>
             {
                 this.FinishPeerRead0(peer);
                 return((object)null);
             });
         }
         else
         {
             peer.EventLoop.Execute(() => this.FinishPeerRead0(peer));
         }
     }
     catch (Exception cause)
     {
         Logger.Warn("Closing Local channels {}-{} because exception occurred!", this, peer, cause);
         this.CloseAsync();
         peer.CloseAsync();
         throw;
     }
 }
        void FinishPeerRead0(LocalChannel peer)
        {
            Task peerFinishReadFuture = peer.finishReadFuture;

            if (peerFinishReadFuture != null)
            {
                if (!peerFinishReadFuture.IsCompleted)
                {
                    this.RunFinishPeerReadTask(peer);
                    return;
                }
                else
                {
                    // Lazy unset to make sure we don't prematurely unset it while scheduling a new task.
                    Interlocked.CompareExchange(ref peer.finishReadFuture, null, peerFinishReadFuture);
                }
            }

            IChannelPipeline peerPipeline = peer.Pipeline;

            if (peer.readInProgress)
            {
                peer.readInProgress = false;
                while (peer.inboundBuffer.TryDequeue(out object received))
                {
                    peerPipeline.FireChannelRead(received);
                }
                peerPipeline.FireChannelReadComplete();
            }
        }
        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);
        }
예제 #4
0
        void Serve0(LocalChannel child)
        {
            _ = _inboundBuffer.TryEnqueue(child);

            if (SharedConstants.False < (uint)Volatile.Read(ref v_acceptInProgress))
            {
                _ = Interlocked.Exchange(ref v_acceptInProgress, SharedConstants.False);
                ReadInbound();
            }
        }
 void FinishPeerRead(LocalChannel peer)
 {
     // If the peer is also writing, then we must schedule the event on the event loop to preserve read order.
     if (peer.EventLoop == this.EventLoop && !peer.writeInProgress)
     {
         this.FinishPeerRead0(peer);
     }
     else
     {
         this.RunFinishPeerReadTask(peer);
     }
 }
예제 #6
0
        public LocalChannel Serve(LocalChannel peer)
        {
            LocalChannel child = this.NewLocalChannel(peer);

            if (this.EventLoop.InEventLoop)
            {
                this.Serve0(child);
            }
            else
            {
                this.EventLoop.Execute(() => this.Serve0(child));
            }
            return(child);
        }
예제 #7
0
        public LocalChannel Serve(LocalChannel peer)
        {
            LocalChannel child = NewLocalChannel(peer);

            if (EventLoop.InEventLoop)
            {
                Serve0(child);
            }
            else
            {
                EventLoop.Execute(() => Serve0(child));
            }
            return(child);
        }
예제 #8
0
        void Serve0(LocalChannel child)
        {
            this.inboundBuffer.TryEnqueue(child);

            if (this.acceptInProgress)
            {
                this.acceptInProgress = false;
                IChannelPipeline pipeline = this.Pipeline;
                while (this.inboundBuffer.TryDequeue(out object m))
                {
                    pipeline.FireChannelRead(m);
                }
                pipeline.FireChannelReadComplete();
            }
        }
        internal LocalChannel(LocalServerChannel parent, LocalChannel peer)
            : base(parent)
        {
            //this.Configuration.Allocator(new PreferHeapByteBufAllocator(config.getAllocator()));
            this.peer = peer;
            if (parent != null)
            {
                this.localAddress = parent.LocalAddress;
            }
            if (peer != null)
            {
                this.remoteAddress = peer.LocalAddress;
            }

            this.Configuration = new DefaultChannelConfiguration(this);
            this.shutdownHook  = () => this.Unsafe.CloseAsync();
        }
예제 #10
0
 /// <summary>
 /// A factory method for <see cref="LocalChannel"/>s. Users may override it to create custom instances of <see cref="LocalChannel"/>s.
 /// </summary>
 /// <param name="peer">An existing <see cref="LocalChannel"/> that will act as a peer for the new channel.</param>
 /// <returns>The newly created <see cref="LocalChannel"/> instance.</returns>
 protected LocalChannel NewLocalChannel(LocalChannel peer) => new LocalChannel(this, peer);
 public LocalUnsafe(LocalChannel channel)
     : base(channel)
 {
     this.localChannel = channel;
 }
        protected override void DoClose()
        {
            var peer     = this.peer;
            var oldState = this.state;

            try
            {
                if (oldState != State.Closed)
                {
                    // Update all internal state before the closeFuture is notified.
                    if (this.localAddress != null)
                    {
                        if (this.Parent == null)
                        {
                            LocalChannelRegistry.Unregister(this.localAddress);
                        }
                        this.localAddress = null;
                    }

                    // State change must happen before finishPeerRead to ensure writes are released either in doWrite or
                    // channelRead.
                    this.state = State.Closed;

                    // Preserve order of event and force a read operation now before the close operation is processed.
                    this.FinishPeerRead(this);

                    TaskCompletionSource promise = this.connectPromise;
                    if (promise != null)
                    {
                        // Use tryFailure() instead of setFailure() to avoid the race against cancel().
                        promise.TrySetException(DoCloseClosedChannelException);
                        this.connectPromise = null;
                    }
                }

                if (peer != null)
                {
                    this.peer = null;
                    // Need to execute the close in the correct EventLoop (see https://github.com/netty/netty/issues/1777).
                    // Also check if the registration was not done yet. In this case we submit the close to the EventLoop
                    // to make sure its run after the registration completes
                    // (see https://github.com/netty/netty/issues/2144).
                    IEventLoop peerEventLoop = peer.EventLoop;
                    bool       peerIsActive  = peer.Active;
                    if (peerEventLoop.InEventLoop && !this.registerInProgress)
                    {
                        peer.TryClose(peerIsActive);
                    }
                    else
                    {
                        try
                        {
                            peerEventLoop.Execute(() => peer.TryClose(peerIsActive));
                        }
                        catch (Exception cause)
                        {
                            Logger.Warn("Releasing Inbound Queues for channels {}-{} because exception occurred!", this, peer, cause);

                            if (peerEventLoop.InEventLoop)
                            {
                                peer.ReleaseInboundBuffers();
                            }
                            else
                            {
                                // inboundBuffers is a SPSC so we may leak if the event loop is shutdown prematurely or
                                // rejects the close Runnable but give a best effort.
                                peer.CloseAsync();
                            }
                            throw;
                        }
                    }
                }
            }
            finally
            {
                // Release all buffers if the Channel was already registered in the past and if it was not closed before.
                if (oldState != State.Closed)
                {
                    // We need to release all the buffers that may be put into our inbound queue since we closed the Channel
                    // to ensure we not leak any memory. This is fine as it basically gives the same guarantees as TCP which
                    // means even if the promise was notified before its not really guaranteed that the "remote peer" will
                    // see the buffer at all.
                    this.ReleaseInboundBuffers();
                }
            }
        }