1 /*
   2  * Copyright (c) 2010, 2011, 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 6979009
  26  * @summary Ensure ClosedByInterruptException is thrown when I/O operation
  27  *     interrupted by Thread.interrupt
  28  * @key randomness
  29  */
  30 
  31 import java.io.*;
  32 import java.util.Random;
  33 import java.nio.ByteBuffer;
  34 import java.nio.channels.*;
  35 
  36 public class ClosedByInterrupt {
  37 
  38     static final int K = 1024;
  39     static final Random rand = new Random();
  40 
  41     static volatile boolean failed;
  42 
  43     public static void main(String[] args) throws Exception {
  44         File f = File.createTempFile("blah", null);
  45         f.deleteOnExit();
  46 
  47         // create 1MB file.
  48         byte[] b = new byte[K*K];
  49         rand.nextBytes(b);
  50         ByteBuffer bb = ByteBuffer.wrap(b);
  51         try (FileChannel fc = new FileOutputStream(f).getChannel()) {
  52             while (bb.hasRemaining())
  53                 fc.write(bb);
  54         }
  55 
  56         // test with 1-16 concurrent threads
  57         for (int i=1; i<=16; i++) {
  58             System.out.format("%d thread(s)%n", i);
  59             test(f, i);
  60             if (failed)
  61                 break;
  62         }
  63 
  64         if (failed)
  65             throw new RuntimeException("Test failed");
  66     }
  67 
  68     /**
  69      * Starts "nThreads" that do I/O on the given file concurrently. Continuously
  70      * interrupts one of the threads to cause the file to be closed and
  71      * ClosedByInterruptException to be thrown. The other threads should "fail" with
  72      * ClosedChannelException (or the more specific AsynchronousCloseException).
  73      */
  74     static void test(File f, int nThreads) throws Exception {
  75         try (FileChannel fc = new RandomAccessFile(f, "rwd").getChannel()) {
  76             Thread[] threads = new Thread[nThreads];
  77 
  78             // start threads
  79             for (int i=0; i<nThreads; i++) {
  80                 boolean interruptible = (i==0);
  81                 ReaderWriter task = new ReaderWriter(fc, interruptible);
  82                 Thread t = new Thread(task);
  83                 t.start();
  84                 threads[i] = t;
  85             }
  86 
  87             // give time for threads to start
  88             Thread.sleep(500 + rand.nextInt(1000));
  89 
  90             // interrupt thread until channel is closed
  91             while (fc.isOpen()) {
  92                 threads[0].interrupt();
  93                 Thread.sleep(rand.nextInt(50));
  94             }
  95 
  96             // wait for test to finish
  97             for (int i=0; i<nThreads; i++) {
  98                 threads[i].join();
  99             }
 100         }
 101     }
 102 
 103     /**
 104      * A task that continuously reads or writes to random areas of a file
 105      * until the channel is closed. An "interruptible" task expects the
 106      * channel to be closed by an interupt, a "non-interruptible" thread
 107      * does not.
 108      */
 109     static class ReaderWriter implements Runnable {
 110         final FileChannel fc;
 111         final boolean interruptible;
 112         final boolean writer;
 113 
 114         ReaderWriter(FileChannel fc, boolean interruptible) {
 115             this.fc = fc;
 116             this.interruptible = interruptible;
 117             this.writer = rand.nextBoolean();
 118         }
 119 
 120         public void run() {
 121             ByteBuffer bb = ByteBuffer.allocate(K);
 122             if (writer)
 123                 rand.nextBytes(bb.array());
 124 
 125             try {
 126                 for (;;) {
 127                     long position = rand.nextInt(K*K - bb.capacity());
 128                     if (writer) {
 129                         bb.position(0).limit(bb.capacity());
 130                         fc.write(bb, position);
 131                     } else {
 132                         bb.clear();
 133                         fc.read(bb, position);
 134                     }
 135                     if (!interruptible) {
 136                         // give the interruptible thread a chance
 137                         try {
 138                             Thread.sleep(rand.nextInt(50));
 139                         } catch (InterruptedException e) {
 140                             unexpected(e);
 141                         }
 142                     }
 143                 }
 144             } catch (ClosedByInterruptException e) {
 145                 if (interruptible) {
 146                     if (Thread.interrupted()) {
 147                         expected(e + " thrown and interrupt status set");
 148                     } else {
 149                         unexpected(e + " thrown but interrupt status not set");
 150                     }
 151                 } else {
 152                     unexpected(e);
 153                 }
 154             } catch (ClosedChannelException e) {
 155                 if (interruptible) {
 156                     unexpected(e);
 157                 } else {
 158                     expected(e);
 159                 }
 160             } catch (Exception e) {
 161                 unexpected(e);
 162             }
 163         }
 164     }
 165 
 166     static void expected(Exception e) {
 167         System.out.format("%s (expected)%n", e);
 168     }
 169 
 170     static void expected(String msg) {
 171         System.out.format("%s (expected)%n", msg);
 172     }
 173 
 174     static void unexpected(Exception e) {
 175         System.err.format("%s (not expected)%n", e);
 176         failed = true;
 177     }
 178 
 179     static void unexpected(String msg) {
 180         System.err.println(msg);
 181         failed = true;
 182     }
 183 }