8200179: (se) More Selector cleanup
authoralanb
Sat, 24 Mar 2018 08:49:55 +0000
changeset 49417 1d3139252c1c
parent 49416 f14852315495
child 49418 5bfe30bb50a2
child 56347 bac3a660249a
8200179: (se) More Selector cleanup Reviewed-by: bpb
make/CompileJavaModules.gmk
make/mapfiles/libnio/mapfile-linux
make/mapfiles/libnio/mapfile-macosx
make/mapfiles/libnio/mapfile-solaris
src/java.base/share/classes/sun/nio/ch/AbstractPollArrayWrapper.java
src/java.base/share/classes/sun/nio/ch/AbstractPollSelectorImpl.java
src/java.base/share/classes/sun/nio/ch/PollSelectorProvider.java
src/java.base/solaris/classes/sun/nio/ch/DevPollArrayWrapper.java
src/java.base/solaris/classes/sun/nio/ch/DevPollSelectorImpl.java
src/java.base/solaris/classes/sun/nio/ch/EventPortSelectorImpl.java
src/java.base/solaris/classes/sun/nio/ch/EventPortWrapper.java
src/java.base/unix/classes/sun/nio/ch/PollArrayWrapper.java
src/java.base/unix/classes/sun/nio/ch/PollSelectorImpl.java
src/java.base/unix/classes/sun/nio/ch/PollSelectorProvider.java
src/java.base/unix/native/libnio/ch/Net.c
src/java.base/unix/native/libnio/ch/PollArrayWrapper.c
src/java.base/unix/native/libnio/ch/PollSelectorImpl.c
src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpChannelImpl.java
src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpMultiChannelImpl.java
src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpServerChannelImpl.java
--- a/make/CompileJavaModules.gmk	Sat Mar 24 14:43:04 2018 +0900
+++ b/make/CompileJavaModules.gmk	Sat Mar 24 08:49:55 2018 +0000
@@ -51,14 +51,6 @@
 # data files and shouldn't go in the product
 java.base_EXCLUDE_FILES += sun/text/resources/BreakIteratorRules.java
 
-ifneq ($(filter solaris macosx linux windows,$(OPENJDK_TARGET_OS)), )
-  java.base_EXCLUDE_FILES += \
-      sun/nio/ch/AbstractPollSelectorImpl.java \
-      sun/nio/ch/PollSelectorImpl.java \
-      sun/nio/ch/PollSelectorProvider.java \
-      #
-endif
-
 ifneq ($(OPENJDK_TARGET_OS), solaris)
   java.base_EXCLUDE_FILES += \
       SolarisLoginModule.java \
--- a/make/mapfiles/libnio/mapfile-linux	Sat Mar 24 14:43:04 2018 +0900
+++ b/make/mapfiles/libnio/mapfile-linux	Sat Mar 24 08:49:55 2018 +0000
@@ -118,8 +118,7 @@
 		Java_sun_nio_ch_Net_pollnvalValue;
 		Java_sun_nio_ch_Net_pollconnValue;
                 Java_sun_nio_ch_Net_isExclusiveBindAvailable;
-                Java_sun_nio_ch_PollArrayWrapper_interrupt;
-                Java_sun_nio_ch_PollArrayWrapper_poll0;
+                Java_sun_nio_ch_PollSelectorImpl_poll;
                 Java_sun_nio_ch_ServerSocketChannelImpl_accept0;
                 Java_sun_nio_ch_ServerSocketChannelImpl_initIDs;
                 Java_sun_nio_ch_SocketChannelImpl_checkConnect;
--- a/make/mapfiles/libnio/mapfile-macosx	Sat Mar 24 14:43:04 2018 +0900
+++ b/make/mapfiles/libnio/mapfile-macosx	Sat Mar 24 08:49:55 2018 +0000
@@ -118,8 +118,7 @@
                 Java_sun_nio_ch_Net_pollnvalValue;
                 Java_sun_nio_ch_Net_pollconnValue;
                 Java_sun_nio_ch_Net_isExclusiveBindAvailable;
-                Java_sun_nio_ch_PollArrayWrapper_interrupt;
-                Java_sun_nio_ch_PollArrayWrapper_poll0;
+                Java_sun_nio_ch_PollSelectorImpl_poll;
                 Java_sun_nio_ch_ServerSocketChannelImpl_accept0;
                 Java_sun_nio_ch_ServerSocketChannelImpl_initIDs;
                 Java_sun_nio_ch_SocketChannelImpl_checkConnect;
--- a/make/mapfiles/libnio/mapfile-solaris	Sat Mar 24 14:43:04 2018 +0900
+++ b/make/mapfiles/libnio/mapfile-solaris	Sat Mar 24 08:49:55 2018 +0000
@@ -116,8 +116,7 @@
 		Java_sun_nio_ch_Net_pollnvalValue;
 		Java_sun_nio_ch_Net_pollconnValue;
                 Java_sun_nio_ch_Net_isExclusiveBindAvailable;
-                Java_sun_nio_ch_PollArrayWrapper_interrupt;
-                Java_sun_nio_ch_PollArrayWrapper_poll0;
+                Java_sun_nio_ch_PollSelectorImpl_poll;
                 Java_sun_nio_ch_ServerSocketChannelImpl_accept0;
                 Java_sun_nio_ch_ServerSocketChannelImpl_initIDs;
                 Java_sun_nio_ch_SocketChannelImpl_checkConnect;
--- a/src/java.base/share/classes/sun/nio/ch/AbstractPollArrayWrapper.java	Sat Mar 24 14:43:04 2018 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-/*
- * 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;
-
-
-/**
- * Manipulates a native array of pollfd structs.
- *
- * @author Mike McCloskey
- * @since 1.4
- */
-
-public abstract class AbstractPollArrayWrapper {
-
-    // Miscellaneous constants
-    static final short SIZE_POLLFD   = 8;
-    static final short FD_OFFSET     = 0;
-    static final short EVENT_OFFSET  = 4;
-    static final short REVENT_OFFSET = 6;
-
-    // The poll fd array
-    protected AllocatedNativeObject pollArray;
-
-    // Number of valid entries in the pollArray
-    protected int totalChannels = 0;
-
-    // Base address of the native pollArray
-    protected long pollArrayAddress;
-
-    // Access methods for fd structures
-    int getEventOps(int i) {
-        int offset = SIZE_POLLFD * i + EVENT_OFFSET;
-        return pollArray.getShort(offset);
-    }
-
-    int getReventOps(int i) {
-        int offset = SIZE_POLLFD * i + REVENT_OFFSET;
-        return pollArray.getShort(offset);
-    }
-
-    int getDescriptor(int i) {
-        int offset = SIZE_POLLFD * i + FD_OFFSET;
-        return pollArray.getInt(offset);
-    }
-
-    void putEventOps(int i, int event) {
-        int offset = SIZE_POLLFD * i + EVENT_OFFSET;
-        pollArray.putShort(offset, (short)event);
-    }
-
-    void putReventOps(int i, int revent) {
-        int offset = SIZE_POLLFD * i + REVENT_OFFSET;
-        pollArray.putShort(offset, (short)revent);
-    }
-
-    void putDescriptor(int i, int fd) {
-        int offset = SIZE_POLLFD * i + FD_OFFSET;
-        pollArray.putInt(offset, fd);
-    }
-
-}
--- a/src/java.base/share/classes/sun/nio/ch/AbstractPollSelectorImpl.java	Sat Mar 24 14:43:04 2018 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,194 +0,0 @@
-/*
- * Copyright (c) 2001, 2018, 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.io.IOException;
-import java.nio.channels.*;
-import java.nio.channels.spi.*;
-import java.util.*;
-
-
-/**
- * An abstract selector impl.
- */
-
-abstract class AbstractPollSelectorImpl
-    extends SelectorImpl
-{
-
-    // The poll fd array
-    PollArrayWrapper pollWrapper;
-
-    // Initial capacity of the pollfd array
-    protected final int INIT_CAP = 10;
-
-    // The list of SelectableChannels serviced by this Selector
-    protected SelectionKeyImpl[] channelArray;
-
-    // In some impls the first entry of channelArray is bogus
-    protected int channelOffset = 0;
-
-    // The number of valid channels in this Selector's poll array
-    protected int totalChannels;
-
-    // True if this Selector has been closed
-    private boolean closed = false;
-
-    // Lock for close and cleanup
-    private Object closeLock = new Object();
-
-    AbstractPollSelectorImpl(SelectorProvider sp, int channels, int offset) {
-        super(sp);
-        this.totalChannels = channels;
-        this.channelOffset = offset;
-    }
-
-    public void putEventOps(SelectionKeyImpl sk, int ops) {
-        synchronized (closeLock) {
-            if (closed)
-                throw new ClosedSelectorException();
-            pollWrapper.putEventOps(sk.getIndex(), ops);
-        }
-    }
-
-    public Selector wakeup() {
-        pollWrapper.interrupt();
-        return this;
-    }
-
-    protected abstract int doSelect(long timeout) throws IOException;
-
-    protected void implClose() throws IOException {
-        synchronized (closeLock) {
-            if (closed)
-                return;
-            closed = true;
-            // Deregister channels
-            for(int i=channelOffset; i<totalChannels; i++) {
-                SelectionKeyImpl ski = channelArray[i];
-                assert(ski.getIndex() != -1);
-                ski.setIndex(-1);
-                deregister(ski);
-                SelectableChannel selch = channelArray[i].channel();
-                if (!selch.isOpen() && !selch.isRegistered())
-                    ((SelChImpl)selch).kill();
-            }
-            implCloseInterrupt();
-            pollWrapper.free();
-            pollWrapper = null;
-            channelArray = null;
-            totalChannels = 0;
-        }
-    }
-
-    protected abstract void implCloseInterrupt() throws IOException;
-
-    /**
-     * Copy the information in the pollfd structs into the opss
-     * of the corresponding Channels. Add the ready keys to the
-     * ready queue.
-     */
-    protected int updateSelectedKeys() {
-        int numKeysUpdated = 0;
-        // Skip zeroth entry; it is for interrupts only
-        for (int i=channelOffset; i<totalChannels; i++) {
-            int rOps = pollWrapper.getReventOps(i);
-            if (rOps != 0) {
-                SelectionKeyImpl sk = channelArray[i];
-                pollWrapper.putReventOps(i, 0);
-                if (selectedKeys.contains(sk)) {
-                    if (sk.channel.translateAndSetReadyOps(rOps, sk)) {
-                        numKeysUpdated++;
-                    }
-                } else {
-                    sk.channel.translateAndSetReadyOps(rOps, sk);
-                    if ((sk.nioReadyOps() & sk.nioInterestOps()) != 0) {
-                        selectedKeys.add(sk);
-                        numKeysUpdated++;
-                    }
-                }
-            }
-        }
-        return numKeysUpdated;
-    }
-
-    protected void implRegister(SelectionKeyImpl ski) {
-        synchronized (closeLock) {
-            if (closed)
-                throw new ClosedSelectorException();
-
-            // Check to see if the array is large enough
-            if (channelArray.length == totalChannels) {
-                // Make a larger array
-                int newSize = pollWrapper.totalChannels * 2;
-                SelectionKeyImpl temp[] = new SelectionKeyImpl[newSize];
-                // Copy over
-                for (int i=channelOffset; i<totalChannels; i++)
-                    temp[i] = channelArray[i];
-                channelArray = temp;
-                // Grow the NativeObject poll array
-                pollWrapper.grow(newSize);
-            }
-            channelArray[totalChannels] = ski;
-            ski.setIndex(totalChannels);
-            pollWrapper.addEntry(ski.channel);
-            totalChannels++;
-            keys.add(ski);
-        }
-    }
-
-    protected void implDereg(SelectionKeyImpl ski) throws IOException {
-        // Algorithm: Copy the sc from the end of the list and put it into
-        // the location of the sc to be removed (since order doesn't
-        // matter). Decrement the sc count. Update the index of the sc
-        // that is moved.
-        int i = ski.getIndex();
-        assert (i >= 0);
-        if (i != totalChannels - 1) {
-            // Copy end one over it
-            SelectionKeyImpl endChannel = channelArray[totalChannels-1];
-            channelArray[i] = endChannel;
-            endChannel.setIndex(i);
-            pollWrapper.release(i);
-            PollArrayWrapper.replaceEntry(pollWrapper, totalChannels - 1,
-                                          pollWrapper, i);
-        } else {
-            pollWrapper.release(i);
-        }
-        // Destroy the last one
-        channelArray[totalChannels-1] = null;
-        totalChannels--;
-        pollWrapper.totalChannels--;
-        ski.setIndex(-1);
-        // Remove the key from keys and selectedKeys
-        keys.remove(ski);
-        selectedKeys.remove(ski);
-        deregister((AbstractSelectionKey)ski);
-        SelectableChannel selch = ski.channel();
-        if (!selch.isOpen() && !selch.isRegistered())
-            ((SelChImpl)selch).kill();
-    }
-}
--- a/src/java.base/share/classes/sun/nio/ch/PollSelectorProvider.java	Sat Mar 24 14:43:04 2018 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2001, 2003, 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.io.IOException;
-import java.nio.channels.*;
-import java.nio.channels.spi.*;
-
-public class PollSelectorProvider
-    extends SelectorProviderImpl
-{
-    public AbstractSelector openSelector() throws IOException {
-        return new PollSelectorImpl(this);
-    }
-
-    public Channel inheritedChannel() throws IOException {
-        return InheritedChannel.getChannel();
-    }
-}
--- a/src/java.base/solaris/classes/sun/nio/ch/DevPollArrayWrapper.java	Sat Mar 24 14:43:04 2018 +0900
+++ b/src/java.base/solaris/classes/sun/nio/ch/DevPollArrayWrapper.java	Sat Mar 24 08:49:55 2018 +0000
@@ -26,12 +26,6 @@
 package sun.nio.ch;
 
 import java.io.IOException;
