1/* 2 * Copyright (C) 2011 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 17package libcore.java.io; 18 19import java.io.IOException; 20import java.io.InputStream; 21import java.io.InterruptedIOException; 22import java.io.OutputStream; 23import java.io.PipedInputStream; 24import java.io.PipedOutputStream; 25import java.io.PipedReader; 26import java.io.PipedWriter; 27import java.net.InetSocketAddress; 28import java.net.Socket; 29import java.nio.ByteBuffer; 30import java.nio.channels.ClosedByInterruptException; 31import java.nio.channels.ClosedChannelException; 32import java.nio.channels.Pipe; 33import java.nio.channels.ReadableByteChannel; 34import java.nio.channels.ServerSocketChannel; 35import java.nio.channels.SocketChannel; 36import java.nio.channels.WritableByteChannel; 37import junit.framework.TestCase; 38 39/** 40 * Test that interrupting a thread blocked on I/O causes that thread to throw 41 * an InterruptedIOException. 42 */ 43public final class InterruptedStreamTest extends TestCase { 44 45 private static final int BUFFER_SIZE = 1024 * 1024; 46 47 private Socket[] sockets; 48 49 @Override protected void setUp() throws Exception { 50 Thread.interrupted(); // clear interrupted bit 51 super.tearDown(); 52 } 53 54 @Override protected void tearDown() throws Exception { 55 if (sockets != null) { 56 sockets[0].close(); 57 sockets[1].close(); 58 sockets = null; 59 } 60 Thread.interrupted(); // clear interrupted bit 61 super.tearDown(); 62 } 63 64 public void testInterruptPipedInputStream() throws Exception { 65 PipedOutputStream out = new PipedOutputStream(); 66 PipedInputStream in = new PipedInputStream(out); 67 testInterruptInputStream(in); 68 } 69 70 public void testInterruptPipedOutputStream() throws Exception { 71 PipedOutputStream out = new PipedOutputStream(); 72 new PipedInputStream(out); 73 testInterruptOutputStream(out); 74 } 75 76 public void testInterruptPipedReader() throws Exception { 77 PipedWriter writer = new PipedWriter(); 78 PipedReader reader = new PipedReader(writer); 79 testInterruptReader(reader); 80 } 81 82 public void testInterruptPipedWriter() throws Exception { 83 final PipedWriter writer = new PipedWriter(); 84 new PipedReader(writer); 85 testInterruptWriter(writer); 86 } 87 88 public void testInterruptReadablePipeChannel() throws Exception { 89 testInterruptReadableChannel(Pipe.open().source()); 90 } 91 92 public void testInterruptWritablePipeChannel() throws Exception { 93 testInterruptWritableChannel(Pipe.open().sink()); 94 } 95 96 public void testInterruptReadableSocketChannel() throws Exception { 97 sockets = newSocketChannelPair(); 98 testInterruptReadableChannel(sockets[0].getChannel()); 99 } 100 101 public void testInterruptWritableSocketChannel() throws Exception { 102 sockets = newSocketChannelPair(); 103 testInterruptWritableChannel(sockets[0].getChannel()); 104 } 105 106 /** 107 * Returns a pair of connected sockets backed by NIO socket channels. 108 */ 109 private Socket[] newSocketChannelPair() throws IOException { 110 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); 111 serverSocketChannel.socket().bind(new InetSocketAddress(0)); 112 SocketChannel clientSocketChannel = SocketChannel.open(); 113 clientSocketChannel.connect(serverSocketChannel.socket().getLocalSocketAddress()); 114 SocketChannel server = serverSocketChannel.accept(); 115 serverSocketChannel.close(); 116 return new Socket[] { clientSocketChannel.socket(), server.socket() }; 117 } 118 119 private void testInterruptInputStream(final InputStream in) throws Exception { 120 Thread thread = interruptMeLater(); 121 try { 122 in.read(); 123 fail(); 124 } catch (InterruptedIOException expected) { 125 } finally { 126 waitForInterrupt(thread); 127 } 128 } 129 130 private void testInterruptReader(final PipedReader reader) throws Exception { 131 Thread thread = interruptMeLater(); 132 try { 133 reader.read(); 134 fail(); 135 } catch (InterruptedIOException expected) { 136 } finally { 137 waitForInterrupt(thread); 138 } 139 } 140 141 private void testInterruptReadableChannel(final ReadableByteChannel channel) throws Exception { 142 Thread thread = interruptMeLater(); 143 try { 144 channel.read(ByteBuffer.allocate(BUFFER_SIZE)); 145 fail(); 146 } catch (ClosedByInterruptException expected) { 147 } finally { 148 waitForInterrupt(thread); 149 } 150 } 151 152 private void testInterruptOutputStream(final OutputStream out) throws Exception { 153 Thread thread = interruptMeLater(); 154 try { 155 // this will block when the receiving buffer fills up 156 while (true) { 157 out.write(new byte[BUFFER_SIZE]); 158 } 159 } catch (InterruptedIOException expected) { 160 } finally { 161 waitForInterrupt(thread); 162 } 163 } 164 165 private void testInterruptWriter(final PipedWriter writer) throws Exception { 166 Thread thread = interruptMeLater(); 167 try { 168 // this will block when the receiving buffer fills up 169 while (true) { 170 writer.write(new char[BUFFER_SIZE]); 171 } 172 } catch (InterruptedIOException expected) { 173 } finally { 174 waitForInterrupt(thread); 175 } 176 } 177 178 private void testInterruptWritableChannel(final WritableByteChannel channel) throws Exception { 179 Thread thread = interruptMeLater(); 180 try { 181 // this will block when the receiving buffer fills up 182 while (true) { 183 channel.write(ByteBuffer.allocate(BUFFER_SIZE)); 184 } 185 } catch (ClosedByInterruptException expected) { 186 } catch (ClosedChannelException expected) { 187 } finally { 188 waitForInterrupt(thread); 189 } 190 } 191 192 private Thread interruptMeLater() throws Exception { 193 final Thread toInterrupt = Thread.currentThread(); 194 Thread thread = new Thread(new Runnable () { 195 @Override public void run() { 196 try { 197 Thread.sleep(1000); 198 } catch (InterruptedException ex) { 199 } 200 toInterrupt.interrupt(); 201 } 202 }); 203 thread.start(); 204 return thread; 205 } 206 207 private static void waitForInterrupt(Thread thread) throws Exception { 208 try { 209 thread.join(); 210 } catch (InterruptedException ignore) { 211 // There is currently a race between Thread.interrupt in 212 // interruptMeLater and Thread.join here. Most of the time 213 // we won't get an InterruptedException, but occasionally 214 // we do, so for now ignore this exception. 215 // http://b/6951157 216 } 217 } 218} 219