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