1/*
2 * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.nio.ch;
27
28import java.io.IOException;
29import java.nio.channels.*;
30import java.nio.channels.spi.*;
31import java.util.*;
32
33/**
34 * An implementation of Selector for Linux 2.6+ kernels that uses
35 * the epoll event notification facility.
36 */
37class EPollSelectorImpl
38    extends SelectorImpl
39{
40
41    // File descriptors used for interrupt
42    protected int fd0;
43    protected int fd1;
44
45    // The poll object
46    EPollArrayWrapper pollWrapper;
47
48    // Maps from file descriptors to keys
49    private Map<Integer,SelectionKeyImpl> fdToKey;
50
51    // True if this Selector has been closed
52    private volatile boolean closed;
53
54    // Lock for interrupt triggering and clearing
55    private final Object interruptLock = new Object();
56    private boolean interruptTriggered = false;
57
58    /**
59     * Package private constructor called by factory method in
60     * the abstract superclass Selector.
61     */
62    EPollSelectorImpl(SelectorProvider sp) throws IOException {
63        super(sp);
64        long pipeFds = IOUtil.makePipe(false);
65        fd0 = (int) (pipeFds >>> 32);
66        fd1 = (int) pipeFds;
67        try {
68            pollWrapper = new EPollArrayWrapper();
69            pollWrapper.initInterrupt(fd0, fd1);
70            fdToKey = new HashMap<>();
71        } catch (Throwable t) {
72            try {
73                FileDispatcherImpl.closeIntFD(fd0);
74            } catch (IOException ioe0) {
75                t.addSuppressed(ioe0);
76            }
77            try {
78                FileDispatcherImpl.closeIntFD(fd1);
79            } catch (IOException ioe1) {
80                t.addSuppressed(ioe1);
81            }
82            throw t;
83        }
84    }
85
86    protected int doSelect(long timeout) throws IOException {
87        if (closed)
88            throw new ClosedSelectorException();
89        processDeregisterQueue();
90        try {
91            begin();
92            pollWrapper.poll(timeout);
93        } finally {
94            end();
95        }
96        processDeregisterQueue();
97        int numKeysUpdated = updateSelectedKeys();
98        if (pollWrapper.interrupted()) {
99            // Clear the wakeup pipe
100            pollWrapper.putEventOps(pollWrapper.interruptedIndex(), 0);
101            synchronized (interruptLock) {
102                pollWrapper.clearInterrupted();
103                IOUtil.drain(fd0);
104                interruptTriggered = false;
105            }
106        }
107        return numKeysUpdated;
108    }
109
110    /**
111     * Update the keys whose fd's have been selected by the epoll.
112     * Add the ready keys to the ready queue.
113     */
114    private int updateSelectedKeys() {
115        int entries = pollWrapper.updated;
116        int numKeysUpdated = 0;
117        for (int i=0; i<entries; i++) {
118            int nextFD = pollWrapper.getDescriptor(i);
119            SelectionKeyImpl ski = fdToKey.get(Integer.valueOf(nextFD));
120            // ski is null in the case of an interrupt
121            if (ski != null) {
122                int rOps = pollWrapper.getEventOps(i);
123                if (selectedKeys.contains(ski)) {
124                    if (ski.channel.translateAndSetReadyOps(rOps, ski)) {
125                        numKeysUpdated++;
126                    }
127                } else {
128                    ski.channel.translateAndSetReadyOps(rOps, ski);
129                    if ((ski.nioReadyOps() & ski.nioInterestOps()) != 0) {
130                        selectedKeys.add(ski);
131                        numKeysUpdated++;
132                    }
133                }
134            }
135        }
136        return numKeysUpdated;
137    }
138
139    protected void implClose() throws IOException {
140        if (closed)
141            return;
142        closed = true;
143
144        // prevent further wakeup
145        synchronized (interruptLock) {
146            interruptTriggered = true;
147        }
148
149        FileDispatcherImpl.closeIntFD(fd0);
150        FileDispatcherImpl.closeIntFD(fd1);
151
152        pollWrapper.closeEPollFD();
153        // it is possible
154        selectedKeys = null;
155
156        // Deregister channels
157        Iterator<SelectionKey> i = keys.iterator();
158        while (i.hasNext()) {
159            SelectionKeyImpl ski = (SelectionKeyImpl)i.next();
160            deregister(ski);
161            SelectableChannel selch = ski.channel();
162            if (!selch.isOpen() && !selch.isRegistered())
163                ((SelChImpl)selch).kill();
164            i.remove();
165        }
166
167        fd0 = -1;
168        fd1 = -1;
169    }
170
171    protected void implRegister(SelectionKeyImpl ski) {
172        if (closed)
173            throw new ClosedSelectorException();
174        SelChImpl ch = ski.channel;
175        int fd = Integer.valueOf(ch.getFDVal());
176        fdToKey.put(fd, ski);
177        pollWrapper.add(fd);
178        keys.add(ski);
179    }
180
181    protected void implDereg(SelectionKeyImpl ski) throws IOException {
182        assert (ski.getIndex() >= 0);
183        SelChImpl ch = ski.channel;
184        int fd = ch.getFDVal();
185        fdToKey.remove(Integer.valueOf(fd));
186        pollWrapper.remove(fd);
187        ski.setIndex(-1);
188        keys.remove(ski);
189        selectedKeys.remove(ski);
190        deregister((AbstractSelectionKey)ski);
191        SelectableChannel selch = ski.channel();
192        if (!selch.isOpen() && !selch.isRegistered())
193            ((SelChImpl)selch).kill();
194    }
195
196    public void putEventOps(SelectionKeyImpl ski, int ops) {
197        if (closed)
198            throw new ClosedSelectorException();
199        SelChImpl ch = ski.channel;
200        pollWrapper.setInterest(ch.getFDVal(), ops);
201    }
202
203    public Selector wakeup() {
204        synchronized (interruptLock) {
205            if (!interruptTriggered) {
206                pollWrapper.interrupt();
207                interruptTriggered = true;
208            }
209        }
210        return this;
211    }
212}
213