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