public override Task ConnectAsync(EndPoint remoteAddress, EndPoint localAddress) { var promise = new TaskCompletionSource(); if (Volatile.Read(ref _channel.v_state) == State.Connected) { var cause = new AlreadyConnectedException(); Util.SafeSetFailure(promise, cause, Logger); _ = _channel.Pipeline.FireExceptionCaught(cause); return(promise.Task); } if (Volatile.Read(ref _channel.v_connectPromise) is object) { ThrowHelper.ThrowConnectionPendingException(); } _ = Interlocked.Exchange(ref _channel.v_connectPromise, promise); if (Volatile.Read(ref _channel.v_state) != State.Bound) { // Not bound yet and no localAddress specified - get one. if (localAddress is null) { localAddress = new LocalAddress(_channel); } } if (localAddress is object) { try { _channel.DoBind(localAddress); } catch (Exception ex) { Util.SafeSetFailure(promise, ex, Logger); _ = _channel.CloseAsync(); return(promise.Task); } } IChannel boundChannel = LocalChannelRegistry.Get(remoteAddress); if (!(boundChannel is LocalServerChannel serverChannel)) { Exception cause = new ConnectException($"connection refused: {remoteAddress}", null); Util.SafeSetFailure(promise, cause, Logger); _ = _channel.CloseAsync(); return(promise.Task); } _ = Interlocked.Exchange(ref _channel.v_peer, serverChannel.Serve(_channel)); return(promise.Task); }
public override Task ConnectAsync(EndPoint remoteAddress, EndPoint localAddress) { var promise = new TaskCompletionSource(); if (this.localChannel.state == State.Connected) { var cause = new AlreadyConnectedException(); Util.SafeSetFailure(promise, cause, Logger); this.localChannel.Pipeline.FireExceptionCaught(cause); return(promise.Task); } if (this.localChannel.connectPromise != null) { throw new ConnectionPendingException(); } this.localChannel.connectPromise = promise; if (this.localChannel.state != State.Bound) { // Not bound yet and no localAddress specified - get one. if (localAddress == null) { localAddress = new LocalAddress(this.localChannel); } } if (localAddress != null) { try { this.localChannel.DoBind(localAddress); } catch (Exception ex) { Util.SafeSetFailure(promise, ex, Logger); this.channel.CloseAsync(); return(promise.Task); } } IChannel boundChannel = LocalChannelRegistry.Get(remoteAddress); if (!(boundChannel is LocalServerChannel)) { Exception cause = new ConnectException($"connection refused: {remoteAddress}", null); Util.SafeSetFailure(promise, cause, Logger); this.localChannel.CloseAsync(); return(promise.Task); } this.localChannel.peer = ((LocalServerChannel)boundChannel).Serve(this.localChannel); return(promise.Task); }
protected override void DoClose() { if (this.state <= 1) { // Update all internal state before the closeFuture is notified. if (this.localAddress != null) { LocalChannelRegistry.Unregister(this.localAddress); this.localAddress = null; } this.state = 2; } }
protected override void DoClose() { if (Volatile.Read(ref v_state) <= 1) { // Update all internal state before the closeFuture is notified. var thisLocalAddr = Volatile.Read(ref v_localAddress); if (thisLocalAddr is object) { LocalChannelRegistry.Unregister(thisLocalAddr); _ = Interlocked.Exchange(ref v_localAddress, null); } _ = Interlocked.Exchange(ref v_state, 2); } }
protected override void DoClose() { var peer = Volatile.Read(ref v_peer); var oldState = Volatile.Read(ref v_state); try { if (oldState != State.Closed) { // Update all internal state before the closeFuture is notified. var thisLocalAddr = Volatile.Read(ref v_localAddress); if (thisLocalAddr is object) { if (Parent is null) { LocalChannelRegistry.Unregister(thisLocalAddr); } _ = Interlocked.Exchange(ref v_localAddress, null); } // State change must happen before finishPeerRead to ensure writes are released either in doWrite or // channelRead. _ = Interlocked.Exchange(ref v_state, State.Closed); // Preserve order of event and force a read operation now before the close operation is processed. if (SharedConstants.False < (uint)Volatile.Read(ref v_writeInProgress) && peer is object) { FinishPeerRead(peer); } TaskCompletionSource promise = Volatile.Read(ref v_connectPromise); if (promise is object) { // Use tryFailure() instead of setFailure() to avoid the race against cancel(). _ = promise.TrySetException(DoCloseClosedChannelException); _ = Interlocked.Exchange(ref v_connectPromise, null); } } if (peer is object) { _ = Interlocked.Exchange(ref v_peer, null); // Always call peer.eventLoop().execute() even if peer.eventLoop().inEventLoop() is true. // This ensures that if both channels are on the same event loop, the peer's channelInActive // event is triggered *after* this peer's channelInActive event IEventLoop peerEventLoop = peer.EventLoop; bool peerIsActive = peer.IsActive; 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. ReleaseInboundBuffers(); } } }
protected override void DoBind(EndPoint localAddress) { _ = Interlocked.Exchange(ref v_localAddress, LocalChannelRegistry.Register(this, Volatile.Read(ref v_localAddress), localAddress)); _ = Interlocked.Exchange(ref v_state, State.Bound); }
protected override void DoBind(EndPoint localAddress) { this.localAddress = LocalChannelRegistry.Register(this, this.localAddress, localAddress); this.state = 1; }
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(); } } }