Home | History | Annotate | Download | only in channels
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package libcore.java.nio.channels;
     17 
     18 import junit.framework.TestCase;
     19 
     20 import android.system.OsConstants;
     21 import java.io.File;
     22 import java.io.FileInputStream;
     23 import java.io.FileOutputStream;
     24 import java.io.InterruptedIOException;
     25 import java.io.IOException;
     26 import java.nio.ByteBuffer;
     27 import java.nio.channels.AsynchronousCloseException;
     28 import java.nio.channels.ClosedByInterruptException;
     29 import java.nio.channels.ClosedChannelException;
     30 import java.nio.channels.FileChannel;
     31 import libcore.io.Libcore;
     32 
     33 import static libcore.io.IoUtils.closeQuietly;
     34 
     35 /**
     36  * A test for file interrupt behavior. Because forcing a real file to block on read or write is
     37  * difficult this test uses Unix FIFO / Named Pipes. FIFOs appear to Java as files but the test
     38  * has more control over the available data. Reader will block until the other end writes, and
     39  * writers can also be made to block.
     40  *
     41  * <p>Using FIFOs has a few drawbacks:
     42  * <ol>
     43  * <li>FIFOs are not supported from Java or the command-line on Android, so this test includes
     44  * native code to create the FIFO.
     45  * <li>FIFOs will not open() until there is both a reader and a writer of the FIFO; each test must
     46  * always attach both ends or experience a blocked test.
     47  * <li>FIFOs are not supported on some file systems. e.g. VFAT, so the test has to be particular
     48  * about the temporary directory it uses to hold the FIFO.
     49  * <li>Writes to FIFOs are buffered by the OS which makes blocking behavior more difficult to
     50  * induce. See {@link ChannelWriter} and {@link StreamWriter}.
     51  * </ol>
     52  */
     53 public class FileIOInterruptTest extends TestCase {
     54 
     55   private static File VOGAR_DEVICE_TEMP_DIR = new File("/data/data/file_io_interrupt_test");
     56 
     57   private File fifoFile;
     58 
     59   @Override
     60   public void setUp() throws Exception {
     61     super.setUp();
     62 
     63     // This test relies on a FIFO file. The file system must support FIFOs, so we check the path.
     64     String tmpDirName = System.getProperty("java.io.tmpdir");
     65     File tmpDir;
     66     if (tmpDirName.startsWith("/sdcard")) {
     67       // Vogar execution on device runs in /sdcard. Unfortunately the file system used does not
     68       // support FIFOs so the test must use one that is more likely to work.
     69       if (!VOGAR_DEVICE_TEMP_DIR.exists()) {
     70         assertTrue(VOGAR_DEVICE_TEMP_DIR.mkdir());
     71       }
     72       VOGAR_DEVICE_TEMP_DIR.deleteOnExit();
     73       tmpDir = VOGAR_DEVICE_TEMP_DIR;
     74     } else {
     75       tmpDir = new File(tmpDirName);
     76     }
     77     fifoFile = new File(tmpDir, "fifo_file.tmp");
     78     if (fifoFile.exists()) {
     79       fifoFile.delete();
     80     }
     81     fifoFile.deleteOnExit();
     82 
     83     // Create the fifo. This will throw an exception if the file system does not support it.
     84     Libcore.os.mkfifo(fifoFile.getAbsolutePath(), OsConstants.S_IRWXU);
     85   }
     86 
     87   @Override
     88   public void tearDown() throws Exception {
     89     super.tearDown();
     90     fifoFile.delete();
     91     VOGAR_DEVICE_TEMP_DIR.delete();
     92 
     93     // Clear the interrupted state, if set.
     94     Thread.interrupted();
     95   }
     96 
     97   public void testStreamRead_exceptionWhenAlreadyClosed() throws Exception {
     98     FifoWriter fifoWriter = new FifoWriter(fifoFile);
     99     fifoWriter.start();
    100 
    101     FileInputStream fis = new FileInputStream(fifoFile);
    102     fis.close();
    103 
    104     byte[] buffer = new byte[10];
    105     try {
    106       fis.read(buffer);
    107       fail();
    108     } catch (IOException expected) {
    109       assertSame(IOException.class, expected.getClass());
    110     }
    111 
    112     fifoWriter.tidyUp();
    113   }
    114 
    115   // This test fails on the RI: close() does not wake up a blocking FileInputStream.read() call.
    116   public void testStreamRead_exceptionOnCloseWhenBlocked() throws Exception {
    117     FifoWriter fifoWriter = new FifoWriter(fifoFile);
    118     fifoWriter.start();
    119 
    120     FileInputStream fis = new FileInputStream(fifoFile);
    121     StreamReader streamReader = new StreamReader(fis);
    122     Thread streamReaderThread = createAndStartThread("StreamReader", streamReader);
    123 
    124     // Delay until we can be fairly sure the reader thread is blocking.
    125     streamReader.waitForThreadToBlock();
    126 
    127     // Now close the OutputStream to see what happens.
    128     fis.close();
    129 
    130     // Test for expected behavior in the reader thread.
    131     waitToDie(streamReaderThread);
    132     assertSame(InterruptedIOException.class, streamReader.ioe.getClass());
    133     assertFalse(streamReader.wasInterrupted);
    134 
    135     // Tidy up the writer thread.
    136     fifoWriter.tidyUp();
    137   }
    138 
    139   public void testStreamWrite_exceptionWhenAlreadyClosed() throws Exception {
    140     FifoReader fifoReader = new FifoReader(fifoFile);
    141     fifoReader.start();
    142 
    143     FileOutputStream fos = new FileOutputStream(fifoFile);
    144     byte[] buffer = new byte[10];
    145     fos.close();
    146 
    147     try {
    148       fos.write(buffer);
    149       fail();
    150     } catch (IOException expected) {
    151       assertSame(IOException.class, expected.getClass());
    152     }
    153 
    154     fifoReader.tidyUp();
    155   }
    156 
    157   // This test fails on the RI: close() does not wake up a blocking FileInputStream.write() call.
    158   public void testStreamWrite_exceptionOnCloseWhenBlocked() throws Exception {
    159     FifoReader fifoReader = new FifoReader(fifoFile);
    160     fifoReader.start();
    161 
    162     FileOutputStream fos = new FileOutputStream(fifoFile);
    163     StreamWriter streamWriter = new StreamWriter(fos);
    164     Thread streamWriterThread = createAndStartThread("StreamWriter", streamWriter);
    165 
    166     // Delay until we can be fairly sure the writer thread is blocking.
    167     streamWriter.waitForThreadToBlock();
    168 
    169     // Now close the OutputStream to see what happens.
    170     fos.close();
    171 
    172     // Test for expected behavior in the writer thread.
    173     waitToDie(streamWriterThread);
    174     assertSame(InterruptedIOException.class, streamWriter.ioe.getClass());
    175     assertFalse(streamWriter.wasInterrupted);
    176 
    177     // Tidy up the reader thread.
    178     fifoReader.tidyUp();
    179   }
    180 
    181   public void testChannelRead_exceptionWhenAlreadyClosed() throws Exception {
    182     testChannelRead_exceptionWhenAlreadyClosed(ChannelReader.Method.READ);
    183   }
    184 
    185   public void testChannelReadV_exceptionWhenAlreadyClosed() throws Exception {
    186     testChannelRead_exceptionWhenAlreadyClosed(ChannelReader.Method.READV);
    187   }
    188 
    189   private void testChannelRead_exceptionWhenAlreadyClosed(ChannelReader.Method method)
    190       throws Exception {
    191     FifoWriter fifoWriter = new FifoWriter(fifoFile);
    192     fifoWriter.start();
    193     FileInputStream fis = new FileInputStream(fifoFile);
    194     FileChannel fileInputChannel = fis.getChannel();
    195     fileInputChannel.close();
    196 
    197     ByteBuffer buffer = ByteBuffer.allocateDirect(10);
    198     try {
    199       if (method == ChannelReader.Method.READ) {
    200         fileInputChannel.read(buffer);
    201       } else {
    202         ByteBuffer buffer2 = ByteBuffer.allocateDirect(10);
    203         fileInputChannel.read(new ByteBuffer[] { buffer, buffer2});
    204       }
    205       fail();
    206     } catch (IOException expected) {
    207       assertSame(ClosedChannelException.class, expected.getClass());
    208     }
    209 
    210     fifoWriter.tidyUp();
    211   }
    212 
    213   public void testChannelRead_exceptionWhenAlreadyInterrupted() throws Exception {
    214     testChannelRead_exceptionWhenAlreadyInterrupted(ChannelReader.Method.READ);
    215   }
    216 
    217   public void testChannelReadV_exceptionWhenAlreadyInterrupted() throws Exception {
    218     testChannelRead_exceptionWhenAlreadyInterrupted(ChannelReader.Method.READV);
    219   }
    220 
    221   private void testChannelRead_exceptionWhenAlreadyInterrupted(ChannelReader.Method method)
    222       throws Exception {
    223     FifoWriter fifoWriter = new FifoWriter(fifoFile);
    224     fifoWriter.start();
    225     FileInputStream fis = new FileInputStream(fifoFile);
    226     FileChannel fileInputChannel = fis.getChannel();
    227 
    228     Thread.currentThread().interrupt();
    229 
    230     ByteBuffer buffer = ByteBuffer.allocateDirect(10);
    231     try {
    232       if (method == ChannelReader.Method.READ) {
    233         fileInputChannel.read(buffer);
    234       } else {
    235         ByteBuffer buffer2 = ByteBuffer.allocateDirect(10);
    236         fileInputChannel.read(new ByteBuffer[] { buffer, buffer2});
    237       }
    238       fail();
    239     } catch (IOException expected) {
    240       assertSame(ClosedByInterruptException.class, expected.getClass());
    241     }
    242 
    243     // Check but also clear the interrupted status, so we can wait for the FifoWriter thread in
    244     // tidyUp().
    245     assertTrue(Thread.interrupted());
    246 
    247     fifoWriter.tidyUp();
    248   }
    249 
    250   public void testChannelRead_exceptionOnCloseWhenBlocked() throws Exception {
    251     testChannelRead_exceptionOnCloseWhenBlocked(ChannelReader.Method.READ);
    252   }
    253 
    254   public void testChannelReadV_exceptionOnCloseWhenBlocked() throws Exception {
    255     testChannelRead_exceptionOnCloseWhenBlocked(ChannelReader.Method.READV);
    256   }
    257 
    258   private void testChannelRead_exceptionOnCloseWhenBlocked(ChannelReader.Method method)
    259       throws Exception {
    260     FifoWriter fifoWriter = new FifoWriter(fifoFile);
    261     fifoWriter.start();
    262     FileInputStream fis = new FileInputStream(fifoFile);
    263     FileChannel fileInputChannel = fis.getChannel();
    264 
    265     ChannelReader channelReader = new ChannelReader(fileInputChannel, method);
    266     Thread channelReaderThread = createAndStartThread("ChannelReader", channelReader);
    267 
    268     // Delay until we can be fairly sure the reader thread is blocking.
    269     channelReader.waitForThreadToBlock();
    270 
    271     // Now close the FileChannel to see what happens.
    272     fileInputChannel.close();
    273 
    274     // Test for expected behavior in the reader thread.
    275     waitToDie(channelReaderThread);
    276     assertSame(AsynchronousCloseException.class, channelReader.ioe.getClass());
    277     assertFalse(channelReader.wasInterrupted);
    278 
    279     // Tidy up the writer thread.
    280     fifoWriter.tidyUp();
    281   }
    282 
    283   public void testChannelRead_exceptionOnInterrupt() throws Exception {
    284     testChannelRead_exceptionOnInterrupt(ChannelReader.Method.READ);
    285   }
    286 
    287   public void testChannelReadV_exceptionOnInterrupt() throws Exception {
    288     testChannelRead_exceptionOnInterrupt(ChannelReader.Method.READV);
    289   }
    290 
    291   private void testChannelRead_exceptionOnInterrupt(ChannelReader.Method method) throws Exception {
    292     FifoWriter fifoWriter = new FifoWriter(fifoFile);
    293     fifoWriter.start();
    294     FileChannel fileChannel = new FileInputStream(fifoFile).getChannel();
    295 
    296     ChannelReader channelReader = new ChannelReader(fileChannel, method);
    297     Thread channelReaderThread = createAndStartThread("ChannelReader", channelReader);
    298 
    299     // Delay until we can be fairly sure the reader thread is blocking.
    300     channelReader.waitForThreadToBlock();
    301 
    302     // Now interrupt the reader thread to see what happens.
    303     channelReaderThread.interrupt();
    304 
    305     // Test for expected behavior in the reader thread.
    306     waitToDie(channelReaderThread);
    307     assertSame(ClosedByInterruptException.class, channelReader.ioe.getClass());
    308     assertTrue(channelReader.wasInterrupted);
    309 
    310     // Tidy up the writer thread.
    311     fifoWriter.tidyUp();
    312   }
    313 
    314   public void testChannelWrite_exceptionWhenAlreadyClosed() throws Exception {
    315     testChannelWrite_exceptionWhenAlreadyClosed(ChannelWriter.Method.WRITE);
    316   }
    317 
    318   public void testChannelWriteV_exceptionWhenAlreadyClosed() throws Exception {
    319     testChannelWrite_exceptionWhenAlreadyClosed(ChannelWriter.Method.WRITEV);
    320   }
    321 
    322   private void testChannelWrite_exceptionWhenAlreadyClosed(ChannelWriter.Method method)
    323       throws Exception {
    324     FifoReader fifoReader = new FifoReader(fifoFile);
    325     fifoReader.start();
    326     FileChannel fileOutputChannel = new FileOutputStream(fifoFile).getChannel();
    327     fileOutputChannel.close();
    328 
    329     ByteBuffer buffer = ByteBuffer.allocateDirect(10);
    330     try {
    331       if (method == ChannelWriter.Method.WRITE) {
    332         fileOutputChannel.write(buffer);
    333       } else {
    334         ByteBuffer buffer2 = ByteBuffer.allocateDirect(10);
    335         fileOutputChannel.write(new ByteBuffer[] { buffer, buffer2 });
    336       }
    337       fail();
    338     } catch (IOException expected) {
    339       assertSame(ClosedChannelException.class, expected.getClass());
    340     }
    341 
    342     fifoReader.tidyUp();
    343   }
    344 
    345   public void testChannelWrite_exceptionWhenAlreadyInterrupted() throws Exception {
    346     testChannelWrite_exceptionWhenAlreadyInterrupted(ChannelWriter.Method.WRITE);
    347   }
    348 
    349   public void testChannelWriteV_exceptionWhenAlreadyInterrupted() throws Exception {
    350     testChannelWrite_exceptionWhenAlreadyInterrupted(ChannelWriter.Method.WRITEV);
    351   }
    352 
    353   private void testChannelWrite_exceptionWhenAlreadyInterrupted(ChannelWriter.Method method)
    354       throws Exception {
    355     FifoReader fifoReader = new FifoReader(fifoFile);
    356     fifoReader.start();
    357     FileOutputStream fos = new FileOutputStream(fifoFile);
    358     FileChannel fileInputChannel = fos.getChannel();
    359 
    360     Thread.currentThread().interrupt();
    361 
    362     ByteBuffer buffer = ByteBuffer.allocateDirect(10);
    363     try {
    364       if (method == ChannelWriter.Method.WRITE) {
    365         fileInputChannel.write(buffer);
    366       } else {
    367         ByteBuffer buffer2 = ByteBuffer.allocateDirect(10);
    368         fileInputChannel.write(new ByteBuffer[] { buffer, buffer2 });
    369       }
    370       fail();
    371     } catch (IOException expected) {
    372       assertSame(ClosedByInterruptException.class, expected.getClass());
    373     }
    374 
    375     // Check but also clear the interrupted status, so we can wait for the FifoReader thread in
    376     // tidyUp().
    377     assertTrue(Thread.interrupted());
    378 
    379     fifoReader.tidyUp();
    380   }
    381 
    382   public void testChannelWrite_exceptionOnCloseWhenBlocked() throws Exception {
    383     testChannelWrite_exceptionOnCloseWhenBlocked(ChannelWriter.Method.WRITE);
    384   }
    385 
    386   public void testChannelWriteV_exceptionOnCloseWhenBlocked() throws Exception {
    387     testChannelWrite_exceptionOnCloseWhenBlocked(ChannelWriter.Method.WRITEV);
    388   }
    389 
    390   private void testChannelWrite_exceptionOnCloseWhenBlocked(ChannelWriter.Method method)
    391       throws Exception {
    392     FifoReader fifoReader = new FifoReader(fifoFile);
    393     fifoReader.start();
    394     FileChannel fileOutputChannel = new FileOutputStream(fifoFile).getChannel();
    395 
    396     ChannelWriter channelWriter = new ChannelWriter(fileOutputChannel, method);
    397     Thread channelWriterThread = createAndStartThread("ChannelWriter", channelWriter);
    398 
    399     // Delay until we can be fairly sure the writer thread is blocking.
    400     channelWriter.waitForThreadToBlock();
    401 
    402     // Now close the channel to see what happens.
    403     fileOutputChannel.close();
    404 
    405     // Test for expected behavior in the writer thread.
    406     waitToDie(channelWriterThread);
    407     // // The RI throws ChannelClosedException. AsynchronousCloseException is more correct according to
    408     // // the docs.
    409     //
    410     // Lies. RI throws AsynchronousCloseException only if NO data was written before interrupt.
    411     // I altered the ChannelWriter to write exactly 32k bytes and this triggers this behavior.
    412     // the AsynchronousCloseException. If some of data is written, the #write will return the number
    413     // of bytes written, and then the FOLLOWING #write will throw ChannelClosedException because
    414     // file has been closed. Android is actually doing a wrong thing by always throwing the
    415     // AsynchronousCloseException. Client application have no idea that SOME data
    416     //  was written in this case.
    417     assertSame(AsynchronousCloseException.class, channelWriter.ioe.getClass());
    418     assertFalse(channelWriter.wasInterrupted);
    419 
    420     // Tidy up the writer thread.
    421     fifoReader.tidyUp();
    422   }
    423 
    424   public void testChannelWrite_exceptionOnInterrupt() throws Exception {
    425     testChannelWrite_exceptionOnInterrupt(ChannelWriter.Method.WRITE);
    426   }
    427 
    428   public void testChannelWriteV_exceptionOnInterrupt() throws Exception {
    429     testChannelWrite_exceptionOnInterrupt(ChannelWriter.Method.WRITEV);
    430   }
    431 
    432   private void testChannelWrite_exceptionOnInterrupt(ChannelWriter.Method method) throws Exception {
    433     FifoReader fifoReader = new FifoReader(fifoFile);
    434     fifoReader.start();
    435 
    436     FileChannel fileChannel = new FileOutputStream(fifoFile).getChannel();
    437     ChannelWriter channelWriter = new ChannelWriter(fileChannel, method);
    438     Thread channelWriterThread = createAndStartThread("ChannelWriter", channelWriter);
    439 
    440     // Delay until we can be fairly sure the writer thread is blocking.
    441     channelWriter.waitForThreadToBlock();
    442 
    443     // Now interrupt the writer thread to see what happens.
    444     channelWriterThread.interrupt();
    445 
    446     // Test for expected behavior in the writer thread.
    447     waitToDie(channelWriterThread);
    448     assertSame(ClosedByInterruptException.class, channelWriter.ioe.getClass());
    449     assertTrue(channelWriter.wasInterrupted);
    450 
    451     // Tidy up the reader thread.
    452     fifoReader.tidyUp();
    453   }
    454 
    455   private static class StreamReader implements Runnable {
    456 
    457     private final FileInputStream inputStream;
    458     volatile boolean started;
    459     volatile IOException ioe;
    460     volatile boolean wasInterrupted;
    461 
    462     StreamReader(FileInputStream inputStream) {
    463       this.inputStream = inputStream;
    464     }
    465 
    466     @Override
    467     public void run() {
    468       byte[] buffer = new byte[10];
    469       try {
    470         started = true;
    471         int bytesRead = inputStream.read(buffer);
    472         fail("This isn't supposed to happen: read() returned: " + bytesRead);
    473       } catch (IOException e) {
    474         this.ioe = e;
    475       }
    476       wasInterrupted = Thread.interrupted();
    477     }
    478 
    479     public void waitForThreadToBlock() {
    480       for (int i = 0; i < 10 && !started; i++) {
    481         delay(100);
    482       }
    483       assertTrue(started);
    484       // Just give it some more time to start blocking.
    485       delay(100);
    486     }
    487   }
    488 
    489   private static class StreamWriter implements Runnable {
    490 
    491     private final FileOutputStream outputStream;
    492     volatile int bytesWritten;
    493     volatile IOException ioe;
    494     volatile boolean wasInterrupted;
    495 
    496     StreamWriter(FileOutputStream outputStream) {
    497       this.outputStream = outputStream;
    498     }
    499 
    500     @Override
    501     public void run() {
    502       // Writes to FIFOs are buffered. We try to fill the buffer and induce blocking (the
    503       // buffer is typically 64k).
    504       byte[] buffer = new byte[10000];
    505       while (true) {
    506         try {
    507           outputStream.write(buffer);
    508           bytesWritten += buffer.length;
    509         } catch (IOException e) {
    510           this.ioe = e;
    511           break;
    512         }
    513         wasInterrupted = Thread.interrupted();
    514       }
    515     }
    516 
    517     public void waitForThreadToBlock() {
    518       int lastCount = bytesWritten;
    519       for (int i = 0; i < 10; i++) {
    520         delay(500);
    521         int newBytesWritten = bytesWritten;
    522         if (newBytesWritten > 0 && lastCount == newBytesWritten) {
    523           // The thread is probably blocking.
    524           return;
    525         }
    526         lastCount = bytesWritten;
    527       }
    528       fail("Writer never started blocking. Bytes written: " + bytesWritten);
    529     }
    530   }
    531 
    532   private static class ChannelReader implements Runnable {
    533     enum Method {
    534       READ,
    535       READV,
    536     }
    537 
    538     private final FileChannel channel;
    539     private final Method method;
    540     volatile boolean started;
    541     volatile IOException ioe;
    542     volatile boolean wasInterrupted;
    543 
    544     ChannelReader(FileChannel channel, Method method) {
    545       this.channel = channel;
    546       this.method = method;
    547     }
    548 
    549     @Override
    550     public void run() {
    551       ByteBuffer buffer = ByteBuffer.allocateDirect(10);
    552       try {
    553         started = true;
    554         if (method == Method.READ) {
    555           channel.read(buffer);
    556         } else {
    557           ByteBuffer buffer2 = ByteBuffer.allocateDirect(10);
    558           channel.read(new ByteBuffer[] { buffer, buffer2 });
    559         }
    560         fail("All tests should block until an exception");
    561       } catch (IOException e) {
    562         this.ioe = e;
    563       }
    564       wasInterrupted = Thread.interrupted();
    565     }
    566 
    567     public void waitForThreadToBlock() {
    568       for (int i = 0; i < 10 && !started; i++) {
    569         delay(100);
    570       }
    571       assertTrue(started);
    572       // Just give it some more time to start blocking.
    573       delay(100);
    574     }
    575   }
    576 
    577   private static class ChannelWriter implements Runnable {
    578     enum Method {
    579       WRITE,
    580       WRITEV,
    581     }
    582 
    583     private final FileChannel channel;
    584     private final Method method;
    585     volatile int bytesWritten;
    586     volatile IOException ioe;
    587     volatile boolean wasInterrupted;
    588 
    589     ChannelWriter(FileChannel channel, Method method) {
    590       this.channel = channel;
    591       this.method = method;
    592     }
    593 
    594     @Override
    595     public void run() {
    596       ByteBuffer buffer1 = ByteBuffer.allocateDirect(32000);
    597       ByteBuffer buffer2 = ByteBuffer.allocateDirect(32000);
    598       // Writes to FIFOs are buffered. We try to fill the buffer and induce blocking (the
    599       // buffer is typically 64k).
    600       while (true) {
    601         // Make the buffers look non-empty.
    602         buffer1.position(0).limit(buffer1.capacity());
    603         buffer2.position(0).limit(buffer2.capacity());
    604         try {
    605           if (method == Method.WRITE) {
    606             bytesWritten += channel.write(buffer1);
    607           } else {
    608             bytesWritten += channel.write(new ByteBuffer[]{ buffer1, buffer2 });
    609           }
    610         } catch (IOException e) {
    611           this.ioe = e;
    612           break;
    613         }
    614       }
    615       wasInterrupted = Thread.interrupted();
    616     }
    617 
    618     public void waitForThreadToBlock() {
    619       int lastCount = bytesWritten;
    620       for (int i = 0; i < 10; i++) {
    621         delay(500);
    622         int newBytesWritten = bytesWritten;
    623         if (newBytesWritten > 0 && lastCount == newBytesWritten) {
    624           // The thread is probably blocking.
    625           return;
    626         }
    627         lastCount = bytesWritten;
    628       }
    629       fail("Writer never started blocking. Bytes written: " + bytesWritten);
    630     }
    631   }
    632 
    633   /**
    634    * Opens a FIFO for writing. Exists to unblock the other end of the FIFO.
    635    */
    636   private static class FifoWriter extends Thread {
    637 
    638     private final File file;
    639     private FileOutputStream fos;
    640 
    641     public FifoWriter(File file) {
    642       super("FifoWriter");
    643       this.file = file;
    644     }
    645 
    646     @Override
    647     public void run() {
    648       try {
    649         fos = new FileOutputStream(file);
    650       } catch (IOException ignored) {
    651       }
    652     }
    653 
    654     public void tidyUp() {
    655       FileIOInterruptTest.waitToDie(this);
    656       closeQuietly(fos);
    657     }
    658   }
    659 
    660   /**
    661    * Opens a FIFO for reading. Exists to unblock the other end of the FIFO.
    662    */
    663   private static class FifoReader extends Thread {
    664 
    665     private final File file;
    666     private FileInputStream fis;
    667 
    668     public FifoReader(File file) {
    669       super("FifoReader");
    670       this.file = file;
    671     }
    672 
    673     @Override
    674     public void run() {
    675       try {
    676         fis = new FileInputStream(file);
    677       } catch (IOException ignored) {
    678       }
    679     }
    680 
    681     public void tidyUp() {
    682       FileIOInterruptTest.waitToDie(this);
    683       closeQuietly(fis);
    684     }
    685   }
    686 
    687   private static Thread createAndStartThread(String name, Runnable runnable) {
    688     Thread t = new Thread(runnable, name);
    689     t.setDaemon(true);
    690     t.start();
    691     return t;
    692   }
    693 
    694   private static void waitToDie(Thread thread) {
    695     // Protect against this thread already being interrupted, which would prevent the test waiting
    696     // for the requested time.
    697     assertFalse(Thread.currentThread().isInterrupted());
    698     try {
    699       thread.join(5000);
    700     } catch (InterruptedException ignored) {
    701     }
    702 
    703     if (thread.isAlive()) {
    704       fail("Thread \"" + thread.getName() + "\" did not exit.");
    705     }
    706   }
    707 
    708   private static void delay(int millis) {
    709     // Protect against this thread being interrupted, which would prevent us waiting.
    710     assertFalse(Thread.currentThread().isInterrupted());
    711     try {
    712       Thread.sleep(millis);
    713     } catch (InterruptedException ignored) {
    714     }
    715   }
    716 
    717 }
    718