1 /* 2 * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 * KQueueSelectorImpl.java 28 * Implementation of Selector using FreeBSD / Mac OS X kqueues 29 */ 30 31 package sun.nio.ch; 32 33 import java.io.IOException; 34 import java.nio.channels.ClosedSelectorException; 35 import java.nio.channels.SelectableChannel; 36 import java.nio.channels.SelectionKey; 37 import java.nio.channels.Selector; 38 import java.nio.channels.spi.SelectorProvider; 39 import java.util.HashMap; 40 import java.util.Iterator; 41 42 class KQueueSelectorImpl 43 extends SelectorImpl 44 { 45 // File descriptors used for interrupt 46 private final int fd0; 47 private final int fd1; 48 49 // The kqueue manipulator 50 private final KQueueArrayWrapper kqueueWrapper; 51 52 // Map from a file descriptor to an entry containing the selection key 53 private final HashMap<Integer, MapEntry> fdMap; 54 55 // True if this Selector has been closed 56 private boolean closed; 57 58 // Lock for interrupt triggering and clearing 59 private final Object interruptLock = new Object(); 60 private boolean interruptTriggered; 61 62 // used by updateSelectedKeys to handle cases where the same file 63 // descriptor is polled by more than one filter 64 private long updateCount; 65 66 // Used to map file descriptors to a selection key and "update count" 67 // (see updateSelectedKeys for usage). 68 private static class MapEntry { 69 SelectionKeyImpl ski; 70 long updateCount; 71 MapEntry(SelectionKeyImpl ski) { 72 this.ski = ski; 73 } 74 } 75 76 /** 77 * Package private constructor called by factory method in 78 * the abstract superclass Selector. 79 */ 80 KQueueSelectorImpl(SelectorProvider sp) throws IOException { 81 super(sp); 82 long fds = IOUtil.makePipe(false); 83 fd0 = (int)(fds >>> 32); 84 fd1 = (int)fds; 85 try { 86 kqueueWrapper = new KQueueArrayWrapper(fd0, fd1); 87 fdMap = new HashMap<>(); 88 } catch (Throwable t) { 89 try { 90 FileDispatcherImpl.closeIntFD(fd0); 91 } catch (IOException ioe0) { 92 t.addSuppressed(ioe0); 93 } 94 try { 95 FileDispatcherImpl.closeIntFD(fd1); 96 } catch (IOException ioe1) { 97 t.addSuppressed(ioe1); 98 } 99 throw t; 100 } 101 } 102 103 private void ensureOpen() { 104 if (closed) 105 throw new ClosedSelectorException(); 106 } 107 108 @Override 109 protected int doSelect(long timeout) 110 throws IOException 111 { 112 ensureOpen(); 113 int numEntries; 114 processDeregisterQueue(); 115 try { 116 begin(); 117 numEntries = kqueueWrapper.poll(timeout); 118 } finally { 119 end(); 120 } 121 processDeregisterQueue(); 122 return updateSelectedKeys(numEntries); 123 } 124 125 /** 126 * Update the keys whose fd's have been selected by kqueue. 127 * Add the ready keys to the selected key set. 128 * If the interrupt fd has been selected, drain it and clear the interrupt. 129 */ 130 private int updateSelectedKeys(int numEntries) 131 throws IOException 132 { 133 int numKeysUpdated = 0; 134 boolean interrupted = false; 135 136 // A file descriptor may be registered with kqueue with more than one 137 // filter and so there may be more than one event for a fd. The update 138 // count in the MapEntry tracks when the fd was last updated and this 139 // ensures that the ready ops are updated rather than replaced by a 140 // second or subsequent event. 141 updateCount++; 142 143 for (int i = 0; i < numEntries; i++) { 144 int nextFD = kqueueWrapper.getDescriptor(i); 145 if (nextFD == fd0) { 146 interrupted = true; 147 } else { 148 MapEntry me = fdMap.get(Integer.valueOf(nextFD)); 149 if (me != null) { 150 int rOps = kqueueWrapper.getReventOps(i); 151 SelectionKeyImpl ski = me.ski; 152 if (selectedKeys.contains(ski)) { 153 // first time this file descriptor has been encountered on this 154 // update? 155 if (me.updateCount != updateCount) { 156 if (ski.channel.translateAndSetReadyOps(rOps, ski)) { 157 numKeysUpdated++; 158 me.updateCount = updateCount; 159 } 160 } else { 161 // ready ops have already been set on this update 162 ski.channel.translateAndUpdateReadyOps(rOps, ski); 163 } 164 } else { 165 ski.channel.translateAndSetReadyOps(rOps, ski); 166 if ((ski.nioReadyOps() & ski.nioInterestOps()) != 0) { 167 selectedKeys.add(ski); 168 numKeysUpdated++; 169 me.updateCount = updateCount; 170 } 171 } 172 } 173 } 174 } 175 176 if (interrupted) { 177 clearInterrupt(); 178 } 179 return numKeysUpdated; 180 } 181 182 @Override 183 protected void implClose() throws IOException { 184 if (!closed) { 185 closed = true; 186 187 // prevent further wakeup 188 synchronized (interruptLock) { 189 interruptTriggered = true; 190 } 191 192 kqueueWrapper.close(); 193 FileDispatcherImpl.closeIntFD(fd0); 194 FileDispatcherImpl.closeIntFD(fd1); 195 196 // Deregister channels 197 Iterator<SelectionKey> i = keys.iterator(); 198 while (i.hasNext()) { 199 SelectionKeyImpl ski = (SelectionKeyImpl)i.next(); 200 deregister(ski); 201 SelectableChannel selch = ski.channel(); 202 if (!selch.isOpen() && !selch.isRegistered()) 203 ((SelChImpl)selch).kill(); 204 i.remove(); 205 } 206 } 207 } 208 209 @Override 210 protected void implRegister(SelectionKeyImpl ski) { 211 ensureOpen(); 212 int fd = IOUtil.fdVal(ski.channel.getFD()); 213 fdMap.put(Integer.valueOf(fd), new MapEntry(ski)); 214 keys.add(ski); 215 } 216 217 @Override 218 protected void implDereg(SelectionKeyImpl ski) throws IOException { 219 int fd = ski.channel.getFDVal(); 220 fdMap.remove(Integer.valueOf(fd)); 221 kqueueWrapper.release(ski.channel); 222 keys.remove(ski); 223 selectedKeys.remove(ski); 224 deregister(ski); 225 SelectableChannel selch = ski.channel(); 226 if (!selch.isOpen() && !selch.isRegistered()) 227 ((SelChImpl)selch).kill(); 228 } 229 230 @Override 231 public void putEventOps(SelectionKeyImpl ski, int ops) { 232 ensureOpen(); 233 kqueueWrapper.setInterest(ski.channel, ops); 234 } 235 236 @Override 237 public Selector wakeup() { 238 synchronized (interruptLock) { 239 if (!interruptTriggered) { 240 kqueueWrapper.interrupt(); 241 interruptTriggered = true; 242 } 243 } 244 return this; 245 } 246 247 private void clearInterrupt() throws IOException { 248 synchronized (interruptLock) { 249 IOUtil.drain(fd0); 250 interruptTriggered = false; 251 } 252 } 253 }