1 /*
2  * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /* @test
25  * @bug 4607272
26  * @summary Unit test for AsynchronousChannelGroup
27  * @key randomness
28  */
29 
30 import java.nio.ByteBuffer;
31 import java.nio.channels.*;
32 import java.net.*;
33 import java.util.*;
34 import java.util.concurrent.*;
35 import java.io.IOException;
36 
37 public class Basic {
38     static final Random rand = new Random();
39     static final ThreadFactory threadFactory = (Runnable r) -> {
40         return new Thread(r);
41     };
42 
main(String[] args)43     public static void main(String[] args) throws Exception {
44         shutdownTests();
45         shutdownNowTests();
46         afterShutdownTests();
47         miscTests();
48     }
49 
awaitTermination(AsynchronousChannelGroup group)50     static void awaitTermination(AsynchronousChannelGroup group) throws InterruptedException {
51         boolean terminated = group.awaitTermination(20, TimeUnit.SECONDS);
52         if (!terminated)
53             throw new RuntimeException("Group should have terminated");
54     }
55 
testShutdownWithNoChannels(ExecutorService pool, AsynchronousChannelGroup group)56     static void testShutdownWithNoChannels(ExecutorService pool,
57                                            AsynchronousChannelGroup group)
58         throws Exception
59     {
60         group.shutdown();
61         if (!group.isShutdown())
62             throw new RuntimeException("Group should be shutdown");
63         // group should terminate quickly
64         awaitTermination(group);
65         if (pool != null && !pool.isTerminated())
66             throw new RuntimeException("Executor should have terminated");
67     }
68 
testShutdownWithChannels(ExecutorService pool, AsynchronousChannelGroup group)69     static void testShutdownWithChannels(ExecutorService pool,
70                                          AsynchronousChannelGroup group)
71         throws Exception
72     {
73 
74         // create channel that is bound to group
75         AsynchronousChannel ch;
76         switch (rand.nextInt(2)) {
77             case 0 : ch = AsynchronousSocketChannel.open(group); break;
78             case 1 : ch = AsynchronousServerSocketChannel.open(group); break;
79             default : throw new AssertionError();
80         }
81         group.shutdown();
82         if (!group.isShutdown())
83             throw new RuntimeException("Group should be shutdown");
84 
85         // last channel so should terminate after this channel is closed
86         ch.close();
87 
88         // group should terminate quickly
89         awaitTermination(group);
90         if (pool != null && !pool.isTerminated())
91             throw new RuntimeException("Executor should have terminated");
92     }
93 
shutdownTests()94     static void shutdownTests() throws Exception {
95         System.out.println("-- test shutdown --");
96 
97         // test shutdown with no channels in groups
98         for (int i = 0; i < 100; i++) {
99             ExecutorService pool = Executors.newCachedThreadPool();
100             AsynchronousChannelGroup group = AsynchronousChannelGroup
101                     .withCachedThreadPool(pool, rand.nextInt(5));
102             testShutdownWithNoChannels(pool, group);
103         }
104         for (int i = 0; i < 100; i++) {
105             int nThreads = 1 + rand.nextInt(8);
106             AsynchronousChannelGroup group = AsynchronousChannelGroup
107                     .withFixedThreadPool(nThreads, threadFactory);
108             testShutdownWithNoChannels(null, group);
109         }
110         for (int i = 0; i < 100; i++) {
111             ExecutorService pool = Executors.newCachedThreadPool();
112             AsynchronousChannelGroup group = AsynchronousChannelGroup
113                     .withThreadPool(pool);
114             testShutdownWithNoChannels(pool, group);
115         }
116 
117         // test shutdown with channel in group
118         for (int i = 0; i < 100; i++) {
119             ExecutorService pool = Executors.newCachedThreadPool();
120             AsynchronousChannelGroup group = AsynchronousChannelGroup
121                     .withCachedThreadPool(pool, rand.nextInt(10));
122             try {
123                 testShutdownWithChannels(pool, group);
124             } finally {
125                 group.shutdown();
126             }
127         }
128         for (int i = 0; i < 100; i++) {
129             int nThreads = 1 + rand.nextInt(8);
130             AsynchronousChannelGroup group = AsynchronousChannelGroup
131                     .withFixedThreadPool(nThreads, threadFactory);
132             try {
133                 testShutdownWithChannels(null, group);
134             } finally {
135                 group.shutdown();
136             }
137         }
138         for (int i = 0; i < 100; i++) {
139             ExecutorService pool = Executors.newCachedThreadPool();
140             AsynchronousChannelGroup group = AsynchronousChannelGroup
141                     .withThreadPool(pool);
142             try {
143                 testShutdownWithChannels(pool, group);
144             } finally {
145                 group.shutdown();
146             }
147         }
148     }
149 
testShutdownNow(ExecutorService pool, AsynchronousChannelGroup group)150     static void testShutdownNow(ExecutorService pool,
151                                 AsynchronousChannelGroup group)
152         throws Exception
153     {
154         // I/O in progress
155         AsynchronousServerSocketChannel ch = AsynchronousServerSocketChannel
156                 .open(group).bind(new InetSocketAddress(0));
157         ch.accept();
158 
159         // forceful shutdown
160         group.shutdownNow();
161 
162         // shutdownNow is required to close all channels
163         if (ch.isOpen())
164             throw new RuntimeException("Channel should be closed");
165 
166         awaitTermination(group);
167 
168         if (pool != null && !pool.isTerminated())
169             throw new RuntimeException("Executor should have terminated");
170     }
171 
shutdownNowTests()172     static void shutdownNowTests() throws Exception {
173         System.out.println("-- test shutdownNow --");
174 
175         for (int i = 0; i < 10; i++) {
176             ExecutorService pool = pool = Executors.newCachedThreadPool();
177             AsynchronousChannelGroup group = AsynchronousChannelGroup
178                     .withCachedThreadPool(pool, rand.nextInt(5));
179             try {
180                 testShutdownNow(pool, group);
181             } finally {
182                 group.shutdown();
183             }
184         }
185         for (int i = 0; i < 10; i++) {
186             int nThreads = 1 + rand.nextInt(8);
187             AsynchronousChannelGroup group = AsynchronousChannelGroup
188                     .withFixedThreadPool(nThreads, threadFactory);
189             try {
190                 testShutdownNow(null, group);
191             } finally {
192                 group.shutdown();
193             }
194         }
195         for (int i = 0; i < 10; i++) {
196             ExecutorService pool = Executors.newCachedThreadPool();
197             AsynchronousChannelGroup group = AsynchronousChannelGroup
198                     .withThreadPool(pool);
199             try {
200                 testShutdownNow(pool, group);
201             } finally {
202                 group.shutdown();
203             }
204         }
205     }
206 
207     // test creating channels in group after group is shutdown
afterShutdownTests()208     static void afterShutdownTests() throws Exception {
209         System.out.println("-- test operations after group is shutdown  --");
210         AsynchronousChannelGroup group =
211             AsynchronousChannelGroup.withFixedThreadPool(1, threadFactory);
212 
213         try (AsynchronousSocketChannel ch = AsynchronousSocketChannel.open(group);
214                 AsynchronousServerSocketChannel listener =
215                     AsynchronousServerSocketChannel.open(group)) {
216 
217             // initiate accept
218             listener.bind(new InetSocketAddress(0));
219             Future<AsynchronousSocketChannel> result = listener.accept();
220 
221             // shutdown group
222             group.shutdown();
223             if (!group.isShutdown())
224                 throw new RuntimeException("Group should be shutdown");
225 
226             // attempt to create another channel
227             try {
228                 AsynchronousSocketChannel.open(group);
229                 throw new RuntimeException("ShutdownChannelGroupException expected");
230             } catch (ShutdownChannelGroupException x) {
231             }
232             try {
233                 AsynchronousServerSocketChannel.open(group);
234                 throw new RuntimeException("ShutdownChannelGroupException expected");
235             } catch (ShutdownChannelGroupException x) {
236             }
237 
238             // attempt to create another channel by connecting. This should cause
239             // the accept operation to fail.
240             InetAddress lh = InetAddress.getLocalHost();
241             int port = ((InetSocketAddress)listener.getLocalAddress()).getPort();
242             InetSocketAddress isa = new InetSocketAddress(lh, port);
243             ch.connect(isa).get();
244             try {
245                 result.get();
246                 throw new RuntimeException("Connection was accepted");
247             } catch (ExecutionException x) {
248                 Throwable cause = x.getCause();
249                 if (!(cause instanceof IOException))
250                     throw new RuntimeException("Cause should be IOException");
251                 cause = cause.getCause();
252                 if (!(cause instanceof ShutdownChannelGroupException))
253                     throw new RuntimeException("IOException cause should be ShutdownChannelGroupException");
254             }
255 
256             // initiate another accept even though channel group is shutdown.
257             Future<AsynchronousSocketChannel> res = listener.accept();
258             try {
259                 res.get(3, TimeUnit.SECONDS);
260                 throw new RuntimeException("TimeoutException expected");
261             } catch (TimeoutException x) {
262             }
263             // connect to the listener which should cause the accept to complete
264             AsynchronousSocketChannel.open().connect(isa);
265             try {
266                 res.get();
267                 throw new RuntimeException("Connection was accepted");
268             } catch (ExecutionException x) {
269                 Throwable cause = x.getCause();
270                 if (!(cause instanceof IOException))
271                     throw new RuntimeException("Cause should be IOException");
272                 cause = cause.getCause();
273                 if (!(cause instanceof ShutdownChannelGroupException))
274                     throw new RuntimeException("IOException cause should be ShutdownChannelGroupException");
275             }
276 
277             // group should *not* terminate as channels are open
278             boolean terminated = group.awaitTermination(3, TimeUnit.SECONDS);
279             if (terminated) {
280                 throw new RuntimeException("Group should not have terminated");
281             }
282         } finally {
283             group.shutdown();
284         }
285     }
286 
miscTests()287     static void miscTests() throws Exception {
288         System.out.println("-- miscellenous tests --");
289         try {
290             AsynchronousChannelGroup.withFixedThreadPool(1, null);
291             throw new RuntimeException("NPE expected");
292         } catch (NullPointerException x) {
293         }
294         try {
295             AsynchronousChannelGroup.withFixedThreadPool(0, threadFactory);
296             throw new RuntimeException("IAE expected");
297         } catch (IllegalArgumentException e) {
298         }
299         try {
300             AsynchronousChannelGroup.withCachedThreadPool(null, 0);
301             throw new RuntimeException("NPE expected");
302         } catch (NullPointerException x) {
303         }
304         try {
305             AsynchronousChannelGroup.withThreadPool(null);
306             throw new RuntimeException("NPE expected");
307         } catch (NullPointerException e) {
308         }
309     }
310 }
311