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 ss = new ServerSocket(0); 42 new Killer(ss).start(); 43 try { 44 System.err.println("accept..."); 45 Socket s = ss.accept(); 46 fail("accept returned " + s + "!"); 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(false); 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: " + s + "!"); 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(false); 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: " + s + "!"); 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(false); 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: " + s + "!"); 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(128); // Minimal receive buffer size. 180 Socket s = new Socket(); 181 182 // Set the send buffer size really small, to ensure we block. 183 int sendBufferSize = 1024; 184 s.setSendBufferSize(sendBufferSize); 185 sendBufferSize = s.getSendBufferSize(); // How big is the buffer really, Linux? 186 187 // Linux still seems to accept more than it should. 188 // How much seems to differ from device to device, but I've yet to see anything accept 189 // twice as much again. 190 sendBufferSize *= 2; 191 192 s.connect(ss.getLocalSocketAddress()); 193 new Killer(s).start(); 194 try { 195 System.err.println("write..."); 196 // Write too much so the buffer is full and we block, 197 // waiting for the server to read (which it never will). 198 // If the asynchronous close fails, we'll see a test timeout here. 199 byte[] buf = new byte[sendBufferSize]; 200 s.getOutputStream().write(buf); 201 fail(); 202 } catch (SocketException expected) { 203 // We throw "Connection reset by peer", which I don't _think_ is a problem. 204 // assertEquals("Socket closed", expected.getMessage()); 205 } 206 ss.close(); 207 } 208 209 // This server accepts connections, but doesn't read or write anything. 210 // It holds on to the Socket connecting to the client so it won't be GCed. 211 // Call "close" to close both the server socket and its client connection. 212 static class SilentServer { 213 private final ServerSocket ss; 214 private Socket client; 215 216 public SilentServer() throws IOException { 217 this(0); 218 } 219 220 public SilentServer(int receiveBufferSize) throws IOException { 221 ss = new ServerSocket(0); 222 if (receiveBufferSize != 0) { 223 ss.setReceiveBufferSize(receiveBufferSize); 224 } 225 new Thread(new Runnable() { 226 public void run() { 227 try { 228 client = ss.accept(); 229 } catch (Exception ex) { 230 ex.printStackTrace(); 231 } 232 } 233 }).start(); 234 } 235 236 public SocketAddress getLocalSocketAddress() { 237 return ss.getLocalSocketAddress(); 238 } 239 240 public void close() throws IOException { 241 client.close(); 242 ss.close(); 243 } 244 } 245 246 // This thread calls the "close" method on the supplied T after 2s. 247 static class Killer<T> extends Thread { 248 private final T s; 249 250 public Killer(T s) { 251 this.s = s; 252 } 253 254 public void run() { 255 try { 256 System.err.println("sleep..."); 257 Thread.sleep(2000); 258 System.err.println("close..."); 259 s.getClass().getMethod("close").invoke(s); 260 } catch (Exception ex) { 261 ex.printStackTrace(); 262 } 263 } 264 } 265} 266