1 /*
   2  * Copyright (c) 2008, 2012, 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 
  43     public static void main(String[] args) throws Exception {
  44         shutdownTests();
  45         shutdownNowTests();
  46         afterShutdownTests();
  47         miscTests();
  48     }
  49 
  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 
  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 
  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 
  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             testShutdownWithChannels(pool, group);
 123         }
 124         for (int i = 0; i < 100; i++) {
 125             int nThreads = 1 + rand.nextInt(8);
 126             AsynchronousChannelGroup group = AsynchronousChannelGroup
 127                     .withFixedThreadPool(nThreads, threadFactory);
 128             testShutdownWithChannels(null, group);
 129         }
 130         for (int i = 0; i < 100; i++) {
 131             ExecutorService pool = Executors.newCachedThreadPool();
 132             AsynchronousChannelGroup group = AsynchronousChannelGroup
 133                     .withThreadPool(pool);
 134             testShutdownWithChannels(pool, group);
 135         }
 136     }
 137 
 138     static void testShutdownNow(ExecutorService pool,
 139                                 AsynchronousChannelGroup group)
 140         throws Exception
 141     {
 142         // I/O in progress
 143         AsynchronousServerSocketChannel ch = AsynchronousServerSocketChannel
 144                 .open(group).bind(new InetSocketAddress(0));
 145         ch.accept();
 146 
 147         // forceful shutdown
 148         group.shutdownNow();
 149 
 150         // shutdownNow is required to close all channels
 151         if (ch.isOpen())
 152             throw new RuntimeException("Channel should be closed");
 153 
 154         awaitTermination(group);
 155 
 156         if (pool != null && !pool.isTerminated())
 157             throw new RuntimeException("Executor should have terminated");
 158     }
 159 
 160     static void shutdownNowTests() throws Exception {
 161         System.out.println("-- test shutdownNow --");
 162 
 163         for (int i = 0; i < 10; i++) {
 164             ExecutorService pool = pool = Executors.newCachedThreadPool();
 165             AsynchronousChannelGroup group = AsynchronousChannelGroup
 166                     .withCachedThreadPool(pool, rand.nextInt(5));
 167             testShutdownNow(pool, group);
 168         }
 169         for (int i = 0; i < 10; i++) {
 170             int nThreads = 1 + rand.nextInt(8);
 171             AsynchronousChannelGroup group = AsynchronousChannelGroup
 172                     .withFixedThreadPool(nThreads, threadFactory);
 173             testShutdownNow(null, group);
 174         }
 175         for (int i = 0; i < 10; i++) {
 176             ExecutorService pool = Executors.newCachedThreadPool();
 177             AsynchronousChannelGroup group = AsynchronousChannelGroup
 178                     .withThreadPool(pool);
 179             testShutdownNow(pool, group);
 180         }
 181     }
 182 
 183     // test creating channels in group after group is shutdown
 184     static void afterShutdownTests() throws Exception {
 185         System.out.println("-- test operations after group is shutdown  --");
 186         AsynchronousChannelGroup group =
 187             AsynchronousChannelGroup.withFixedThreadPool(1, threadFactory);
 188 
 189         AsynchronousSocketChannel ch = AsynchronousSocketChannel.open(group);
 190         AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open(group);
 191 
 192         // initiate accept
 193         listener.bind(new InetSocketAddress(0));
 194         Future<AsynchronousSocketChannel> result = listener.accept();
 195 
 196         // shutdown group
 197         group.shutdown();
 198         if (!group.isShutdown())
 199             throw new RuntimeException("Group should be shutdown");
 200 
 201         // attempt to create another channel
 202         try {
 203             AsynchronousSocketChannel.open(group);
 204             throw new RuntimeException("ShutdownChannelGroupException expected");
 205         } catch (ShutdownChannelGroupException x) {
 206         }
 207         try {
 208             AsynchronousServerSocketChannel.open(group);
 209             throw new RuntimeException("ShutdownChannelGroupException expected");
 210         } catch (ShutdownChannelGroupException x) {
 211         }
 212 
 213         // attempt to create another channel by connecting. This should cause
 214         // the accept operation to fail.
 215         InetAddress lh = InetAddress.getLocalHost();
 216         int port = ((InetSocketAddress)listener.getLocalAddress()).getPort();
 217         InetSocketAddress isa = new InetSocketAddress(lh, port);
 218         ch.connect(isa).get();
 219         try {
 220             result.get();
 221             throw new RuntimeException("Connection was accepted");
 222         } catch (ExecutionException x) {
 223             Throwable cause = x.getCause();
 224             if (!(cause instanceof IOException))
 225                 throw new RuntimeException("Cause should be IOException");
 226             cause = cause.getCause();
 227             if (!(cause instanceof ShutdownChannelGroupException))
 228                 throw new RuntimeException("IOException cause should be ShutdownChannelGroupException");
 229         }
 230 
 231         // initiate another accept even though channel group is shutdown.
 232         Future<AsynchronousSocketChannel> res = listener.accept();
 233         try {
 234             res.get(3, TimeUnit.SECONDS);
 235             throw new RuntimeException("TimeoutException expected");
 236         } catch (TimeoutException x) {
 237         }
 238         // connect to the listener which should cause the accept to complete
 239         AsynchronousSocketChannel.open().connect(isa);
 240         try {
 241             res.get();
 242             throw new RuntimeException("Connection was accepted");
 243         } catch (ExecutionException x) {
 244             Throwable cause = x.getCause();
 245             if (!(cause instanceof IOException))
 246                 throw new RuntimeException("Cause should be IOException");
 247             cause = cause.getCause();
 248             if (!(cause instanceof ShutdownChannelGroupException))
 249                 throw new RuntimeException("IOException cause should be ShutdownChannelGroupException");
 250         }
 251 
 252         // group should *not* terminate as channels are open
 253         boolean terminated = group.awaitTermination(3, TimeUnit.SECONDS);
 254         if (terminated)
 255             throw new RuntimeException("Group should not have terminated");
 256 
 257         // close channel; group should terminate quickly
 258         ch.close();
 259         listener.close();
 260         awaitTermination(group);
 261     }
 262 
 263     static void miscTests() throws Exception {
 264         System.out.println("-- miscellenous tests --");
 265         try {
 266             AsynchronousChannelGroup.withFixedThreadPool(1, null);
 267             throw new RuntimeException("NPE expected");
 268         } catch (NullPointerException x) {
 269         }
 270         try {
 271             AsynchronousChannelGroup.withFixedThreadPool(0, threadFactory);
 272             throw new RuntimeException("IAE expected");
 273         } catch (IllegalArgumentException e) {
 274         }
 275         try {
 276             AsynchronousChannelGroup.withCachedThreadPool(null, 0);
 277             throw new RuntimeException("NPE expected");
 278         } catch (NullPointerException x) {
 279         }
 280         try {
 281             AsynchronousChannelGroup.withThreadPool(null);
 282             throw new RuntimeException("NPE expected");
 283         } catch (NullPointerException e) {
 284         }
 285     }
 286 }