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 */
16package libcore.java.nio.channels;
17
18import android.system.Os;
19import java.io.IOException;
20import java.net.InetSocketAddress;
21import java.net.ServerSocket;
22import java.io.FileDescriptor;
23import java.nio.ByteBuffer;
24import java.nio.channels.NoConnectionPendingException;
25import java.nio.channels.SelectionKey;
26import java.nio.channels.Selector;
27import java.nio.channels.ServerSocketChannel;
28import java.nio.channels.SocketChannel;
29import java.util.concurrent.CountDownLatch;
30import java.util.concurrent.TimeUnit;
31import junit.framework.TestCase;
32
33public class SelectorTest extends TestCase {
34    public void testNonBlockingConnect_immediate() throws Exception {
35        // Test the case where we [probably] connect immediately.
36        Selector selector = Selector.open();
37        ServerSocketChannel ssc = ServerSocketChannel.open();
38        try {
39            ssc.configureBlocking(false);
40            ssc.socket().bind(null);
41
42            SocketChannel sc = SocketChannel.open();
43            sc.configureBlocking(false);
44            sc.connect(ssc.socket().getLocalSocketAddress());
45            SelectionKey key = sc.register(selector, SelectionKey.OP_CONNECT);
46            assertEquals(1, selector.select());
47            assertEquals(SelectionKey.OP_CONNECT, key.readyOps());
48            sc.finishConnect();
49        } finally {
50            selector.close();
51            ssc.close();
52        }
53    }
54
55    // http://code.google.com/p/android/issues/detail?id=15388
56    public void testInterrupted() throws IOException {
57        Selector selector = Selector.open();
58        Thread.currentThread().interrupt();
59        try {
60            int count = selector.select();
61            assertEquals(0, count);
62            assertTrue(Thread.currentThread().isInterrupted());
63        } finally {
64            // Clear the interrupted thread state so that it does not interfere with later tests.
65            Thread.interrupted();
66
67            selector.close();
68        }
69    }
70
71    public void testManyWakeupCallsTriggerOnlyOneWakeup() throws Exception {
72        final Selector selector = Selector.open();
73        try {
74            selector.wakeup();
75            selector.wakeup();
76            selector.wakeup();
77            selector.select();
78
79            // create a latch that will reach 0 when select returns
80            final CountDownLatch selectReturned = new CountDownLatch(1);
81            Thread thread = new Thread(new Runnable() {
82                @Override public void run() {
83                    try {
84                        selector.select();
85                        selectReturned.countDown();
86                    } catch (IOException ignored) {
87                    }
88                }
89            });
90            thread.start();
91
92            // select doesn't ever return, so await() times out and returns false
93            assertFalse(selectReturned.await(2, TimeUnit.SECONDS));
94        } finally {
95            selector.close();
96        }
97    }
98
99    // We previously leaked a file descriptor for each selector instance created.
100    //
101    // http://code.google.com/p/android/issues/detail?id=5993
102    // http://code.google.com/p/android/issues/detail?id=4825
103    public void testLeakingPipes() throws IOException {
104        for (int i = 0; i < 2000; i++) {
105            Selector selector = Selector.open();
106            selector.close();
107        }
108    }
109
110    public void test_57456() throws Exception {
111        Selector selector = Selector.open();
112        ServerSocketChannel ssc = ServerSocketChannel.open();
113
114        try {
115            // Connect.
116            ssc.configureBlocking(false);
117            ssc.socket().bind(null);
118            SocketChannel sc = SocketChannel.open();
119            sc.connect(ssc.socket().getLocalSocketAddress());
120            sc.finishConnect();
121
122            // Switch to non-blocking so we can use a Selector.
123            sc.configureBlocking(false);
124
125            // Have the 'server' write something.
126            ssc.accept().write(ByteBuffer.allocate(128));
127
128            // At this point, the client should be able to read or write immediately.
129            // (It shouldn't be able to connect because it's already connected.)
130            SelectionKey key = sc.register(selector,
131                    SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE);
132            assertEquals(1, selector.select());
133            assertEquals(SelectionKey.OP_READ | SelectionKey.OP_WRITE, key.readyOps());
134            assertEquals(0, selector.select());
135        } finally {
136            selector.close();
137            ssc.close();
138        }
139    }
140
141    // http://code.google.com/p/android/issues/detail?id=80785
142    public void test_80785() throws Exception {
143        Selector selector = Selector.open();
144        selector.close();
145
146        // Historically on android this did not throw an exception. Due to the bug it would throw
147        // an (undeclared) IOException.
148        selector.wakeup();
149    }
150
151    public void test28318596() throws Exception {
152        Selector selector = Selector.open();
153        ServerSocketChannel ssc = ServerSocketChannel.open();
154        SocketChannel server = null;
155        FileDescriptor dup = null;
156        try {
157            ssc.configureBlocking(false);
158            ssc.bind(null);
159            SocketChannel sc = SocketChannel.open();
160            sc.connect(ssc.getLocalAddress());
161            sc.finishConnect();
162
163            // Switch to non-blocking so we can use a Selector.
164            sc.configureBlocking(false);
165
166            sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
167            assertEquals(1, selector.select(100));
168            assertEquals(0, selector.select(100));
169
170            server = ssc.accept();
171            server.write(ByteBuffer.allocate(8192));
172
173            // This triggered b/28318596. We'd call through to preClose() which would dup2
174            // a known sink descriptor into the channel's descriptor. All subsequent calls
175            // to epoll_ctl(EPOLL_CTL_DEL) would then fail because the kernel was unhappy about
176            // the fact that the descriptor was associated with a different file. This meant that
177            // we'd spuriously return from select because we've never managed to remove the file
178            // associated with the selection key from the epoll fd's interest set.
179            server.shutdownInput();
180            server.shutdownOutput();
181            // We dup the socket here to work around kernel cleanup mechanisms. The kernel will
182            // automatically unregister a file reference from all associated epoll instances once
183            // the last non-epoll instance has been closed.
184            dup = Os.dup(sc.socket().getFileDescriptor$());
185            sc.close();
186
187            // The following is a finicky loop to try and figure out whether we're going into
188            // a tight loop where select returns immediately (we should've received a POLLHUP
189            // and/or POLLIN on |sc|).
190            long start = System.currentTimeMillis();
191            for (int i = 0; i < 10; ++i) {
192                assertEquals(0, selector.select(500));
193            }
194
195            server.close();
196            long end = System.currentTimeMillis();
197            // There should have been no events during the loop above (the size of
198            // the interest set is zero) so all of the selects should timeout and take
199            // ~5000ms.
200            assertTrue("Time taken: " + (end - start), (end - start) > 2000);
201        } finally {
202            selector.close();
203            ssc.close();
204
205            if (server != null) {
206                server.close();
207            }
208
209            if (dup != null) {
210                Os.close(dup);
211            }
212        }
213    }
214}
215