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