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); }
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 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); } }
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); }
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(); }
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(); } } }
/// <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);