-import java.security.AccessController;
-import java.util.BitSet;
-import java.util.Map;
-import java.util.HashMap;
-import sun.security.action.GetIntegerAction;
-
 
 /**
  * Manipulates a native array of pollfd structs on Solaris:
@@ -48,43 +42,18 @@
 
 class DevPollArrayWrapper {
 
-    // Event masks
-    static final short POLLIN       = 0x0001;
-    static final short POLLPRI      = 0x0002;
-    static final short POLLOUT      = 0x0004;
-    static final short POLLRDNORM   = 0x0040;
-    static final short POLLWRNORM   = POLLOUT;
-    static final short POLLRDBAND   = 0x0080;
-    static final short POLLWRBAND   = 0x0100;
-    static final short POLLNORM     = POLLRDNORM;
-    static final short POLLERR      = 0x0008;
-    static final short POLLHUP      = 0x0010;
-    static final short POLLNVAL     = 0x0020;
+    // special event to remove a file descriptor from the driver
     static final short POLLREMOVE   = 0x0800;
-    static final short POLLCONN     = POLLOUT;
 
-    // Miscellaneous constants
+    // struct pollfd constants
     static final short SIZE_POLLFD   = 8;
     static final short FD_OFFSET     = 0;
     static final short EVENT_OFFSET  = 4;
     static final short REVENT_OFFSET = 6;
 
-    // Special value to indicate that an update should be ignored
-    static final byte  IGNORE        = (byte)-1;
-
-    // Maximum number of open file descriptors
-    static final int   OPEN_MAX      = IOUtil.fdLimit();
-
-    // Number of pollfd structures to create.
-    // dpwrite/ioctl(DP_POLL) allows up to OPEN_MAX-1
-    static final int   NUM_POLLFDS   = Math.min(OPEN_MAX-1, 8192);
-
-    // Initial size of arrays for fd registration changes
-    private static final int INITIAL_PENDING_UPDATE_SIZE = 64;
-
-    // maximum size of updatesLow
-    private static final int MAX_UPDATE_ARRAY_SIZE = AccessController.doPrivileged(
-        new GetIntegerAction("sun.nio.ch.maxUpdateArraySize", Math.min(OPEN_MAX, 64*1024)));
+    // maximum number of pollfd structure to poll or update at a time
+    // dpwrite/ioctl(DP_POLL) allows up to file descriptor limit minus 1
+    static final int NUM_POLLFDS = Math.min(IOUtil.fdLimit()-1, 1024);
 
     // The pollfd array for results from devpoll driver
     private final AllocatedNativeObject pollArray;
@@ -95,66 +64,29 @@
     // The fd of the devpoll driver
     private int wfd;
 
-    // The fd of the interrupt line going out
-    private int outgoingInterruptFD;
-
-    // The fd of the interrupt line coming in
-    private int incomingInterruptFD;
-
-    // The index of the interrupt FD
-    private int interruptedIndex;
-
-    // Number of updated pollfd entries
-    int updated;
-
-    // object to synchronize fd registration changes
-    private final Object updateLock = new Object();
-
-    // number of file descriptors with registration changes pending
-    private int updateCount;
+    DevPollArrayWrapper() throws IOException {
+        this.wfd = init();
 
-    // file descriptors with registration changes pending
-    private int[] updateDescriptors = new int[INITIAL_PENDING_UPDATE_SIZE];
-
-    // events for file descriptors with registration changes pending, indexed
-    // by file descriptor and stored as bytes for efficiency reasons. For
-    // file descriptors higher than MAX_UPDATE_ARRAY_SIZE (unlimited case at
-    // least then the update is stored in a map.
-    private final byte[] eventsLow = new byte[MAX_UPDATE_ARRAY_SIZE];
-    private Map<Integer,Byte> eventsHigh;
-
-    // Used by release and updateRegistrations to track whether a file
-    // descriptor is registered with /dev/poll.
-    private final BitSet registered = new BitSet();
-
-    DevPollArrayWrapper() throws IOException {
         int allocationSize = NUM_POLLFDS * SIZE_POLLFD;
-        pollArray = new AllocatedNativeObject(allocationSize, true);
-        pollArrayAddress = pollArray.address();
-        wfd = init();
-        if (OPEN_MAX > MAX_UPDATE_ARRAY_SIZE)
-            eventsHigh = new HashMap<>();
+        this.pollArray = new AllocatedNativeObject(allocationSize, true);
+        this.pollArrayAddress = pollArray.address();
     }
 
-    void initInterrupt(int fd0, int fd1) throws IOException {
-        outgoingInterruptFD = fd1;
-        incomingInterruptFD = fd0;
-        register(wfd, fd0, POLLIN);
+    void close() throws IOException {
+        FileDispatcherImpl.closeIntFD(wfd);
+        pollArray.free();
     }
 
-    void putReventOps(int i, int revent) {
-        int offset = SIZE_POLLFD * i + REVENT_OFFSET;
-        pollArray.putShort(offset, (short)revent);
+    void register(int fd, int ops) throws IOException {
+        register(wfd, fd, ops);
     }
 
-    int getEventOps(int i) {
-        int offset = SIZE_POLLFD * i + EVENT_OFFSET;
-        return pollArray.getShort(offset);
+    void registerMultiple(int numfds) throws IOException {
+        registerMultiple(wfd, pollArrayAddress, numfds);
     }
 
-    int getReventOps(int i) {
-        int offset = SIZE_POLLFD * i + REVENT_OFFSET;
-        return pollArray.getShort(offset);
+    int poll(long timeout) throws IOException {
+        return poll0(pollArrayAddress, NUM_POLLFDS, timeout, wfd);
     }
 
     int getDescriptor(int i) {
@@ -162,158 +94,24 @@
         return pollArray.getInt(offset);
     }
 
-    private void setUpdateEvents(int fd, byte events) {
-        if (fd < MAX_UPDATE_ARRAY_SIZE) {
-            eventsLow[fd] = events;
-        } else {
-            eventsHigh.put(Integer.valueOf(fd), Byte.valueOf(events));
-        }
-    }
-
-    private byte getUpdateEvents(int fd) {
-        if (fd < MAX_UPDATE_ARRAY_SIZE) {
-            return eventsLow[fd];
-        } else {
-            Byte result = eventsHigh.get(Integer.valueOf(fd));
-            // result should never be null
-            return result.byteValue();
-        }
-    }
-
-    void setInterest(int fd, int mask) {
-        synchronized (updateLock) {
-            // record the file descriptor and events, expanding the
-            // respective arrays first if necessary.
-            int oldCapacity = updateDescriptors.length;
-            if (updateCount == oldCapacity) {
-                int newCapacity = oldCapacity + INITIAL_PENDING_UPDATE_SIZE;
-                int[] newDescriptors = new int[newCapacity];
-                System.arraycopy(updateDescriptors, 0, newDescriptors, 0, oldCapacity);
-                updateDescriptors = newDescriptors;
-            }
-            updateDescriptors[updateCount++] = fd;
-
-            // events are stored as bytes for efficiency reasons
-            byte b = (byte)mask;
-            assert (b == mask) && (b != IGNORE);
-            setUpdateEvents(fd, b);
-        }
-    }
-
-    void release(int fd) throws IOException {
-        synchronized (updateLock) {
-            // ignore any pending update for this file descriptor
-            setUpdateEvents(fd, IGNORE);
-
-            // remove from /dev/poll
-            if (registered.get(fd)) {
-                register(wfd, fd, POLLREMOVE);
-                registered.clear(fd);
-            }
-        }
-    }
-
-    void close() throws IOException {
-        FileDispatcherImpl.closeIntFD(wfd);
-        pollArray.free();
-    }
-
-    int poll(long timeout) throws IOException {
-        updateRegistrations();
-        updated = poll0(pollArrayAddress, NUM_POLLFDS, timeout, wfd);
-        for (int i=0; i<updated; i++) {
-            if (getDescriptor(i) == incomingInterruptFD) {
-                interruptedIndex = i;
-                interrupted = true;
-                break;
-            }
-        }
-        return updated;
+    short getEventOps(int i) {
+        int offset = SIZE_POLLFD * i + EVENT_OFFSET;
+        return pollArray.getShort(offset);
     }
 
-    void updateRegistrations() throws IOException {
-        synchronized (updateLock) {
-            // Populate pollfd array with updated masks
-            int j = 0;
-            int index = 0;
-            while (j < updateCount) {
-                int fd = updateDescriptors[j];
-                short events = getUpdateEvents(fd);
-                boolean wasRegistered = registered.get(fd);
-
-                // events = 0 => POLLREMOVE or do-nothing
-                if (events != IGNORE) {
-                    if (events == 0) {
-                        if (wasRegistered) {
-                            events = POLLREMOVE;
-                            registered.clear(fd);
-                        } else {
-                            events = IGNORE;
-                        }
-                    } else {
-                        if (!wasRegistered) {
-                            registered.set(fd);
-                        }
-                    }
-                }
-
-                // populate pollfd array with updated event
-                if (events != IGNORE) {
-                    // insert POLLREMOVE if changing events
-                    if (wasRegistered && events != POLLREMOVE) {
-                        putPollFD(pollArray, index, fd, POLLREMOVE);
-                        index++;
-                    }
-                    putPollFD(pollArray, index, fd, events);
-                    index++;
-                    if (index >= (NUM_POLLFDS-1)) {
-                        registerMultiple(wfd, pollArray.address(), index);
-                        index = 0;
-                    }
-
-                    // events for this fd now up to date
-                    setUpdateEvents(fd, IGNORE);
-                }
-                j++;
-            }
-
-            // write any remaining updates
-            if (index > 0)
-                registerMultiple(wfd, pollArray.address(), index);
-
-            updateCount = 0;
-        }
+    short getReventOps(int i) {
+        int offset = SIZE_POLLFD * i + REVENT_OFFSET;
+        return pollArray.getShort(offset);
     }
 
-    private void putPollFD(AllocatedNativeObject array, int index, int fd,
-                           short event)
-    {
+    /**
+     * Updates the pollfd structure at the given index
+     */
+    void putPollFD(int index, int fd, short event) {
         int structIndex = SIZE_POLLFD * index;
-        array.putInt(structIndex + FD_OFFSET, fd);
-        array.putShort(structIndex + EVENT_OFFSET, event);
-        array.putShort(structIndex + REVENT_OFFSET, (short)0);
-    }
-
-    boolean interrupted = false;
-
-    public void interrupt() {
-        try {
-            IOUtil.write1(outgoingInterruptFD, (byte)0);
-        } catch (IOException ioe) {
-            throw new InternalError(ioe);
-        }
-    }
-
-    public int interruptedIndex() {
-        return interruptedIndex;
-    }
-
-    boolean interrupted() {
-        return interrupted;
-    }
-
-    void clearInterrupted() {
-        interrupted = false;
+        pollArray.putInt(structIndex + FD_OFFSET, fd);
+        pollArray.putShort(structIndex + EVENT_OFFSET, event);
+        pollArray.putShort(structIndex + REVENT_OFFSET, (short)0);
     }
 
     private native int init() throws IOException;
