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