View Javadoc
1   /*
2    * Copyright 2019 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.testsuite.transport;
17  
18  import io.netty.bootstrap.ServerBootstrap;
19  import io.netty.channel.Channel;
20  import io.netty.channel.ChannelFuture;
21  import io.netty.channel.ChannelInboundHandlerAdapter;
22  import io.netty.channel.EventLoop;
23  import io.netty.channel.EventLoopGroup;
24  import io.netty.channel.ServerChannel;
25  import io.netty.channel.SingleThreadEventLoop;
26  import io.netty.channel.local.LocalAddress;
27  import io.netty.channel.local.LocalServerChannel;
28  import io.netty.util.concurrent.EventExecutor;
29  import io.netty.util.concurrent.Future;
30  import org.junit.jupiter.api.Test;
31  import org.junit.jupiter.api.Timeout;
32  
33  import java.util.concurrent.Callable;
34  import java.util.concurrent.CountDownLatch;
35  import java.util.concurrent.RejectedExecutionException;
36  import java.util.concurrent.TimeUnit;
37  
38  import static org.junit.jupiter.api.Assertions.assertEquals;
39  import static org.junit.jupiter.api.Assertions.assertFalse;
40  import static org.junit.jupiter.api.Assertions.assertTrue;
41  import static org.junit.jupiter.api.Assertions.fail;
42  
43  public abstract class AbstractSingleThreadEventLoopTest {
44  
45      @Timeout(value = 5000, unit = TimeUnit.MILLISECONDS)
46      public void testChannelsRegistered() throws Exception {
47          EventLoopGroup group = newEventLoopGroup();
48          final SingleThreadEventLoop loop = (SingleThreadEventLoop) group.next();
49  
50          try {
51              final Channel ch1 = newChannel();
52              final Channel ch2 = newChannel();
53  
54              int rc = registeredChannels(loop);
55              boolean channelCountSupported = rc != -1;
56  
57              if (channelCountSupported) {
58                  assertEquals(0, registeredChannels(loop));
59              }
60  
61              assertTrue(loop.register(ch1).syncUninterruptibly().isSuccess());
62              assertTrue(loop.register(ch2).syncUninterruptibly().isSuccess());
63              if (channelCountSupported) {
64                  checkNumRegisteredChannels(loop, 2);
65              }
66  
67              assertTrue(ch1.deregister().syncUninterruptibly().isSuccess());
68              if (channelCountSupported) {
69                  checkNumRegisteredChannels(loop, 1);
70              }
71          } finally {
72              group.shutdownGracefully();
73          }
74      }
75  
76      private static void checkNumRegisteredChannels(SingleThreadEventLoop loop, int numChannels) throws Exception {
77          // We need to loop as some EventLoop implementations may need some time to update the counter correctly.
78          while (registeredChannels(loop) != numChannels) {
79              Thread.sleep(50);
80          }
81      }
82  
83      // Only reliable if run from event loop
84      private static int registeredChannels(final SingleThreadEventLoop loop) throws Exception {
85          return loop.submit(new Callable<Integer>() {
86              @Override
87              public Integer call() {
88                  return loop.registeredChannels();
89              }
90          }).get(1, TimeUnit.SECONDS);
91      }
92  
93      @Test
94      @SuppressWarnings("deprecation")
95      public void shutdownBeforeStart() throws Exception {
96          EventLoopGroup group = newEventLoopGroup();
97          assertFalse(group.awaitTermination(2, TimeUnit.MILLISECONDS));
98          group.shutdown();
99          assertTrue(group.awaitTermination(200, TimeUnit.MILLISECONDS));
100     }
101 
102     @Test
103     public void shutdownGracefullyZeroQuietBeforeStart() throws Exception {
104         EventLoopGroup group = newEventLoopGroup();
105         assertTrue(group.shutdownGracefully(0L, 2L, TimeUnit.SECONDS).await(200L));
106     }
107 
108     // Copied from AbstractEventLoopTest
109     @Test
110     @Timeout(value = 5000, unit = TimeUnit.MILLISECONDS)
111     public void testShutdownGracefullyNoQuietPeriod() throws Exception {
112         EventLoopGroup loop = newEventLoopGroup();
113         ServerBootstrap b = new ServerBootstrap();
114         b.group(loop)
115         .channel(serverChannelClass())
116         .childHandler(new ChannelInboundHandlerAdapter());
117 
118         // Not close the Channel to ensure the EventLoop is still shutdown in time.
119         ChannelFuture cf = serverChannelClass() == LocalServerChannel.class
120                 ? b.bind(new LocalAddress("local")) : b.bind(0);
121         cf.sync().channel();
122 
123         Future<?> f = loop.shutdownGracefully(0, 1, TimeUnit.MINUTES);
124         assertTrue(loop.awaitTermination(600, TimeUnit.MILLISECONDS));
125         assertTrue(f.syncUninterruptibly().isSuccess());
126         assertTrue(loop.isShutdown());
127         assertTrue(loop.isTerminated());
128     }
129 
130     @Test
131     public void shutdownGracefullyBeforeStart() throws Exception {
132         EventLoopGroup group = newEventLoopGroup();
133         assertTrue(group.shutdownGracefully(200L, 1000L, TimeUnit.MILLISECONDS).await(500L));
134     }
135 
136     @Test
137     public void gracefulShutdownAfterStart() throws Exception {
138         EventLoop loop = newEventLoopGroup().next();
139         final CountDownLatch latch = new CountDownLatch(1);
140         loop.execute(new Runnable() {
141             @Override
142             public void run() {
143                 latch.countDown();
144             }
145         });
146 
147         // Wait for the event loop thread to start.
148         latch.await();
149 
150         // Request the event loop thread to stop.
151         loop.shutdownGracefully(200L, 3000L, TimeUnit.MILLISECONDS);
152 
153         // Wait until the event loop is terminated.
154         assertTrue(loop.awaitTermination(500L, TimeUnit.MILLISECONDS));
155 
156         assertRejection(loop);
157     }
158 
159     private static final Runnable NOOP = new Runnable() {
160         @Override
161         public void run() { }
162     };
163 
164     private static void assertRejection(EventExecutor loop) {
165         try {
166             loop.execute(NOOP);
167             fail("A task must be rejected after shutdown() is called.");
168         } catch (RejectedExecutionException e) {
169             // Expected
170         }
171     }
172 
173     protected abstract EventLoopGroup newEventLoopGroup();
174     protected abstract Channel newChannel();
175     protected abstract Class<? extends ServerChannel> serverChannelClass();
176 }