1 /*
   2  * Copyright 2009 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  20  * CA 95054 USA or visit www.sun.com if you need additional information or
  21  * have any questions.
  22  */
  23 
  24 /* @test
  25  * @bug 6693490
  26  * @summary Pre-close file descriptor may inadvertently get registered with
  27  *     epoll during close
  28  */
  29 
  30 import java.net.*;
  31 import java.nio.channels.*;
  32 import java.util.concurrent.*;
  33 import java.util.*;
  34 import java.io.IOException;
  35 
  36 public class RegAfterPreClose {
  37 
  38     static volatile boolean done;
  39 
  40     /**
  41      * A task that continuously connects to a given address and immediately
  42      * closes the connection.
  43      */
  44     static class Connector implements Runnable {
  45         private final SocketAddress sa;
  46         Connector(int port) throws IOException {
  47             InetAddress lh = InetAddress.getLocalHost();
  48             this.sa = new InetSocketAddress(lh, port);
  49         }
  50         public void run() {
  51             while (!done) {
  52                 try {
  53                     SocketChannel.open(sa).close();
  54                 } catch (IOException x) {
  55                     // back-off as probably resource related
  56                     try {
  57                         Thread.sleep(10);
  58                     } catch (InterruptedException  ignore) { }
  59                 }
  60             }
  61         }
  62     }
  63 
  64     /**
  65      * A task that closes a channel.
  66      */
  67     static class Closer implements Runnable {
  68         private final Channel channel;
  69         Closer(Channel sc) {
  70             this.channel = sc;
  71         }
  72         public void run() {
  73             try {
  74                 channel.close();
  75             } catch (IOException ignore) { }
  76         }
  77     }
  78 
  79     public static void main(String[] args) throws Exception {
  80         // create listener
  81         InetSocketAddress isa = new InetSocketAddress(0);
  82         ServerSocketChannel ssc = ServerSocketChannel.open();
  83         ssc.socket().bind(isa);
  84 
  85         // register with Selector
  86         final Selector sel = Selector.open();
  87         ssc.configureBlocking(false);
  88         SelectionKey key = ssc.register(sel, SelectionKey.OP_ACCEPT);
  89 
  90         ThreadFactory factory = new ThreadFactory() {
  91             @Override
  92             public Thread newThread(Runnable r) {
  93                 Thread t = new Thread(r);
  94                 t.setDaemon(true);
  95                 return t;
  96             }
  97         };
  98 
  99         // schedule test to run for 1 minute
 100         Executors.newScheduledThreadPool(1, factory).schedule(new Runnable() {
 101             public void run() {
 102                 done = true;
 103                 sel.wakeup();
 104             }}, 1, TimeUnit.MINUTES);
 105 
 106         // create Executor that handles tasks that closes channels
 107         // "asynchronously" - this creates the conditions to provoke the bug.
 108         Executor executor = Executors.newFixedThreadPool(2, factory);
 109 
 110         // submit task that connects to listener
 111         executor.execute(new Connector(ssc.socket().getLocalPort()));
 112 
 113         // loop accepting connections until done (or an IOException is thrown)
 114         while (!done) {
 115             sel.select();
 116             if (key.isAcceptable()) {
 117                 SocketChannel sc = ssc.accept();
 118                 if (sc != null) {
 119                     sc.configureBlocking(false);
 120                     sc.register(sel, SelectionKey.OP_READ);
 121                     executor.execute(new Closer(sc));
 122                 }
 123             }
 124             sel.selectedKeys().clear();
 125         }
 126     }
 127 }