summaryrefslogtreecommitdiff
path: root/ojluni/src/main/java/sun/nio/ch/DefaultSelectorProvider.java
blob: bafb74e23524c89ead01cb8de34a47b8732d4e8a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/*
 * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.nio.ch;

import java.nio.channels.spi.SelectorProvider;
import java.security.AccessController;
import sun.security.action.GetPropertyAction;

/**
 * Creates this platform's default SelectorProvider
 */

public class DefaultSelectorProvider {

    /**
     * Prevent instantiation.
     */
    private DefaultSelectorProvider() { }

    // Android-removed: Dead code: We always use PollSelectorProvider.
    /*
    @SuppressWarnings("unchecked")
    private static SelectorProvider createProvider(String cn) {
        Class<SelectorProvider> c;
        try {
            c = (Class<SelectorProvider>)Class.forName(cn);
        } catch (ClassNotFoundException x) {
            throw new AssertionError(x);
        }
        try {
            return c.newInstance();
        } catch (IllegalAccessException | InstantiationException x) {
            throw new AssertionError(x);
        }

    }
    */

    /**
     * Returns the default SelectorProvider.
     */
    public static SelectorProvider create() {
        // Android-note: Explain why we always use of PollSelectorProvider.
        /*
        The OpenJDK epoll based selector suffers from a serious bug where it
        can never successfully deregister keys from closed channels.

        The root cause of this bug is the sequence of operations that occur when
        a channel that's registered with a selector is closed :

        (0) Application code calls Channel.close().

        (1) The channel is "preClosed" - We dup2(2) /dev/null into the channel's
        file descriptor and the channel is marked as closed at the Java level.

        (2) All keys associated with the channel are cancelled. Cancels are
        lazy, which means that the Selectors involved won't necessarily
        deregister these keys until an ongoing call to select() (if any) returns
        or until the next call to select() on that selector.

        (3) Once all selectors associated with the channel deregister these
        cancelled keys, the channel FD is properly closed (via close(2)). Note
        that an arbitrary length of time might elapse between Step 0 and this step.
        This isn't a resource leak because the channel's FD is now a reference
        to "/dev/null".

        THE PROBLEM :
        -------------
        The default Selector implementation on Linux 2.6 and higher uses epoll(7).
        epoll can scale better than poll(2) because a lot of the state related
        to the interest set (the set of descriptors we're polling on) is
        maintained by the kernel. One of the side-effects of this design is that
        callers must call into the kernel to make changes to the interest set
        via epoll_ctl(7), for eg., by using EPOLL_CTL_ADD to add descriptors or
        EPOLL_CTL_DEL to remove descriptors from the interest set. A call to
        epoll_ctl with op = EPOLL_CTL_DEL is made when the selector attempts to
        deregister an FD associated with a channel from the interest set (see
        Step 2, above). These calls will *always fail* because the channel has
        been preClosed (see Step 1). They fail because the kernel uses its own
        internal file structure to maintain state, and rejects the command
        because the descriptor we're passing in describes a different file
        (/dev/null) that isn't selectable and isn't registered with the epoll
        instance.

        This is an issue in upstream OpenJDK as well and various select
        implementations (such as netty) have hacks to work around it. Outside
        of Android, things will work OK in most cases because the kernel has its
        own internal cleanup logic to deregister files from epoll instances
        whenever the last *non epoll* reference to the file has been closed -
        and usually this happens at the point at which the dup2(2) from Step 1
        is called. However, on Android, sockets tagged with the SocketTagger
        will never hit this code path because the socket tagging implementation
        (qtaguid) keeps a reference to the internal file until the socket
        has been untagged. In cases where sockets are closed without being
        untagged, the tagger keeps a reference to it until the process dies.

        THE SOLUTION :
        --------------
        We switch over to using poll(2) instead of epoll(7). One of the
        advantages of poll(2) is that there's less state maintained by the
        kernel. We don't need to make a syscall (analogous to epoll_ctl)
        whenever we want to remove an FD from the interest set; we merely
        remove it from the list of FDs passed in the next time we call
        through to poll. Poll is also slightly more efficient and less
        overhead to set up when the number of FDs being polled is small
        (which is the common case on Android).

        ALTERNATE APPROACHES :
        ----------------------
        For completeness, I'm listing a couple of other approaches that were
        considered but discarded.

        - Removing preClose: This has the disadvantage of increasing the amount
        of time (Delta between Step 0 and Step 3) a channel's descriptor is
        kept alive. This also opens up races in the rare case where a
        closed FD number is reused on a different thread while we have reads
        pending.

        - A Synchronous call to EPOLL_CTL_DEL when a channel is removed: This is a
        non-starter because of the specified order of events in
        AbstractSelectableChannel; implCloseSelectableChannel must be called
        */


        // Android-changed: Always use PollSelectorProvider.
        /*
        String osname = AccessController
            .doPrivileged(new GetPropertyAction("os.name"));
        if (osname.equals("SunOS"))
            return createProvider("sun.nio.ch.DevPollSelectorProvider");
        if (osname.equals("Linux"))
            return createProvider("sun.nio.ch.EPollSelectorProvider");
        */
        return new sun.nio.ch.PollSelectorProvider();
    }

}