@@ -339,8 +339,8 @@ public void testCloseInWritePromiseCompletePreservesOrder() throws InterruptedEx
339339 @ Override
340340 public void channelRead (ChannelHandlerContext ctx , Object msg ) throws Exception {
341341 if (msg .equals (data )) {
342- messageLatch .countDown ();
343342 ReferenceCountUtil .safeRelease (msg );
343+ messageLatch .countDown ();
344344 } else {
345345 super .channelRead (ctx , msg );
346346 }
@@ -408,8 +408,8 @@ public void testWriteInWritePromiseCompletePreservesOrder() throws InterruptedEx
408408 public void channelRead (ChannelHandlerContext ctx , Object msg ) throws Exception {
409409 final long count = messageLatch .getCount ();
410410 if ((data .equals (msg ) && count == 2 ) || (data2 .equals (msg ) && count == 1 )) {
411- messageLatch .countDown ();
412411 ReferenceCountUtil .safeRelease (msg );
412+ messageLatch .countDown ();
413413 } else {
414414 super .channelRead (ctx , msg );
415415 }
@@ -468,8 +468,8 @@ public void testPeerWriteInWritePromiseCompleteDifferentEventLoopPreservesOrder(
468468 @ Override
469469 public void channelRead (ChannelHandlerContext ctx , Object msg ) throws Exception {
470470 if (data2 .equals (msg )) {
471- messageLatch .countDown ();
472471 ReferenceCountUtil .safeRelease (msg );
472+ messageLatch .countDown ();
473473 } else {
474474 super .channelRead (ctx , msg );
475475 }
@@ -485,8 +485,8 @@ public void initChannel(LocalChannel ch) throws Exception {
485485 @ Override
486486 public void channelRead (ChannelHandlerContext ctx , Object msg ) throws Exception {
487487 if (data .equals (msg )) {
488- messageLatch .countDown ();
489488 ReferenceCountUtil .safeRelease (msg );
489+ messageLatch .countDown ();
490490 } else {
491491 super .channelRead (ctx , msg );
492492 }
@@ -550,8 +550,8 @@ public void testPeerWriteInWritePromiseCompleteSameEventLoopPreservesOrder() thr
550550 @ Override
551551 public void channelRead (ChannelHandlerContext ctx , Object msg ) throws Exception {
552552 if (data2 .equals (msg ) && messageLatch .getCount () == 1 ) {
553- messageLatch .countDown ();
554553 ReferenceCountUtil .safeRelease (msg );
554+ messageLatch .countDown ();
555555 } else {
556556 super .channelRead (ctx , msg );
557557 }
@@ -567,8 +567,8 @@ public void initChannel(LocalChannel ch) throws Exception {
567567 @ Override
568568 public void channelRead (ChannelHandlerContext ctx , Object msg ) throws Exception {
569569 if (data .equals (msg ) && messageLatch .getCount () == 2 ) {
570- messageLatch .countDown ();
571570 ReferenceCountUtil .safeRelease (msg );
571+ messageLatch .countDown ();
572572 } else {
573573 super .channelRead (ctx , msg );
574574 }
@@ -641,8 +641,8 @@ public void initChannel(LocalChannel ch) throws Exception {
641641 @ Override
642642 public void channelRead (ChannelHandlerContext ctx , Object msg ) throws Exception {
643643 if (msg .equals (data )) {
644- messageLatch .countDown ();
645644 ReferenceCountUtil .safeRelease (msg );
645+ messageLatch .countDown ();
646646 } else {
647647 super .channelRead (ctx , msg );
648648 }
@@ -697,6 +697,130 @@ public void operationComplete(ChannelFuture future) throws Exception {
697697 }
698698 }
699699
700+ @ Test
701+ public void testWriteWhilePeerIsClosedReleaseObjectAndFailPromise () throws InterruptedException {
702+ Bootstrap cb = new Bootstrap ();
703+ ServerBootstrap sb = new ServerBootstrap ();
704+ final CountDownLatch serverMessageLatch = new CountDownLatch (1 );
705+ final LatchChannelFutureListener serverChannelCloseLatch = new LatchChannelFutureListener (1 );
706+ final LatchChannelFutureListener clientChannelCloseLatch = new LatchChannelFutureListener (1 );
707+ final CountDownLatch writeFailLatch = new CountDownLatch (1 );
708+ final ByteBuf data = Unpooled .wrappedBuffer (new byte [1024 ]);
709+ final ByteBuf data2 = Unpooled .wrappedBuffer (new byte [512 ]);
710+ final CountDownLatch serverChannelLatch = new CountDownLatch (1 );
711+ final AtomicReference <Channel > serverChannelRef = new AtomicReference <Channel >();
712+
713+ try {
714+ cb .group (group1 )
715+ .channel (LocalChannel .class )
716+ .handler (new TestHandler ());
717+
718+ sb .group (group2 )
719+ .channel (LocalServerChannel .class )
720+ .childHandler (new ChannelInitializer <LocalChannel >() {
721+ @ Override
722+ public void initChannel (LocalChannel ch ) throws Exception {
723+ ch .pipeline ().addLast (new ChannelInboundHandlerAdapter () {
724+ @ Override
725+ public void channelRead (ChannelHandlerContext ctx , Object msg ) throws Exception {
726+ if (data .equals (msg )) {
727+ ReferenceCountUtil .safeRelease (msg );
728+ serverMessageLatch .countDown ();
729+ } else {
730+ super .channelRead (ctx , msg );
731+ }
732+ }
733+ });
734+ serverChannelRef .set (ch );
735+ serverChannelLatch .countDown ();
736+ }
737+ });
738+
739+ Channel sc = null ;
740+ Channel cc = null ;
741+ try {
742+ // Start server
743+ sc = sb .bind (TEST_ADDRESS ).syncUninterruptibly ().channel ();
744+
745+ // Connect to the server
746+ cc = cb .connect (sc .localAddress ()).syncUninterruptibly ().channel ();
747+ assertTrue (serverChannelLatch .await (5 , SECONDS ));
748+
749+ final Channel ccCpy = cc ;
750+ final Channel serverChannelCpy = serverChannelRef .get ();
751+ serverChannelCpy .closeFuture ().addListener (serverChannelCloseLatch );
752+ ccCpy .closeFuture ().addListener (clientChannelCloseLatch );
753+
754+ // Make sure a write operation is executed in the eventloop
755+ cc .pipeline ().lastContext ().executor ().execute (new OneTimeTask () {
756+ @ Override
757+ public void run () {
758+ ccCpy .writeAndFlush (data .duplicate ().retain (), ccCpy .newPromise ())
759+ .addListener (new ChannelFutureListener () {
760+ @ Override
761+ public void operationComplete (ChannelFuture future ) throws Exception {
762+ serverChannelCpy .eventLoop ().execute (new OneTimeTask () {
763+ @ Override
764+ public void run () {
765+ // The point of this test is to write while the peer is closed, so we should
766+ // ensure the peer is actually closed before we write.
767+ int waitCount = 0 ;
768+ while (ccCpy .isOpen ()) {
769+ try {
770+ Thread .sleep (50 );
771+ } catch (InterruptedException ignored ) {
772+ // ignored
773+ }
774+ if (++waitCount > 5 ) {
775+ fail ();
776+ }
777+ }
778+ serverChannelCpy .writeAndFlush (data2 .duplicate ().retain (),
779+ serverChannelCpy .newPromise ())
780+ .addListener (new ChannelFutureListener () {
781+ @ Override
782+ public void operationComplete (ChannelFuture future ) throws Exception {
783+ if (!future .isSuccess () &&
784+ future .cause () instanceof ClosedChannelException ) {
785+ writeFailLatch .countDown ();
786+ }
787+ }
788+ });
789+ }
790+ });
791+ ccCpy .close ();
792+ }
793+ });
794+ }
795+ });
796+
797+ assertTrue (serverMessageLatch .await (5 , SECONDS ));
798+ assertTrue (writeFailLatch .await (5 , SECONDS ));
799+ assertTrue (serverChannelCloseLatch .await (5 , SECONDS ));
800+ assertTrue (clientChannelCloseLatch .await (5 , SECONDS ));
801+ assertFalse (ccCpy .isOpen ());
802+ assertFalse (serverChannelCpy .isOpen ());
803+ } finally {
804+ closeChannel (cc );
805+ closeChannel (sc );
806+ }
807+ } finally {
808+ data .release ();
809+ data2 .release ();
810+ }
811+ }
812+
813+ private static final class LatchChannelFutureListener extends CountDownLatch implements ChannelFutureListener {
814+ public LatchChannelFutureListener (int count ) {
815+ super (count );
816+ }
817+
818+ @ Override
819+ public void operationComplete (ChannelFuture future ) throws Exception {
820+ countDown ();
821+ }
822+ }
823+
700824 private static void closeChannel (Channel cc ) {
701825 if (cc != null ) {
702826 cc .close ().syncUninterruptibly ();
@@ -707,6 +831,7 @@ static class TestHandler extends ChannelInboundHandlerAdapter {
707831 @ Override
708832 public void channelRead (ChannelHandlerContext ctx , Object msg ) throws Exception {
709833 logger .info (String .format ("Received mesage: %s" , msg ));
834+ ReferenceCountUtil .safeRelease (msg );
710835 }
711836 }
712837}
0 commit comments