Path: blob/master/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java
40948 views
/*1* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.nio.ch;2627import java.io.IOException;28import java.nio.channels.ClosedSelectorException;29import java.nio.channels.SelectionKey;30import java.nio.channels.Selector;31import java.nio.channels.spi.SelectorProvider;32import java.util.ArrayDeque;33import java.util.Deque;34import java.util.HashMap;35import java.util.Map;36import java.util.concurrent.TimeUnit;37import java.util.function.Consumer;3839import static sun.nio.ch.EPoll.EPOLLIN;40import static sun.nio.ch.EPoll.EPOLL_CTL_ADD;41import static sun.nio.ch.EPoll.EPOLL_CTL_DEL;42import static sun.nio.ch.EPoll.EPOLL_CTL_MOD;434445/**46* Linux epoll based Selector implementation47*/4849class EPollSelectorImpl extends SelectorImpl {5051// maximum number of events to poll in one call to epoll_wait52private static final int NUM_EPOLLEVENTS = Math.min(IOUtil.fdLimit(), 1024);5354// epoll file descriptor55private final int epfd;5657// address of poll array when polling with epoll_wait58private final long pollArrayAddress;5960// eventfd object used for interrupt61private final EventFD eventfd;6263// maps file descriptor to selection key, synchronize on selector64private final Map<Integer, SelectionKeyImpl> fdToKey = new HashMap<>();6566// pending new registrations/updates, queued by setEventOps67private final Object updateLock = new Object();68private final Deque<SelectionKeyImpl> updateKeys = new ArrayDeque<>();6970// interrupt triggering and clearing71private final Object interruptLock = new Object();72private boolean interruptTriggered;7374EPollSelectorImpl(SelectorProvider sp) throws IOException {75super(sp);7677this.epfd = EPoll.create();78this.pollArrayAddress = EPoll.allocatePollArray(NUM_EPOLLEVENTS);7980try {81this.eventfd = new EventFD();82IOUtil.configureBlocking(IOUtil.newFD(eventfd.efd()), false);83} catch (IOException ioe) {84EPoll.freePollArray(pollArrayAddress);85FileDispatcherImpl.closeIntFD(epfd);86throw ioe;87}8889// register the eventfd object for wakeups90EPoll.ctl(epfd, EPOLL_CTL_ADD, eventfd.efd(), EPOLLIN);91}9293private void ensureOpen() {94if (!isOpen())95throw new ClosedSelectorException();96}9798@Override99protected int doSelect(Consumer<SelectionKey> action, long timeout)100throws IOException101{102assert Thread.holdsLock(this);103104// epoll_wait timeout is int105int to = (int) Math.min(timeout, Integer.MAX_VALUE);106boolean blocking = (to != 0);107boolean timedPoll = (to > 0);108109int numEntries;110processUpdateQueue();111processDeregisterQueue();112try {113begin(blocking);114115do {116long startTime = timedPoll ? System.nanoTime() : 0;117numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, to);118if (numEntries == IOStatus.INTERRUPTED && timedPoll) {119// timed poll interrupted so need to adjust timeout120long adjust = System.nanoTime() - startTime;121to -= TimeUnit.MILLISECONDS.convert(adjust, TimeUnit.NANOSECONDS);122if (to <= 0) {123// timeout expired so no retry124numEntries = 0;125}126}127} while (numEntries == IOStatus.INTERRUPTED);128assert IOStatus.check(numEntries);129130} finally {131end(blocking);132}133processDeregisterQueue();134return processEvents(numEntries, action);135}136137/**138* Process changes to the interest ops.139*/140private void processUpdateQueue() {141assert Thread.holdsLock(this);142143synchronized (updateLock) {144SelectionKeyImpl ski;145while ((ski = updateKeys.pollFirst()) != null) {146if (ski.isValid()) {147int fd = ski.getFDVal();148// add to fdToKey if needed149SelectionKeyImpl previous = fdToKey.putIfAbsent(fd, ski);150assert (previous == null) || (previous == ski);151152int newEvents = ski.translateInterestOps();153int registeredEvents = ski.registeredEvents();154if (newEvents != registeredEvents) {155if (newEvents == 0) {156// remove from epoll157EPoll.ctl(epfd, EPOLL_CTL_DEL, fd, 0);158} else {159if (registeredEvents == 0) {160// add to epoll161EPoll.ctl(epfd, EPOLL_CTL_ADD, fd, newEvents);162} else {163// modify events164EPoll.ctl(epfd, EPOLL_CTL_MOD, fd, newEvents);165}166}167ski.registeredEvents(newEvents);168}169}170}171}172}173174/**175* Process the polled events.176* If the interrupt fd has been selected, drain it and clear the interrupt.177*/178private int processEvents(int numEntries, Consumer<SelectionKey> action)179throws IOException180{181assert Thread.holdsLock(this);182183boolean interrupted = false;184int numKeysUpdated = 0;185for (int i=0; i<numEntries; i++) {186long event = EPoll.getEvent(pollArrayAddress, i);187int fd = EPoll.getDescriptor(event);188if (fd == eventfd.efd()) {189interrupted = true;190} else {191SelectionKeyImpl ski = fdToKey.get(fd);192if (ski != null) {193int rOps = EPoll.getEvents(event);194numKeysUpdated += processReadyEvents(rOps, ski, action);195}196}197}198199if (interrupted) {200clearInterrupt();201}202203return numKeysUpdated;204}205206@Override207protected void implClose() throws IOException {208assert Thread.holdsLock(this);209210// prevent further wakeup211synchronized (interruptLock) {212interruptTriggered = true;213}214215FileDispatcherImpl.closeIntFD(epfd);216EPoll.freePollArray(pollArrayAddress);217218eventfd.close();219}220221@Override222protected void implDereg(SelectionKeyImpl ski) throws IOException {223assert !ski.isValid();224assert Thread.holdsLock(this);225226int fd = ski.getFDVal();227if (fdToKey.remove(fd) != null) {228if (ski.registeredEvents() != 0) {229EPoll.ctl(epfd, EPOLL_CTL_DEL, fd, 0);230ski.registeredEvents(0);231}232} else {233assert ski.registeredEvents() == 0;234}235}236237@Override238public void setEventOps(SelectionKeyImpl ski) {239ensureOpen();240synchronized (updateLock) {241updateKeys.addLast(ski);242}243}244245@Override246public Selector wakeup() {247synchronized (interruptLock) {248if (!interruptTriggered) {249try {250eventfd.set();251} catch (IOException ioe) {252throw new InternalError(ioe);253}254interruptTriggered = true;255}256}257return this;258}259260private void clearInterrupt() throws IOException {261synchronized (interruptLock) {262eventfd.reset();263interruptTriggered = false;264}265}266}267268269