Path: blob/master/src/java.base/macosx/classes/sun/nio/ch/KQueueSelectorImpl.java
41137 views
/*1* Copyright (c) 2011, 2018, 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.KQueue.EVFILT_READ;40import static sun.nio.ch.KQueue.EVFILT_WRITE;41import static sun.nio.ch.KQueue.EV_ADD;42import static sun.nio.ch.KQueue.EV_DELETE;4344/**45* KQueue based Selector implementation for macOS46*/4748class KQueueSelectorImpl extends SelectorImpl {4950// maximum number of events to poll in one call to kqueue51private static final int MAX_KEVENTS = 256;5253// kqueue file descriptor54private final int kqfd;5556// address of poll array (event list) when polling for pending events57private final long pollArrayAddress;5859// file descriptors used for interrupt60private final int fd0;61private final int fd1;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;7374// used by updateSelectedKeys to handle cases where the same file75// descriptor is polled by more than one filter76private int pollCount;7778KQueueSelectorImpl(SelectorProvider sp) throws IOException {79super(sp);8081this.kqfd = KQueue.create();82this.pollArrayAddress = KQueue.allocatePollArray(MAX_KEVENTS);8384try {85long fds = IOUtil.makePipe(false);86this.fd0 = (int) (fds >>> 32);87this.fd1 = (int) fds;88} catch (IOException ioe) {89KQueue.freePollArray(pollArrayAddress);90FileDispatcherImpl.closeIntFD(kqfd);91throw ioe;92}9394// register one end of the socket pair for wakeups95KQueue.register(kqfd, fd0, EVFILT_READ, EV_ADD);96}9798private void ensureOpen() {99if (!isOpen())100throw new ClosedSelectorException();101}102103@Override104protected int doSelect(Consumer<SelectionKey> action, long timeout)105throws IOException106{107assert Thread.holdsLock(this);108109long to = Math.min(timeout, Integer.MAX_VALUE); // max kqueue timeout110boolean blocking = (to != 0);111boolean timedPoll = (to > 0);112113int numEntries;114processUpdateQueue();115processDeregisterQueue();116try {117begin(blocking);118119do {120long startTime = timedPoll ? System.nanoTime() : 0;121numEntries = KQueue.poll(kqfd, pollArrayAddress, MAX_KEVENTS, to);122if (numEntries == IOStatus.INTERRUPTED && timedPoll) {123// timed poll interrupted so need to adjust timeout124long adjust = System.nanoTime() - startTime;125to -= TimeUnit.MILLISECONDS.convert(adjust, TimeUnit.NANOSECONDS);126if (to <= 0) {127// timeout expired so no retry128numEntries = 0;129}130}131} while (numEntries == IOStatus.INTERRUPTED);132assert IOStatus.check(numEntries);133134} finally {135end(blocking);136}137processDeregisterQueue();138return processEvents(numEntries, action);139}140141/**142* Process changes to the interest ops.143*/144private void processUpdateQueue() {145assert Thread.holdsLock(this);146147synchronized (updateLock) {148SelectionKeyImpl ski;149while ((ski = updateKeys.pollFirst()) != null) {150if (ski.isValid()) {151int fd = ski.getFDVal();152// add to fdToKey if needed153SelectionKeyImpl previous = fdToKey.putIfAbsent(fd, ski);154assert (previous == null) || (previous == ski);155156int newEvents = ski.translateInterestOps();157int registeredEvents = ski.registeredEvents();158159// DatagramChannelImpl::disconnect has reset socket160if (ski.getAndClearReset() && registeredEvents != 0) {161KQueue.register(kqfd, fd, EVFILT_READ, EV_DELETE);162registeredEvents = 0;163}164165if (newEvents != registeredEvents) {166167// add or delete interest in read events168if ((registeredEvents & Net.POLLIN) != 0) {169if ((newEvents & Net.POLLIN) == 0) {170KQueue.register(kqfd, fd, EVFILT_READ, EV_DELETE);171}172} else if ((newEvents & Net.POLLIN) != 0) {173KQueue.register(kqfd, fd, EVFILT_READ, EV_ADD);174}175176// add or delete interest in write events177if ((registeredEvents & Net.POLLOUT) != 0) {178if ((newEvents & Net.POLLOUT) == 0) {179KQueue.register(kqfd, fd, EVFILT_WRITE, EV_DELETE);180}181} else if ((newEvents & Net.POLLOUT) != 0) {182KQueue.register(kqfd, fd, EVFILT_WRITE, EV_ADD);183}184185ski.registeredEvents(newEvents);186}187}188}189}190}191192/**193* Process the polled events.194* If the interrupt fd has been selected, drain it and clear the interrupt.195*/196private int processEvents(int numEntries, Consumer<SelectionKey> action)197throws IOException198{199assert Thread.holdsLock(this);200201int numKeysUpdated = 0;202boolean interrupted = false;203204// A file descriptor may be registered with kqueue with more than one205// filter and so there may be more than one event for a fd. The poll206// count is incremented here and compared against the SelectionKey's207// "lastPolled" field. This ensures that the ready ops is updated rather208// than replaced when a file descriptor is polled by both the read and209// write filter.210pollCount++;211212for (int i = 0; i < numEntries; i++) {213long kevent = KQueue.getEvent(pollArrayAddress, i);214int fd = KQueue.getDescriptor(kevent);215if (fd == fd0) {216interrupted = true;217} else {218SelectionKeyImpl ski = fdToKey.get(fd);219if (ski != null) {220int rOps = 0;221short filter = KQueue.getFilter(kevent);222if (filter == EVFILT_READ) {223rOps |= Net.POLLIN;224} else if (filter == EVFILT_WRITE) {225rOps |= Net.POLLOUT;226}227int updated = processReadyEvents(rOps, ski, action);228if (updated > 0 && ski.lastPolled != pollCount) {229numKeysUpdated++;230ski.lastPolled = pollCount;231}232}233}234}235236if (interrupted) {237clearInterrupt();238}239return numKeysUpdated;240}241242@Override243protected void implClose() throws IOException {244assert !isOpen();245assert Thread.holdsLock(this);246247// prevent further wakeup248synchronized (interruptLock) {249interruptTriggered = true;250}251252FileDispatcherImpl.closeIntFD(kqfd);253KQueue.freePollArray(pollArrayAddress);254255FileDispatcherImpl.closeIntFD(fd0);256FileDispatcherImpl.closeIntFD(fd1);257}258259@Override260protected void implDereg(SelectionKeyImpl ski) throws IOException {261assert !ski.isValid();262assert Thread.holdsLock(this);263264int fd = ski.getFDVal();265int registeredEvents = ski.registeredEvents();266if (fdToKey.remove(fd) != null) {267if (registeredEvents != 0) {268if ((registeredEvents & Net.POLLIN) != 0)269KQueue.register(kqfd, fd, EVFILT_READ, EV_DELETE);270if ((registeredEvents & Net.POLLOUT) != 0)271KQueue.register(kqfd, fd, EVFILT_WRITE, EV_DELETE);272ski.registeredEvents(0);273}274} else {275assert registeredEvents == 0;276}277}278279@Override280public void setEventOps(SelectionKeyImpl ski) {281ensureOpen();282synchronized (updateLock) {283updateKeys.addLast(ski);284}285}286287@Override288public Selector wakeup() {289synchronized (interruptLock) {290if (!interruptTriggered) {291try {292IOUtil.write1(fd1, (byte)0);293} catch (IOException ioe) {294throw new InternalError(ioe);295}296interruptTriggered = true;297}298}299return this;300}301302private void clearInterrupt() throws IOException {303synchronized (interruptLock) {304IOUtil.drain(fd0);305interruptTriggered = false;306}307}308}309310311