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