--- a/src/java.base/solaris/classes/sun/nio/ch/DevPollSelectorImpl.java	Sat Mar 24 14:43:04 2018 +0900
+++ b/src/java.base/solaris/classes/sun/nio/ch/DevPollSelectorImpl.java	Sat Mar 24 08:49:55 2018 +0000
@@ -26,67 +26,71 @@
 package sun.nio.ch;
 
 import java.io.IOException;
-import java.nio.channels.*;
-import java.nio.channels.spi.*;
-import java.util.*;
+import java.nio.channels.ClosedSelectorException;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.spi.SelectorProvider;
+import java.util.ArrayDeque;
+import java.util.BitSet;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
+import static sun.nio.ch.DevPollArrayWrapper.NUM_POLLFDS;
+import static sun.nio.ch.DevPollArrayWrapper.POLLREMOVE;
 
 /**
- * An implementation of Selector for Solaris.
+ * Solaris /dev/poll based Selector implementation
  */
+
 class DevPollSelectorImpl
     extends SelectorImpl
 {
-    // File descriptors used for interrupt
+    // provides access to /dev/poll driver
+    private final DevPollArrayWrapper pollWrapper;
+
+    // file descriptors used for interrupt
     private final int fd0;
     private final int fd1;
 
-    // The poll object
-    private final DevPollArrayWrapper pollWrapper;
+    // maps file descriptor to selection key, synchronize on selector
+    private final Map<Integer, SelectionKeyImpl> fdToKey = new HashMap<>();
 
-    // Maps from file descriptors to keys
-    private final Map<Integer, SelectionKeyImpl> fdToKey;
+    // file descriptors registered with /dev/poll, synchronize on selector
+    private final BitSet registered = new BitSet();
 
-    // True if this Selector has been closed
-    private boolean closed;
+    // pending new registrations/updates, queued by implRegister and putEventOps
+    private final Object updateLock = new Object();
+    private final Deque<SelectionKeyImpl> newKeys = new ArrayDeque<>();
+    private final Deque<SelectionKeyImpl> updateKeys = new ArrayDeque<>();
+    private final Deque<Integer> updateOps = new ArrayDeque<>();
 
-    // Lock for close/cleanup
-    private final Object closeLock = new Object();
-
-    // Lock for interrupt triggering and clearing
+    // interrupt triggering and clearing
     private final Object interruptLock = new Object();
     private boolean interruptTriggered;
 
-    /**
-     * Package private constructor called by factory method in
-     * the abstract superclass Selector.
-     */
+
     DevPollSelectorImpl(SelectorProvider sp) throws IOException {
         super(sp);
-        long pipeFds = IOUtil.makePipe(false);
-        fd0 = (int) (pipeFds >>> 32);
-        fd1 = (int) pipeFds;
+        this.pollWrapper = new DevPollArrayWrapper();
         try {
-            pollWrapper = new DevPollArrayWrapper();
-            pollWrapper.initInterrupt(fd0, fd1);
-            fdToKey = new HashMap<>();
-        } catch (Throwable t) {
-            try {
-                FileDispatcherImpl.closeIntFD(fd0);
-            } catch (IOException ioe0) {
-                t.addSuppressed(ioe0);
-            }
-            try {
-                FileDispatcherImpl.closeIntFD(fd1);
-            } catch (IOException ioe1) {
-                t.addSuppressed(ioe1);
-            }
-            throw t;
+            long fds = IOUtil.makePipe(false);
+            this.fd0 = (int) (fds >>> 32);
+            this.fd1 = (int) fds;
+        } catch (IOException ioe) {
+            pollWrapper.close();
+            throw ioe;
         }
+
+        // register one end of the socket pair for wakeups
+        pollWrapper.register(fd0, Net.POLLIN);
     }
 
     private void ensureOpen() {
-        if (closed)
+        if (!isOpen())
             throw new ClosedSelectorException();
     }
 
@@ -94,62 +98,123 @@
     protected int doSelect(long timeout)
         throws IOException
     {
-        ensureOpen();
+        assert Thread.holdsLock(this);
+
+        int numEntries;
+        processUpdateQueue();
         processDeregisterQueue();
         try {
             begin();
-            pollWrapper.poll(timeout);
+            numEntries = pollWrapper.poll(timeout);
         } finally {
             end();
         }
         processDeregisterQueue();
-        int numKeysUpdated = updateSelectedKeys();
-        if (pollWrapper.interrupted()) {
-            // Clear the wakeup pipe
-            pollWrapper.putReventOps(pollWrapper.interruptedIndex(), 0);
-            synchronized (interruptLock) {
-                pollWrapper.clearInterrupted();
-                IOUtil.drain(fd0);
-                interruptTriggered = false;
-            }
-        }
-        return numKeysUpdated;
+        return updateSelectedKeys(numEntries);
     }
 
     /**
-     * Update the keys whose fd's have been selected by the devpoll
-     * driver. Add the ready keys to the ready queue.
+     * Process new registrations and changes to the interest ops.
      */
-    private int updateSelectedKeys() {
-        int entries = pollWrapper.updated;
+    private void processUpdateQueue() throws IOException {
+        assert Thread.holdsLock(this);
+
+        synchronized (updateLock) {
+            SelectionKeyImpl ski;
+
+            // new registrations
+            while ((ski = newKeys.pollFirst()) != null) {
+                if (ski.isValid()) {
+                    SelChImpl ch = ski.channel;
+                    int fd = ch.getFDVal();
+                    SelectionKeyImpl previous = fdToKey.put(fd, ski);
+                    assert previous == null;
+                    assert registered.get(fd) == false;
+                }
+            }
+
+            // Translate the queued updates to changes to the set of monitored
+            // file descriptors. The changes are written to the /dev/poll driver
+            // in bulk.
+            assert updateKeys.size() == updateOps.size();
+            int index = 0;
+            while ((ski = updateKeys.pollFirst()) != null) {
+                int ops = updateOps.pollFirst();
+                int fd = ski.channel.getFDVal();
+                if (ski.isValid() && fdToKey.containsKey(fd)) {
+                    if (registered.get(fd)) {
+                        if (ops == 0) {
+                            // remove file descriptor
+                            pollWrapper.putPollFD(index++, fd, POLLREMOVE);
+                            registered.clear(fd);
+                        } else {
+                            // change events
+                            pollWrapper.putPollFD(index++, fd, POLLREMOVE);
+                            pollWrapper.putPollFD(index++, fd, (short)ops);
+                        }
+                    } else if (ops != 0) {
+                        // add file descriptor
+                        pollWrapper.putPollFD(index++, fd, (short)ops);
+                        registered.set(fd);
+                    }
+                    if (index > (NUM_POLLFDS-2)) {
+                        pollWrapper.registerMultiple(index);
+                        index = 0;
+                    }
+                }
+            }
+
+            // write any remaining changes
+            if (index > 0)
+                pollWrapper.registerMultiple(index);
+        }
+    }
+
+    /**
+     * Update the keys whose fd's have been selected by the /dev/poll.
+     * Add the ready keys to the ready queue.
+     */
+    private int updateSelectedKeys(int numEntries) throws IOException {
+        assert Thread.holdsLock(this);
+        assert Thread.holdsLock(nioSelectedKeys());
+
+        boolean interrupted = false;
         int numKeysUpdated = 0;
-        for (int i=0; i<entries; i++) {
-            int nextFD = pollWrapper.getDescriptor(i);
-            SelectionKeyImpl ski = fdToKey.get(Integer.valueOf(nextFD));
-            // ski is null in the case of an interrupt
-            if (ski != null) {
-                int rOps = pollWrapper.getReventOps(i);
-                if (selectedKeys.contains(ski)) {
-                    if (ski.channel.translateAndSetReadyOps(rOps, ski)) {
-                        numKeysUpdated++;
-                    }
-                } else {
-                    ski.channel.translateAndSetReadyOps(rOps, ski);
-                    if ((ski.nioReadyOps() & ski.nioInterestOps()) != 0) {
-                        selectedKeys.add(ski);
-                        numKeysUpdated++;
+        for (int i=0; i<numEntries; i++) {
+            int fd = pollWrapper.getDescriptor(i);
+            if (fd == fd0) {
+                interrupted = true;
+            } else {
+                SelectionKeyImpl ski = fdToKey.get(fd);
+                if (ski != null) {
+                    int rOps = pollWrapper.getReventOps(i);
+                    if (selectedKeys.contains(ski)) {
+                        if (ski.channel.translateAndSetReadyOps(rOps, ski)) {
+                            numKeysUpdated++;
+                        }
+                    } else {
+                        ski.channel.translateAndSetReadyOps(rOps, ski);
+                        if ((ski.nioReadyOps() & ski.nioInterestOps()) != 0) {
+                            selectedKeys.add(ski);
+                            numKeysUpdated++;
+                        }
                     }
                 }
             }
         }
+
+        if (interrupted) {
+            clearInterrupt();
+        }
+
         return numKeysUpdated;
     }
 
     @Override
     protected void implClose() throws IOException {
-        if (closed)
-            return;
-        closed = true;
+        assert !isOpen();
+        assert Thread.holdsLock(this);
+        assert Thread.holdsLock(nioKeys());
 
         // prevent further wakeup
         synchronized (interruptLock) {
@@ -174,42 +239,67 @@
 
     @Override
     protected void implRegister(SelectionKeyImpl ski) {
-        int fd = IOUtil.fdVal(ski.channel.getFD());
-        fdToKey.put(Integer.valueOf(fd), ski);
+        assert Thread.holdsLock(nioKeys());
+        ensureOpen();
+        synchronized (updateLock) {
+            newKeys.addLast(ski);
+        }
         keys.add(ski);
     }
 
     @Override
     protected void implDereg(SelectionKeyImpl ski) throws IOException {
-        int i = ski.getIndex();
-        assert (i >= 0);
+        assert !ski.isValid();
+        assert Thread.holdsLock(this);
+        assert Thread.holdsLock(nioKeys());
+        assert Thread.holdsLock(nioSelectedKeys());
+
         int fd = ski.channel.getFDVal();
-        fdToKey.remove(Integer.valueOf(fd));
-        pollWrapper.release(fd);
-        ski.setIndex(-1);
+        fdToKey.remove(fd);
+        if (registered.get(fd)) {
+            pollWrapper.register(fd, POLLREMOVE);
+            registered.clear(fd);
+        }
+
+        selectedKeys.remove(ski);
         keys.remove(ski);
-        selectedKeys.remove(ski);
-        deregister((AbstractSelectionKey)ski);
+
+        // remove from channel's key set
+        deregister(ski);
+
         SelectableChannel selch = ski.channel();
         if (!selch.isOpen() && !selch.isRegistered())
-            ((SelChImpl)selch).kill();
+            ((SelChImpl) selch).kill();
     }
 
     @Override
-    public void putEventOps(SelectionKeyImpl sk, int ops) {
+    public void putEventOps(SelectionKeyImpl ski, int ops) {
         ensureOpen();
-        int fd = IOUtil.fdVal(sk.channel.getFD());
-        pollWrapper.setInterest(fd, ops);
+        synchronized (updateLock) {
+            updateOps.addLast(ops);   // ops first in case adding the key fails
+            updateKeys.addLast(ski);
+        }
     }
 
     @Override
     public Selector wakeup() {
         synchronized (interruptLock) {
             if (!interruptTriggered) {
-                pollWrapper.interrupt();
+                try {
+                    IOUtil.write1(fd1, (byte)0);
+                } catch (IOException ioe) {
+                    throw new InternalError(ioe);
+                }
                 interruptTriggered = true;
             }
         }
         return this;
     }
+
+    private void clearInterrupt() throws IOException {
+        synchronized (interruptLock) {
+            IOUtil.drain(fd0);
+            interruptTriggered = false;
+        }
+    }
 }
--- a/src/java.base/solaris/classes/sun/nio/ch/EventPortSelectorImpl.java	Sat Mar 24 14:43:04 2018 +0900
+++ b/src/java.base/solaris/classes/sun/nio/ch/EventPortSelectorImpl.java	Sat Mar 24 08:49:55 2018 +0000
@@ -26,11 +26,31 @@
 package sun.nio.ch;
 
 import java.io.IOException;
-import java.nio.channels.*;
-import java.nio.channels.spi.*;
-import java.util.Map;
+import java.nio.channels.ClosedSelectorException;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.spi.SelectorProvider;
+import java.util.ArrayDeque;
+import java.util.Deque;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import jdk.internal.misc.Unsafe;
+
+import static sun.nio.ch.SolarisEventPort.PORT_SOURCE_FD;
+import static sun.nio.ch.SolarisEventPort.PORT_SOURCE_USER;
+import static sun.nio.ch.SolarisEventPort.SIZEOF_PORT_EVENT;
+import static sun.nio.ch.SolarisEventPort.OFFSETOF_EVENTS;
+import static sun.nio.ch.SolarisEventPort.OFFSETOF_SOURCE;
+import static sun.nio.ch.SolarisEventPort.OFFSETOF_OBJECT;
+import static sun.nio.ch.SolarisEventPort.port_create;
+import static sun.nio.ch.SolarisEventPort.port_close;
+import static sun.nio.ch.SolarisEventPort.port_associate;
+import static sun.nio.ch.SolarisEventPort.port_dissociate;
+import static sun.nio.ch.SolarisEventPort.port_getn;
+import static sun.nio.ch.SolarisEventPort.port_send;
 
 /**
  * Selector implementation based on the Solaris event port mechanism.
@@ -39,89 +59,206 @@
 class EventPortSelectorImpl
     extends SelectorImpl
 {
-    private final EventPortWrapper pollWrapper;
+    // maximum number of events to retrive in one call to port_getn
+    static final int MAX_EVENTS = Math.min(IOUtil.fdLimit()-1, 1024);
 
-    // Maps from file descriptors to keys
-    private final Map<Integer, SelectionKeyImpl> fdToKey;
+    // port file descriptor
+    private final int pfd;
+
+    // the poll array (populated by port_getn)
+    private final long pollArrayAddress;
+    private final AllocatedNativeObject pollArray;
 
-    // True if this Selector has been closed
-    private boolean closed;
+    // a registration of a file descriptor with a selector
+    private static class RegEntry {
+        final SelectionKeyImpl ski;
+        int registeredOps;
+        int lastUpdate;
+        RegEntry(SelectionKeyImpl ski) {
+            this.ski = ski;
+        }
+    }
 
-    // Lock for interrupt triggering and clearing
+    // maps a file descriptor to registration entry, synchronize on selector
+    private final Map<Integer, RegEntry> fdToRegEntry = new HashMap<>();
+
+    // the last update operation, incremented by processUpdateQueue
+    private int lastUpdate;
+
+    // pending new registrations/updates, queued by implRegister, putEventOps,
+    // and updateSelectedKeys
+    private final Object updateLock = new Object();
+    private final Deque<SelectionKeyImpl> newKeys = new ArrayDeque<>();
+    private final Deque<SelectionKeyImpl> updateKeys = new ArrayDeque<>();
+    private final Deque<Integer> updateOps = new ArrayDeque<>();
+
+    // interrupt triggering and clearing
     private final Object interruptLock = new Object();
     private boolean interruptTriggered;
 
-    /**
-     * Package private constructor called by factory method in
-     * the abstract superclass Selector.
-     */
     EventPortSelectorImpl(SelectorProvider sp) throws IOException {
         super(sp);
-        pollWrapper = new EventPortWrapper();
-        fdToKey = new HashMap<>();
+
+        this.pfd = port_create();
+
+        int allocationSize = MAX_EVENTS * SIZEOF_PORT_EVENT;
+        this.pollArray = new AllocatedNativeObject(allocationSize, false);
+        this.pollArrayAddress = pollArray.address();
     }
 
     private void ensureOpen() {
-        if (closed)
+        if (!isOpen())
             throw new ClosedSelectorException();
     }
 
     @Override
     protected int doSelect(long timeout) throws IOException {
-        ensureOpen();
+        assert Thread.holdsLock(this);
+
+        int numEvents;
+        processUpdateQueue();
         processDeregisterQueue();
-        int entries;
         try {
             begin();
-            entries = pollWrapper.poll(timeout);
+
+            long to = timeout;
+            boolean timedPoll = (to > 0);
+            do {
+                long startTime = timedPoll ? System.nanoTime() : 0;
+                numEvents = port_getn(pfd, pollArrayAddress, MAX_EVENTS, to);
+                if (numEvents == IOStatus.INTERRUPTED && timedPoll) {
+                    // timed poll interrupted so need to adjust timeout
+                    long adjust = System.nanoTime() - startTime;
+                    to -= TimeUnit.MILLISECONDS.convert(adjust, TimeUnit.NANOSECONDS);
+                    if (to <= 0) {
+                        // timeout also expired so no retry
+                        numEvents = 0;
+                    }
+                }
+            } while (numEvents == IOStatus.INTERRUPTED);
+            assert IOStatus.check(numEvents);
+
         } finally {
             end();
         }
         processDeregisterQueue();
-        int numKeysUpdated = updateSelectedKeys(entries);
-        if (pollWrapper.interrupted()) {
-            synchronized (interruptLock) {
-                interruptTriggered = false;
+        return processPortEvents(numEvents);
+    }
+
+    /**
+     * Process new registrations and changes to the interest ops.
+     */
+    private void processUpdateQueue() throws IOException {
+        assert Thread.holdsLock(this);
+
+        // bump lastUpdate to ensure that the interest ops are changed at most
+        // once per bulk update
+        lastUpdate++;
+
+        synchronized (updateLock) {
+            SelectionKeyImpl ski;
+
+            // new registrations
+            while ((ski = newKeys.pollFirst()) != null) {
+                if (ski.isValid()) {
+                    SelChImpl ch = ski.channel;
+                    int fd = ch.getFDVal();
+                    RegEntry previous = fdToRegEntry.put(fd, new RegEntry(ski));
+                    assert previous == null;
+                }
+            }
+
+            // changes to interest ops
+            assert updateKeys.size() == updateOps.size();
+            while ((ski = updateKeys.pollFirst()) != null) {
+                int ops = updateOps.pollFirst();
+                int fd = ski.channel.getFDVal();
+                RegEntry e = fdToRegEntry.get(fd);
+                if (ski.isValid() && (e != null) && (e.lastUpdate != lastUpdate)) {
+                    assert e.ski == ski;
+                    if ((ops != e.registeredOps)) {
+                        if (ops == 0) {
+                            port_dissociate(pfd, PORT_SOURCE_FD, fd);
+                        } else {
+                            port_associate(pfd, PORT_SOURCE_FD, fd, ops);
+                        }
+                        e.registeredOps = ops;
+                    }
+                    e.lastUpdate = lastUpdate;
+                }
             }
         }
-        return numKeysUpdated;
     }
 
-    private int updateSelectedKeys(int entries) {
+    /**
+     * Process the port events. This method updates the keys of file descriptors
+     * that were polled. It also re-queues the key so that the file descriptor
+     * is re-associated at the next select operation.
+     *
+     * @return the number of selection keys updated.
+     */
+    private int processPortEvents(int numEvents) throws IOException {
+        assert Thread.holdsLock(this);
+        assert Thread.holdsLock(nioSelectedKeys());
+
         int numKeysUpdated = 0;
-        for (int i=0; i<entries; i++) {
-            int nextFD = pollWrapper.getDescriptor(i);
-            SelectionKeyImpl ski = fdToKey.get(Integer.valueOf(nextFD));
-            if (ski != null) {
-                int rOps = pollWrapper.getEventOps(i);
-                if (selectedKeys.contains(ski)) {
-                    if (ski.channel.translateAndSetReadyOps(rOps, ski)) {
-                        numKeysUpdated++;
+        boolean interrupted = false;
+
+        synchronized (updateLock) {
+            for (int i = 0; i < numEvents; i++) {
+                short source = getSource(i);
+                if (source == PORT_SOURCE_FD) {
+                    int fd = getDescriptor(i);
+                    RegEntry e = fdToRegEntry.get(fd);
+                    if (e != null) {
+                        SelectionKeyImpl ski = e.ski;
+                        int rOps = getEventOps(i);
+                        if (selectedKeys.contains(ski)) {
+                            if (ski.channel.translateAndSetReadyOps(rOps, ski)) {
+                                numKeysUpdated++;
+                            }
+                        } else {
+                            ski.channel.translateAndSetReadyOps(rOps, ski);
+                            if ((ski.nioReadyOps() & ski.nioInterestOps()) != 0) {
+                                selectedKeys.add(ski);
+                                numKeysUpdated++;
+                            }
+                        }
+
+                        // queue selection key so that it is re-associated at
+                        // next select. Push to end of deque so that changes to
+                        // the interest ops are processed first
+                        updateKeys.addLast(ski);
+                        updateOps.addLast(e.registeredOps);
+                        e.registeredOps = 0;
                     }
+                } else if (source == PORT_SOURCE_USER) {
+                    interrupted = true;
                 } else {
-                    ski.channel.translateAndSetReadyOps(rOps, ski);
-                    if ((ski.nioReadyOps() & ski.nioInterestOps()) != 0) {
-                        selectedKeys.add(ski);
-                        numKeysUpdated++;
-                    }
+                    assert false;
                 }
             }
         }
+
+        if (interrupted) {
+            clearInterrupt();
+        }
         return numKeysUpdated;
     }
 
     @Override
     protected void implClose() throws IOException {
-        if (closed)
-            return;
-        closed = true;
+        assert !isOpen();
+        assert Thread.holdsLock(this);
+        assert Thread.holdsLock(nioKeys());
 
         // prevent further wakeup
         synchronized (interruptLock) {
             interruptTriggered = true;
         }
 
-        pollWrapper.close();
+        port_close(pfd);
+        pollArray.free();
 
         // Deregister channels
         Iterator<SelectionKey> i = keys.iterator();
@@ -137,42 +274,83 @@
 
     @Override
     protected void implRegister(SelectionKeyImpl ski) {
-        int fd = IOUtil.fdVal(ski.channel.getFD());
-        fdToKey.put(Integer.valueOf(fd), ski);
+        assert Thread.holdsLock(nioKeys());
+        ensureOpen();
+        synchronized (updateLock) {
+            newKeys.addLast(ski);
+        }
         keys.add(ski);
     }
 
     @Override
     protected void implDereg(SelectionKeyImpl ski) throws IOException {
-        int i = ski.getIndex();
-        assert (i >= 0);
+        assert !ski.isValid();
+        assert Thread.holdsLock(this);
+        assert Thread.holdsLock(nioKeys());
+        assert Thread.holdsLock(nioSelectedKeys());
+
         int fd = ski.channel.getFDVal();
-        fdToKey.remove(Integer.valueOf(fd));
-        pollWrapper.release(fd);
-        ski.setIndex(-1);
+        RegEntry e = fdToRegEntry.remove(fd);
+        if (e != null && e.registeredOps != 0) {
+            port_dissociate(pfd, PORT_SOURCE_FD, fd);
+        }
+
+        selectedKeys.remove(ski);
         keys.remove(ski);
-        selectedKeys.remove(ski);
-        deregister((AbstractSelectionKey)ski);
+
+        // remove from channel's key set
+        deregister(ski);
+
         SelectableChannel selch = ski.channel();
         if (!selch.isOpen() && !selch.isRegistered())
-            ((SelChImpl)selch).kill();
+            ((SelChImpl) selch).kill();
     }
 
     @Override
-    public void putEventOps(SelectionKeyImpl sk, int ops) {
+    public void putEventOps(SelectionKeyImpl ski, int ops) {
         ensureOpen();
-        int fd = sk.channel.getFDVal();
-        pollWrapper.setInterest(fd, ops);
+        synchronized (updateLock) {
+            // push to front of deque so that it processed before other
+            // updates for the same key.
+            updateOps.addFirst(ops);
+            updateKeys.addFirst(ski);
+        }
     }
 
     @Override
     public Selector wakeup() {
         synchronized (interruptLock) {
             if (!interruptTriggered) {
-                pollWrapper.interrupt();
+                try {
+                    port_send(pfd, 0);
+                } catch (IOException ioe) {
+                    throw new InternalError(ioe);
+                }
                 interruptTriggered = true;
             }
         }
         return this;
     }
+
+    private void clearInterrupt() throws IOException {
+        synchronized (interruptLock) {
+            interruptTriggered = false;
+        }
+    }
+
+    private short getSource(int i) {
+        int offset = SIZEOF_PORT_EVENT * i + OFFSETOF_SOURCE;
+        return pollArray.getShort(offset);
+    }
+
+    private int getEventOps(int i) {
+        int offset = SIZEOF_PORT_EVENT * i + OFFSETOF_EVENTS;
+        return pollArray.getInt(offset);
+    }
+
+    private int getDescriptor(int i) {
+        //assert Unsafe.getUnsafe().addressSize() == 8;
+        int offset = SIZEOF_PORT_EVENT * i + OFFSETOF_OBJECT;
+        return (int) pollArray.getLong(offset);
+    }
 }
--- a/src/java.base/solaris/classes/sun/nio/ch/EventPortWrapper.java	Sat Mar 24 14:43:04 2018 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,276 +0,0 @@
-/*
- * Copyright (c) 2012, 2018, 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.io.IOException;
-import java.security.AccessController;
-import java.util.BitSet;
-import java.util.HashMap;
-import java.util.Map;
-
-import jdk.internal.misc.Unsafe;
-import sun.security.action.GetIntegerAction;
-import static sun.nio.ch.SolarisEventPort.*;
-
-/**
- * Manages a Solaris event port and manipulates a native array of pollfd structs
- * on Solaris.
- */
-
-class EventPortWrapper {
-    private static final Unsafe unsafe = Unsafe.getUnsafe();
-    private static final int addressSize = unsafe.addressSize();
-
-    // Maximum number of open file descriptors
-    static final int   OPEN_MAX     = IOUtil.fdLimit();
-
-    // Maximum number of events to retrive in one call to port_getn
-    static final int   POLL_MAX     =  Math.min(OPEN_MAX-1, 1024);
-
-    // initial size of the array to hold pending updates
-    private final int INITIAL_PENDING_UPDATE_SIZE = 256;
-
-    // maximum size of updateArray
-    private static final int MAX_UPDATE_ARRAY_SIZE = AccessController.doPrivileged(
-        new GetIntegerAction("sun.nio.ch.maxUpdateArraySize", Math.min(OPEN_MAX, 64*1024)));
-
-    // special update status to indicate that it should be ignored
-    private static final byte IGNORE = -1;
-
-    // port file descriptor
-    private final int pfd;
-
-    // the poll array (populated by port_getn)
-    private final long pollArrayAddress;
-    private final AllocatedNativeObject pollArray;
-
-    // required when accessing the update* fields
-    private final Object updateLock = new Object();
-
-    // the number of pending updates
-    private int updateCount;
-
-    // queue of file descriptors with updates pending
-    private int[] updateDescriptors = new int[INITIAL_PENDING_UPDATE_SIZE];
-
-    // events for file descriptors with registration changes pending, indexed
-    // by file descriptor and stored as bytes for efficiency reasons. For
-    // file descriptors higher than MAX_UPDATE_ARRAY_SIZE (unlimited case at
-    // least then the update is stored in a map.
-    private final byte[] eventsLow = new byte[MAX_UPDATE_ARRAY_SIZE];
-    private Map<Integer,Byte> eventsHigh;
-    // Used by release and updateRegistrations to track whether a file
-    // descriptor is registered with /dev/poll.
-    private final BitSet registered = new BitSet();
-
-    // bit set to indicate if a file descriptor has been visited when
-    // processing updates (used to avoid duplicates calls to port_associate)
-    private BitSet visited = new BitSet();
-
-    EventPortWrapper() throws IOException {
-        int allocationSize = POLL_MAX * SIZEOF_PORT_EVENT;
-        pollArray = new AllocatedNativeObject(allocationSize, true);
-        pollArrayAddress = pollArray.address();
-        this.pfd = port_create();
-        if (OPEN_MAX > MAX_UPDATE_ARRAY_SIZE)
-            eventsHigh = new HashMap<>();
-    }
-
-    void close() throws IOException {
-        port_close(pfd);
-        pollArray.free();
-    }
-
-    private short getSource(int i) {
-        int offset = SIZEOF_PORT_EVENT * i + OFFSETOF_SOURCE;
-        return pollArray.getShort(offset);
-    }
-
-    int getEventOps(int i) {
-        int offset = SIZEOF_PORT_EVENT * i + OFFSETOF_EVENTS;
-        return pollArray.getInt(offset);
-    }
-
-    int getDescriptor(int i) {
-        int offset = SIZEOF_PORT_EVENT * i + OFFSETOF_OBJECT;
-        if (addressSize == 4) {
-            return pollArray.getInt(offset);
-        } else {
-            return (int) pollArray.getLong(offset);
-        }
-    }
-
-    private void setDescriptor(int i, int fd) {
-        int offset = SIZEOF_PORT_EVENT * i + OFFSETOF_OBJECT;
-        if (addressSize == 4) {
-            pollArray.putInt(offset, fd);
-        } else {
-            pollArray.putLong(offset, fd);
-        }
-    }
-
-    private void setUpdate(int fd, byte events) {
-        if (fd < MAX_UPDATE_ARRAY_SIZE) {
-            eventsLow[fd] = events;
-        } else {
-            eventsHigh.put(Integer.valueOf(fd), Byte.valueOf(events));
-        }
-    }
-
-    private byte getUpdate(int fd) {
-        if (fd < MAX_UPDATE_ARRAY_SIZE) {
-            return eventsLow[fd];
-        } else {
-            Byte result = eventsHigh.get(Integer.valueOf(fd));
-            // result should never be null
-            return result.byteValue();
-        }
-    }
-
-    int poll(long timeout) throws IOException {
-        // update registrations prior to poll
-        synchronized (updateLock) {
-
-            // process newest updates first
-            int i = updateCount - 1;
-            while (i >= 0) {
-                int fd = updateDescriptors[i];
-                if (!visited.get(fd)) {
-                    short ev = getUpdate(fd);
-                    if (ev != IGNORE) {
-                        if (ev == 0) {
-                            if (registered.get(fd)) {
-                                port_dissociate(pfd, PORT_SOURCE_FD, (long)fd);
-                                registered.clear(fd);
-                            }
-                        } else {
-                            if (port_associate(pfd, PORT_SOURCE_FD, (long)fd, ev)) {
-                                registered.set(fd);
-                            }
-                        }
-
-                    }
-                    visited.set(fd);
-                }
-                i--;
-            }
-            updateCount = 0;
-        }
-
-        // poll for events
-        int numEntries;
-        long to = timeout;
-        boolean timedPoll = (to > 0);
-        do {
-            long startTime = timedPoll ? System.currentTimeMillis() : 0;
-            numEntries = port_getn(pfd, pollArrayAddress, POLL_MAX, timeout);
-            if (numEntries == IOStatus.INTERRUPTED && timedPoll) {
-                // timed poll interrupted so need to adjust timeout
-                to -= System.currentTimeMillis() - startTime;
-                if (to <= 0) {
-                    // timeout also expired so no retry
-                    numEntries = 0;
-                }
-            }
-        } while (numEntries == IOStatus.INTERRUPTED);
-
-        // after polling we need to queue all polled file descriptors as they
-        // are candidates to register for the next poll.
-        synchronized (updateLock) {
-            for (int i=0; i<numEntries; i++) {
-                if (getSource(i) == PORT_SOURCE_USER) {
-                    interrupted = true;
-                    setDescriptor(i, -1);
-                } else {
-                    // the default is to re-associate for the next poll
-                    int fd = getDescriptor(i);
-                    registered.clear(fd);
-                    setInterest(fd);
-                }
-            }
-        }
-
-        return numEntries;
-    }
-
-    private void setInterest(int fd) {
-        assert Thread.holdsLock(updateLock);
-
-        // record the file descriptor and events, expanding the
-        // respective arrays first if necessary.
-        int oldCapacity = updateDescriptors.length;
-        if (updateCount >= oldCapacity) {
-            int newCapacity = oldCapacity + INITIAL_PENDING_UPDATE_SIZE;
-            int[] newDescriptors = new int[newCapacity];
-            System.arraycopy(updateDescriptors, 0, newDescriptors, 0, oldCapacity);
-            updateDescriptors = newDescriptors;
-        }
-        updateDescriptors[updateCount++] = fd;
-        visited.clear(fd);
-    }
-
-    void setInterest(int fd, int mask) {
-        synchronized (updateLock) {
-            setInterest(fd);
-            setUpdate(fd, (byte)mask);
-            assert getUpdate(fd) == mask;
-        }
-    }
-
-    void release(int fd) {
-        synchronized (updateLock) {
-            if (registered.get(fd)) {
-                try {
-                    port_dissociate(pfd, PORT_SOURCE_FD, (long)fd);
-                } catch (IOException ioe) {
-                    throw new InternalError(ioe);
-                }
-                registered.clear(fd);
-            }
-            setUpdate(fd, IGNORE);
-        }
-    }
-
-    // -- wakeup support --
-
-    private boolean interrupted;
-
-    public void interrupt() {
-        try {
-            port_send(pfd, 0);
-        } catch (IOException ioe) {
-            throw new InternalError(ioe);
-        }
-    }
-
-    boolean interrupted() {
-        return interrupted;
-    }
-
-    void clearInterrupted() {
-        interrupted = false;
-    }
-}
--- a/src/java.base/unix/classes/sun/nio/ch/PollArrayWrapper.java	Sat Mar 24 14:43:04 2018 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,128 +0,0 @@
-/*
- * Copyright (c) 2001, 2013, 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;
-
-
-/**
- * Manipulates a native array of pollfd structs on Solaris:
- *
- * typedef struct pollfd {
- *    int fd;
- *    short events;
- *    short revents;
- * } pollfd_t;
- *
- * @author Mike McCloskey
- * @since 1.4
- */
-
-public class PollArrayWrapper extends AbstractPollArrayWrapper {
-
-    // File descriptor to write for interrupt
-    int interruptFD;
-
-    PollArrayWrapper(int newSize) {
-        newSize = (newSize + 1) * SIZE_POLLFD;
-        pollArray = new AllocatedNativeObject(newSize, false);
-        pollArrayAddress = pollArray.address();
-        totalChannels = 1;
-    }
-
-    void initInterrupt(int fd0, int fd1) {
-        interruptFD = fd1;
-        putDescriptor(0, fd0);
-        putEventOps(0, Net.POLLIN);
-        putReventOps(0, 0);
-    }
-
-    void release(int i) {
-        return;
-    }
-
-    void free() {
-        pollArray.free();
-    }
-
-    /**
-     * Prepare another pollfd struct for use.
-     */
-    void addEntry(SelChImpl sc) {
-        putDescriptor(totalChannels, IOUtil.fdVal(sc.getFD()));
-        putEventOps(totalChannels, 0);
-        putReventOps(totalChannels, 0);
-        totalChannels++;
-    }
-
-    /**
-     * Writes the pollfd entry from the source wrapper at the source index
-     * over the entry in the target wrapper at the target index. The source
-     * array remains unchanged unless the source array and the target are
-     * the same array.
-     */
-    static void replaceEntry(PollArrayWrapper source, int sindex,
-                      PollArrayWrapper target, int tindex) {
-        target.putDescriptor(tindex, source.getDescriptor(sindex));
-        target.putEventOps(tindex, source.getEventOps(sindex));
-        target.putReventOps(tindex, source.getReventOps(sindex));
-    }
-
-    /**
-     * Grows the pollfd array to a size that will accommodate newSize
-     * pollfd entries. This method does no checking of the newSize
-     * to determine if it is in fact bigger than the old size: it
-     * always reallocates an array of the new size.
-     */
-    void grow(int newSize) {
-        // create new array
-        PollArrayWrapper temp = new PollArrayWrapper(newSize);
-
-        // Copy over existing entries
-        for (int i=0; i<totalChannels; i++)
-            replaceEntry(this, i, temp, i);
-
-        // Swap new array into pollArray field
-        pollArray.free();
-        pollArray = temp.pollArray;
-        pollArrayAddress = pollArray.address();
-    }
-
-    int poll(int numfds, int offset, long timeout) {
-        return poll0(pollArrayAddress + (offset * SIZE_POLLFD),
-                     numfds, timeout);
-    }
-
-    public void interrupt() {
-        interrupt(interruptFD);
-    }
-
-    private native int poll0(long pollAddress, int numfds, long timeout);
-
-    private static native void interrupt(int fd);
-
-    static {
-        IOUtil.load();
-    }
-}
--- a/src/java.base/unix/classes/sun/nio/ch/PollSelectorImpl.java	Sat Mar 24 14:43:04 2018 +0900
+++ b/src/java.base/unix/classes/sun/nio/ch/PollSelectorImpl.java	Sat Mar 24 08:49:55 2018 +0000
@@ -22,104 +22,404 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-
 package sun.nio.ch;
 
 import java.io.IOException;
-import java.nio.channels.*;
-import java.nio.channels.spi.*;
-import java.util.*;
+import java.nio.channels.ClosedSelectorException;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.spi.SelectorProvider;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
 
+import jdk.internal.misc.Unsafe;
 
 /**
- * An implementation of Selector for Solaris.
+ * Selector implementation based on poll
  */
 
-class PollSelectorImpl
-    extends AbstractPollSelectorImpl
-{
+class PollSelectorImpl extends SelectorImpl {
+
+    // initial capacity of poll array
+    private static final int INITIAL_CAPACITY = 16;
 
-    // File descriptors used for interrupt
-    private int fd0;
-    private int fd1;
+    // poll array, grows as needed
+    private int pollArrayCapacity = INITIAL_CAPACITY;
+    private int pollArraySize;
+    private AllocatedNativeObject pollArray;
 
-    // Lock for interrupt triggering and clearing
-    private Object interruptLock = new Object();
-    private boolean interruptTriggered = false;
+    // file descriptors used for interrupt
+    private final int fd0;
+    private final int fd1;
+
+    // keys for file descriptors in poll array, synchronize on selector
+    private final List<SelectionKeyImpl> pollKeys = new ArrayList<>();
 
-    /**
-     * Package private constructor called by factory method in
-     * the abstract superclass Selector.
-     */
+    // pending updates, queued by putEventOps
+    private final Object updateLock = new Object();
+    private final Deque<SelectionKeyImpl> updateKeys = new ArrayDeque<>();
+    private final Deque<Integer> updateOps = new ArrayDeque<>();
+
+    // interrupt triggering and clearing
+    private final Object interruptLock = new Object();
+    private boolean interruptTriggered;
+
     PollSelectorImpl(SelectorProvider sp) throws IOException {
-        super(sp, 1, 1);
-        long pipeFds = IOUtil.makePipe(false);
-        fd0 = (int) (pipeFds >>> 32);
-        fd1 = (int) pipeFds;
+        super(sp);
+
+        int size = pollArrayCapacity * SIZE_POLLFD;
+        this.pollArray = new AllocatedNativeObject(size, false);
+
         try {
-            pollWrapper = new PollArrayWrapper(INIT_CAP);
-            pollWrapper.initInterrupt(fd0, fd1);
-            channelArray = new SelectionKeyImpl[INIT_CAP];
-        } catch (Throwable t) {
-            try {
-                FileDispatcherImpl.closeIntFD(fd0);
-            } catch (IOException ioe0) {
-                t.addSuppressed(ioe0);
-            }
-            try {
-                FileDispatcherImpl.closeIntFD(fd1);
-            } catch (IOException ioe1) {
-                t.addSuppressed(ioe1);
-            }
-            throw t;
+            long fds = IOUtil.makePipe(false);
+            this.fd0 = (int) (fds >>> 32);
+            this.fd1 = (int) fds;
+        } catch (IOException ioe) {
+            pollArray.free();
+            throw ioe;
+        }
+
+        // wakeup support
+        synchronized (this) {
+            setFirst(fd0, Net.POLLIN);
         }
     }
 
-    protected int doSelect(long timeout)
-        throws IOException
-    {
-        if (channelArray == null)
+    private void ensureOpen() {
+        if (!isOpen())
             throw new ClosedSelectorException();
+    }
+
+    @Override
+    protected int doSelect(long timeout) throws IOException {
+        assert Thread.holdsLock(this);
+
+        processUpdateQueue();
         processDeregisterQueue();
         try {
             begin();
-            pollWrapper.poll(totalChannels, 0, timeout);
+
+            int to = (int) Math.min(timeout, Integer.MAX_VALUE); // max poll timeout
+            boolean timedPoll = (to > 0);
+            int numPolled;
+            do {
+                long startTime = timedPoll ? System.nanoTime() : 0;
+                numPolled = poll(pollArray.address(), pollArraySize, to);
+                if (numPolled == IOStatus.INTERRUPTED && timedPoll) {
+                    // timed poll interrupted so need to adjust timeout
+                    long adjust = System.nanoTime() - startTime;
+                    to -= TimeUnit.MILLISECONDS.convert(adjust, TimeUnit.NANOSECONDS);
+                    if (to <= 0) {
+                        // timeout expired so no retry
+                        numPolled = 0;
+                    }
+                }
+            } while (numPolled == IOStatus.INTERRUPTED);
+            assert numPolled <= pollArraySize;
+
         } finally {
             end();
         }
+
         processDeregisterQueue();
-        int numKeysUpdated = updateSelectedKeys();
-        if (pollWrapper.getReventOps(0) != 0) {
-            // Clear the wakeup pipe
-            pollWrapper.putReventOps(0, 0);
-            synchronized (interruptLock) {
-                IOUtil.drain(fd0);
-                interruptTriggered = false;
+        return updateSelectedKeys();
+    }
+
+    /**
+     * Process changes to the interest ops.
+     */
+    private void processUpdateQueue() {
+        assert Thread.holdsLock(this);
+
+        synchronized (updateLock) {
+            assert updateKeys.size() == updateOps.size();
+
+            SelectionKeyImpl ski;
+            while ((ski = updateKeys.pollFirst()) != null) {
+                int ops = updateOps.pollFirst();
+                if (ski.isValid()) {
+                    int index = ski.getIndex();
+                    assert index >= 0 && index < pollArraySize;
+                    if (index > 0) {
+                        assert pollKeys.get(index) == ski;
+                        if (ops == 0) {
+                            remove(ski);
+                        } else {
+                            update(ski, ops);
+                        }
+                    } else if (ops != 0) {
+                        add(ski, ops);
+                    }
+                }
             }
         }
+    }
+
+    /**
+     * Update the keys whose fd's have been selected by kqueue.
+     * Add the ready keys to the selected key set.
+     * If the interrupt fd has been selected, drain it and clear the interrupt.
+     */
+    private int updateSelectedKeys() throws IOException {
+        assert Thread.holdsLock(this);
+        assert Thread.holdsLock(nioSelectedKeys());
+        assert pollArraySize > 0 && pollArraySize == pollKeys.size();
+
+        int numKeysUpdated = 0;
+        for (int i = 1; i < pollArraySize; i++) {
+            int rOps = getReventOps(i);
+            if (rOps != 0) {
+                SelectionKeyImpl ski = pollKeys.get(i);
+                assert ski.channel.getFDVal() == getDescriptor(i);
+                if (ski.isValid()) {
+                    if (selectedKeys.contains(ski)) {
+                        if (ski.channel.translateAndSetReadyOps(rOps, ski)) {
+                            numKeysUpdated++;
+                        }
+                    } else {
+                        ski.channel.translateAndSetReadyOps(rOps, ski);
+                        if ((ski.nioReadyOps() & ski.nioInterestOps()) != 0) {
+                            selectedKeys.add(ski);
+                            numKeysUpdated++;
+                        }
+                    }
+                }
+            }
+        }
+
+        // check for interrupt
+        if (getReventOps(0) != 0) {
+            assert getDescriptor(0) == fd0;
+            clearInterrupt();
+        }
+
         return numKeysUpdated;
     }
 
-    protected void implCloseInterrupt() throws IOException {
+    @Override
+    protected void implClose() throws IOException {
+        assert !isOpen();
+        assert Thread.holdsLock(this);
+        assert Thread.holdsLock(nioKeys());
+
         // prevent further wakeup
         synchronized (interruptLock) {
             interruptTriggered = true;
         }
+
+        pollArray.free();
         FileDispatcherImpl.closeIntFD(fd0);
         FileDispatcherImpl.closeIntFD(fd1);
-        fd0 = -1;
-        fd1 = -1;
-        pollWrapper.release(0);
+
+        // Deregister channels
+        Iterator<SelectionKey> i = keys.iterator();
+        while (i.hasNext()) {
+            SelectionKeyImpl ski = (SelectionKeyImpl)i.next();
+            ski.setIndex(-1);
+            deregister(ski);
+            SelectableChannel selch = ski.channel();
+            if (!selch.isOpen() && !selch.isRegistered())
+                ((SelChImpl)selch).kill();
+            i.remove();
+        }
+    }
+
+    @Override
+    protected void implRegister(SelectionKeyImpl ski) {
+        assert ski.getIndex() == 0;
+        assert Thread.holdsLock(nioKeys());
+
+        ensureOpen();
+        keys.add(ski);
     }
 
+    @Override
+    protected void implDereg(SelectionKeyImpl ski) throws IOException {
+        assert !ski.isValid();
+        assert Thread.holdsLock(this);
+        assert Thread.holdsLock(nioKeys());
+        assert Thread.holdsLock(nioSelectedKeys());
+
+        // remove from poll array
+        int index = ski.getIndex();
+        if (index > 0) {
+            remove(ski);
+        }
+
+        // remove from selected-key and key set
+        selectedKeys.remove(ski);
+        keys.remove(ski);
+
+        // remove from channel's key set
+        deregister(ski);
+
+        SelectableChannel selch = ski.channel();
+        if (!selch.isOpen() && !selch.isRegistered())
+            ((SelChImpl) selch).kill();
+    }
+
+    @Override
+    public void putEventOps(SelectionKeyImpl ski, int ops) {
+        ensureOpen();
+        synchronized (updateLock) {
+            updateOps.addLast(ops);   // ops first in case adding the key fails
+            updateKeys.addLast(ski);
+        }
+    }
+
+    @Override
     public Selector wakeup() {
         synchronized (interruptLock) {
             if (!interruptTriggered) {
-                pollWrapper.interrupt();
+                try {
+                    IOUtil.write1(fd1, (byte)0);
+                } catch (IOException ioe) {
+                    throw new InternalError(ioe);
+                }
                 interruptTriggered = true;
             }
         }
         return this;
     }
 
+    private void clearInterrupt() throws IOException {
+        synchronized (interruptLock) {
+            IOUtil.drain(fd0);
+            interruptTriggered = false;
+        }
+    }
+
+    /**
+     * Sets the first pollfd enty in the poll array to the given fd
+     */
+    private void setFirst(int fd, int ops) {
+        assert pollArraySize == 0;
+        assert pollKeys.isEmpty();
+
+        putDescriptor(0, fd);
+        putEventOps(0, ops);
+        pollArraySize = 1;
+
+        pollKeys.add(null);  // dummy element
+    }
+
+    /**
+     * Adds a pollfd entry to the poll array, expanding the poll array if needed.
+     */
+    private void add(SelectionKeyImpl ski, int ops) {
+        expandIfNeeded();
+
+        int index = pollArraySize;
+        assert index > 0;
+        putDescriptor(index, ski.channel.getFDVal());
+        putEventOps(index, ops);
+        putReventOps(index, 0);
+        ski.setIndex(index);
+        pollArraySize++;
+
+        pollKeys.add(ski);
+        assert pollKeys.size() == pollArraySize;
+    }
+
+    /**
+     * Update the events of pollfd entry.
+     */
+    private void update(SelectionKeyImpl ski, int ops) {
+        int index = ski.getIndex();
+        assert index > 0 && index < pollArraySize;
+        assert getDescriptor(index) == ski.channel.getFDVal();
+        putEventOps(index, ops);
+    }
+
+    /**
+     * Removes a pollfd entry from the poll array
+     */
+    private void remove(SelectionKeyImpl ski) {
+        int index = ski.getIndex();
+        assert index > 0 && index < pollArraySize;
+        assert getDescriptor(index) == ski.channel.getFDVal();
+
+        // replace pollfd at index with the last pollfd in array
+        int lastIndex = pollArraySize - 1;
+        if (lastIndex != index) {
+            SelectionKeyImpl lastKey = pollKeys.get(lastIndex);
+            assert lastKey.getIndex() == lastIndex;
+            int lastFd = getDescriptor(lastIndex);
+            int lastOps = getEventOps(lastIndex);
+            int lastRevents = getReventOps(lastIndex);
+            assert lastKey.channel.getFDVal() == lastFd;
+            putDescriptor(index, lastFd);
+            putEventOps(index, lastOps);
+            putReventOps(index, lastRevents);
+            pollKeys.set(index, lastKey);
+            lastKey.setIndex(index);
+        }
+        pollKeys.remove(lastIndex);
+        pollArraySize--;
+        assert pollKeys.size() == pollArraySize;
+
+        ski.setIndex(0);
+    }
+
+    /**
+     * Expand poll array if at capacity
+     */
+    private void expandIfNeeded() {
+        if (pollArraySize == pollArrayCapacity) {
+            int oldSize = pollArrayCapacity * SIZE_POLLFD;
+            int newCapacity = pollArrayCapacity + INITIAL_CAPACITY;
+            int newSize = newCapacity * SIZE_POLLFD;
+            AllocatedNativeObject newPollArray = new AllocatedNativeObject(newSize, false);
+            Unsafe.getUnsafe().copyMemory(pollArray.address(), newPollArray.address(), oldSize);
+            pollArray.free();
+            pollArray = newPollArray;
+            pollArrayCapacity = newCapacity;
+        }
+    }
+
+    private static final short SIZE_POLLFD   = 8;
+    private static final short FD_OFFSET     = 0;
+    private static final short EVENT_OFFSET  = 4;
+    private static final short REVENT_OFFSET = 6;
+
+    private void putDescriptor(int i, int fd) {
+        int offset = SIZE_POLLFD * i + FD_OFFSET;
+        pollArray.putInt(offset, fd);
+    }
+
+    private int getDescriptor(int i) {
+        int offset = SIZE_POLLFD * i + FD_OFFSET;
+        return pollArray.getInt(offset);
+    }
+
+    private void putEventOps(int i, int event) {
+        int offset = SIZE_POLLFD * i + EVENT_OFFSET;
+        pollArray.putShort(offset, (short)event);
+    }
+
+    private int getEventOps(int i) {
+        int offset = SIZE_POLLFD * i + EVENT_OFFSET;
+        return pollArray.getShort(offset);
+    }
+
+    private void putReventOps(int i, int revent) {
+        int offset = SIZE_POLLFD * i + REVENT_OFFSET;
+        pollArray.putShort(offset, (short)revent);
+    }
+
+    private int getReventOps(int i) {
+        int offset = SIZE_POLLFD * i + REVENT_OFFSET;
+        return pollArray.getShort(offset);
+    }
+
+    private static native int poll(long pollAddress, int numfds, int timeout);
+
+    static {
+        IOUtil.load();
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/unix/classes/sun/nio/ch/PollSelectorProvider.java	Sat Mar 24 08:49:55 2018 +0000
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2001, 2018, 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.io.IOException;
+import java.nio.channels.Channel;
+import java.nio.channels.spi.AbstractSelector;
+
+public class PollSelectorProvider
+    extends SelectorProviderImpl
+{
+    public AbstractSelector openSelector() throws IOException {
+        return new PollSelectorImpl(this);
+    }
+
+    public Channel inheritedChannel() throws IOException {
+        return InheritedChannel.getChannel();
+    }
+}
--- a/src/java.base/unix/native/libnio/ch/Net.c	Sat Mar 24 14:43:04 2018 +0900
+++ b/src/java.base/unix/native/libnio/ch/Net.c	Sat Mar 24 08:49:55 2018 +0000
@@ -40,7 +40,6 @@
 #include "net_util_md.h"
 #include "nio_util.h"
 #include "nio.h"
-#include "sun_nio_ch_PollArrayWrapper.h"
 
 #ifdef _AIX
 #include <sys/utsname.h>
--- a/src/java.base/unix/native/libnio/ch/PollArrayWrapper.c	Sat Mar 24 14:43:04 2018 +0900
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +0,0 @@
-/*
- * Copyright (c) 2001, 2005, 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.
- */
-
-#include "jni.h"
-#include "jni_util.h"
-#include "jvm.h"
-#include "jlong.h"
-#include "sun_nio_ch_PollArrayWrapper.h"
-#include <poll.h>
-#include <unistd.h>
-#include <sys/time.h>
-
-#define RESTARTABLE(_cmd, _result) do { \
-  do { \
-    _result = _cmd; \
-  } while((_result == -1) && (errno == EINTR)); \
-} while(0)
-
-static int
-ipoll(struct pollfd fds[], unsigned int nfds, int timeout)
-{
-    jlong start, now;
-    int remaining = timeout;
-    struct timeval t;
-    int diff;
-
-    gettimeofday(&t, NULL);
-    start = t.tv_sec * 1000 + t.tv_usec / 1000;
-
-    for (;;) {
-        int res = poll(fds, nfds, remaining);
-        if (res < 0 && errno == EINTR) {
-            if (remaining >= 0) {
-                gettimeofday(&t, NULL);
-                now = t.tv_sec * 1000 + t.tv_usec / 1000;
-                diff = now - start;
-                remaining -= diff;
-                if (diff < 0 || remaining <= 0) {
-                    return 0;
-                }
-                start = now;
-            }
-        } else {
-            return res;
-        }
-    }
-}
-
-JNIEXPORT jint JNICALL
-Java_sun_nio_ch_PollArrayWrapper_poll0(JNIEnv *env, jobject this,
-                                       jlong address, jint numfds,
-                                       jlong timeout)
-{
-    struct pollfd *a;
-    int err = 0;
-
-    a = (struct pollfd *) jlong_to_ptr(address);
-
-    if (timeout <= 0) {           /* Indefinite or no wait */
-        RESTARTABLE (poll(a, numfds, timeout), err);
-    } else {                     /* Bounded wait; bounded restarts */
-        err = ipoll(a, numfds, timeout);
-    }
-
-    if (err < 0) {
-        JNU_ThrowIOExceptionWithLastError(env, "Poll failed");
-    }
-    return (jint)err;
-}
-
-JNIEXPORT void JNICALL
-Java_sun_nio_ch_PollArrayWrapper_interrupt(JNIEnv *env, jobject this, jint fd)
-{
-    int fakebuf[1];
-    fakebuf[0] = 1;
-    if (write(fd, fakebuf, 1) < 0) {
-         JNU_ThrowIOExceptionWithLastError(env,
-                                          "Write to interrupt fd failed");
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/unix/native/libnio/ch/PollSelectorImpl.c	Sat Mar 24 08:49:55 2018 +0000
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2001, 2018, 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.
+ */
+
+#include <poll.h>
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jvm.h"
+#include "jlong.h"
+#include "nio.h"
+#include "sun_nio_ch_PollSelectorImpl.h"
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_PollSelectorImpl_poll(JNIEnv *env, jclass clazz,
+                                      jlong address, jint numfds,
+                                      jint timeout)
+{
+    struct pollfd *a;
+    int res;
+
+    a = (struct pollfd *) jlong_to_ptr(address);
+    res = poll(a, numfds, timeout);
+    if (res < 0) {
+        if (errno == EINTR) {
+            return IOS_INTERRUPTED;
+        } else {
+            JNU_ThrowIOExceptionWithLastError(env, "poll failed");
+            return IOS_THROWN;
+        }
+    }
+    return (jint) res;
+}
+
--- a/src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpChannelImpl.java	Sat Mar 24 14:43:04 2018 +0900
+++ b/src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpChannelImpl.java	Sat Mar 24 08:49:55 2018 +0000
@@ -58,7 +58,6 @@
 import sun.nio.ch.IOUtil;
 import sun.nio.ch.NativeThread;
 import sun.nio.ch.Net;
-import sun.nio.ch.PollArrayWrapper;
 import sun.nio.ch.SelChImpl;
 import sun.nio.ch.SelectionKeyImpl;
 import sun.nio.ch.Util;
--- a/src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpMultiChannelImpl.java	Sat Mar 24 14:43:04 2018 +0900
+++ b/src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpMultiChannelImpl.java	Sat Mar 24 08:49:55 2018 +0000
@@ -58,7 +58,6 @@
 import sun.nio.ch.IOStatus;
 import sun.nio.ch.IOUtil;
 import sun.nio.ch.Net;
-import sun.nio.ch.PollArrayWrapper;
 import sun.nio.ch.SelChImpl;
 import sun.nio.ch.SelectionKeyImpl;
 import sun.nio.ch.Util;
--- a/src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpServerChannelImpl.java	Sat Mar 24 14:43:04 2018 +0900
+++ b/src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpServerChannelImpl.java	Sat Mar 24 08:49:55 2018 +0000
@@ -46,7 +46,6 @@
 import sun.nio.ch.IOStatus;
 import sun.nio.ch.IOUtil;
 import sun.nio.ch.Net;
-import sun.nio.ch.PollArrayWrapper;
 import sun.nio.ch.SelChImpl;
 import sun.nio.ch.SelectionKeyImpl;
 import sun.nio.ch.Util;