ConcurrentCloseTest.java revision 3d43199950729e592ac3e104442b4b350a86aec8
1/* 2 * Copyright (C) 2010 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.net; 18 19import java.io.IOException; 20import java.net.DatagramPacket; 21import java.net.DatagramSocket; 22import java.net.InetSocketAddress; 23import java.net.ServerSocket; 24import java.net.Socket; 25import java.net.SocketAddress; 26import java.net.SocketException; 27import java.nio.channels.AsynchronousCloseException; 28import java.nio.channels.ClosedChannelException; 29import java.nio.channels.SocketChannel; 30import java.util.ArrayList; 31import java.util.List; 32import java.util.concurrent.CopyOnWriteArrayList; 33import tests.net.StuckServer; 34 35/** 36 * Test that Socket.close called on another thread interrupts a thread that's blocked doing 37 * network I/O. 38 */ 39public class ConcurrentCloseTest extends junit.framework.TestCase { 40 public void test_accept() throws Exception { 41 ServerSocket s = new ServerSocket(0); 42 new Killer(s).start(); 43 try { 44 System.err.println("accept..."); 45 s.accept(); 46 fail("accept returned!"); 47 } catch (SocketException expected) { 48 assertEquals("Socket closed", expected.getMessage()); 49 } 50 } 51 52 public void test_connect() throws Exception { 53 StuckServer ss = new StuckServer(); 54 Socket s = new Socket(); 55 new Killer(s).start(); 56 try { 57 System.err.println("connect..."); 58 s.connect(ss.getLocalSocketAddress()); 59 fail("connect returned!"); 60 } catch (SocketException expected) { 61 assertEquals("Socket closed", expected.getMessage()); 62 } finally { 63 ss.close(); 64 } 65 } 66 67 public void test_connect_timeout() throws Exception { 68 StuckServer ss = new StuckServer(); 69 Socket s = new Socket(); 70 new Killer(s).start(); 71 try { 72 System.err.println("connect (with timeout)..."); 73 s.connect(ss.getLocalSocketAddress(), 3600 * 1000); 74 fail("connect returned!"); 75 } catch (SocketException expected) { 76 assertEquals("Socket closed", expected.getMessage()); 77 } finally { 78 ss.close(); 79 } 80 } 81 82 public void test_connect_nonBlocking() throws Exception { 83 StuckServer ss = new StuckServer(); 84 SocketChannel s = SocketChannel.open(); 85 new Killer(s.socket()).start(); 86 try { 87 System.err.println("connect (non-blocking)..."); 88 s.configureBlocking(false); 89 s.connect(ss.getLocalSocketAddress()); 90 while (!s.finishConnect()) { 91 // Spin like a mad thing! 92 } 93 fail("connect returned!"); 94 } catch (SocketException expected) { 95 assertEquals("Socket closed", expected.getMessage()); 96 } catch (AsynchronousCloseException alsoOkay) { 97 // See below. 98 } catch (ClosedChannelException alsoOkay) { 99 // For now, I'm assuming that we're happy as long as we get any reasonable exception. 100 // It may be that we're supposed to guarantee only one or the other. 101 } finally { 102 ss.close(); 103 } 104 } 105 106 public void test_read() throws Exception { 107 SilentServer ss = new SilentServer(); 108 Socket s = new Socket(); 109 s.connect(ss.getLocalSocketAddress()); 110 new Killer(s).start(); 111 try { 112 System.err.println("read..."); 113 int i = s.getInputStream().read(); 114 fail("read returned " + i); 115 } catch (SocketException expected) { 116 assertEquals("Socket closed", expected.getMessage()); 117 } 118 ss.close(); 119 } 120 121 public void test_read_multiple() throws Throwable { 122 SilentServer ss = new SilentServer(); 123 final Socket s = new Socket(); 124 s.connect(ss.getLocalSocketAddress()); 125 126 // We want to test that we unblock *all* the threads blocked on a socket, not just one. 127 // We know the implementation uses the same mechanism for all blocking calls, so we just 128 // test read(2) because it's the easiest to test. (recv(2), for example, is only accessible 129 // from Java via a synchronized method.) 130 final ArrayList<Thread> threads = new ArrayList<Thread>(); 131 final List<Throwable> thrownExceptions = new CopyOnWriteArrayList<Throwable>(); 132 for (int i = 0; i < 10; ++i) { 133 Thread t = new Thread(new Runnable() { 134 public void run() { 135 try { 136 try { 137 System.err.println("read..."); 138 int i = s.getInputStream().read(); 139 fail("read returned " + i); 140 } catch (SocketException expected) { 141 assertEquals("Socket closed", expected.getMessage()); 142 } 143 } catch (Throwable ex) { 144 thrownExceptions.add(ex); 145 } 146 } 147 }); 148 threads.add(t); 149 } 150 for (Thread t : threads) { 151 t.start(); 152 } 153 new Killer(s).start(); 154 for (Thread t : threads) { 155 t.join(); 156 } 157 for (Throwable exception : thrownExceptions) { 158 throw exception; 159 } 160 161 ss.close(); 162 } 163 164 public void test_recv() throws Exception { 165 DatagramSocket s = new DatagramSocket(); 166 byte[] buf = new byte[200]; 167 DatagramPacket p = new DatagramPacket(buf, 200); 168 new Killer(s).start(); 169 try { 170 System.err.println("receive..."); 171 s.receive(p); 172 fail("receive returned!"); 173 } catch (SocketException expected) { 174 assertEquals("Socket closed", expected.getMessage()); 175 } 176 } 177 178 public void test_write() throws Exception { 179 final SilentServer ss = new SilentServer(); 180 Socket s = new Socket(); 181 s.connect(ss.getLocalSocketAddress()); 182 new Killer(s).start(); 183 try { 184 System.err.println("write..."); 185 // We just keep writing here until all the buffers are full and we block, 186 // waiting for the server to read (which it never will). If the asynchronous close 187 // fails, we'll see a test timeout here. 188 while (true) { 189 byte[] buf = new byte[256*1024]; 190 s.getOutputStream().write(buf); 191 } 192 } catch (SocketException expected) { 193 // We throw "Connection reset by peer", which I don't _think_ is a problem. 194 // assertEquals("Socket closed", expected.getMessage()); 195 } 196 ss.close(); 197 } 198 199 // This server accepts connections, but doesn't read or write anything. 200 // It holds on to the Socket connecting to the client so it won't be GCed. 201 // Call "close" to close both the server socket and its client connection. 202 static class SilentServer { 203 private final ServerSocket ss; 204 private Socket client; 205 206 public SilentServer() throws IOException { 207 ss = new ServerSocket(0); 208 new Thread(new Runnable() { 209 public void run() { 210 try { 211 client = ss.accept(); 212 } catch (Exception ex) { 213 ex.printStackTrace(); 214 } 215 } 216 }).start(); 217 } 218 219 public SocketAddress getLocalSocketAddress() { 220 return ss.getLocalSocketAddress(); 221 } 222 223 public void close() throws IOException { 224 client.close(); 225 ss.close(); 226 } 227 } 228 229 // This thread calls the "close" method on the supplied T after 2s. 230 static class Killer<T> extends Thread { 231 private final T s; 232 233 public Killer(T s) { 234 this.s = s; 235 } 236 237 public void run() { 238 try { 239 System.err.println("sleep..."); 240 Thread.sleep(2000); 241 System.err.println("close..."); 242 s.getClass().getMethod("close").invoke(s); 243 } catch (Exception ex) { 244 ex.printStackTrace(); 245 } 246 } 247 } 248} 249