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 * Derived from Sun's DevPollSelectorImpl 30 */ 31 32 package sun.nio.ch; 33 34 import java.io.IOException; 35 import java.nio.channels.*; 36 import java.nio.channels.spi.*; 37 import java.util.*; 38 39 class KQueueSelectorImpl 40 extends SelectorImpl 41 { 42 // File descriptors used for interrupt 43 protected int fd0; 44 protected int fd1; 45 46 // The kqueue manipulator 47 KQueueArrayWrapper kqueueWrapper; 48 49 // Count of registered descriptors (including interrupt) 50 private int totalChannels; 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 = false; 57 58 // Lock for interrupt triggering and clearing 59 private final Object interruptLock = new Object(); 60 private boolean interruptTriggered = false; 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 final 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) { 81 super(sp); 82 long fds = IOUtil.makePipe(false); 83 fd0 = (int)(fds >>> 32); 84 fd1 = (int)fds; 85 try { 86 kqueueWrapper = new KQueueArrayWrapper(); 87 kqueueWrapper.initInterrupt(fd0, fd1); 88 fdMap = new HashMap<>(); 89 totalChannels = 1; 90 } catch (Throwable t) { 91 try { 92 FileDispatcherImpl.closeIntFD(fd0); 93 } catch (IOException ioe0) { 94 t.addSuppressed(ioe0); 95 } 96 try { 97 FileDispatcherImpl.closeIntFD(fd1); 98 } catch (IOException ioe1) { 99 t.addSuppressed(ioe1); 100 } 101 throw t; 102 } 103 } 104 105 106 protected int doSelect(long timeout) 107 throws IOException 108 { 109 int entries = 0; 110 if (closed) 111 throw new ClosedSelectorException(); 112 processDeregisterQueue(); 113 try { 114 begin(); 115 entries = kqueueWrapper.poll(timeout); 116 } finally { 117 end(); 118 } 119 processDeregisterQueue(); 120 return updateSelectedKeys(entries); 121 } 122 123 /** 124 * Update the keys whose fd's have been selected by kqueue. 125 * Add the ready keys to the selected key set. 126 * If the interrupt fd has been selected, drain it and clear the interrupt. 127 */ 128 private int updateSelectedKeys(int entries) 129 throws IOException 130 { 131 int numKeysUpdated = 0; 132 boolean interrupted = false; 133 134 // A file descriptor may be registered with kqueue with more than one 135 // filter and so there may be more than one event for a fd. The update 136 // count in the MapEntry tracks when the fd was last updated and this 137 // ensures that the ready ops are updated rather than replaced by a 138 // second or subsequent event. 139 updateCount++; 140 141 for (int i = 0; i < entries; i++) { 142 int nextFD = kqueueWrapper.getDescriptor(i); 143 if (nextFD == fd0) { 144 interrupted = true; 145 } else { 146 MapEntry me = fdMap.get(Integer.valueOf(nextFD)); 147 148 // entry is null in the case of an interrupt 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 // Clear the wakeup pipe 178 synchronized (interruptLock) { 179 IOUtil.drain(fd0); 180 interruptTriggered = false; 181 } 182 } 183 return numKeysUpdated; 184 } 185 186 187 protected void implClose() throws IOException { 188 if (!closed) { 189 closed = true; 190 191 // prevent further wakeup 192 synchronized (interruptLock) { 193 interruptTriggered = true; 194 } 195 196 FileDispatcherImpl.closeIntFD(fd0); 197 FileDispatcherImpl.closeIntFD(fd1); 198 if (kqueueWrapper != null) { 199 kqueueWrapper.close(); 200 kqueueWrapper = null; 201 selectedKeys.clear(); 202 203 // Deregister channels 204 Iterator<SelectionKey> i = keys.iterator(); 205 while (i.hasNext()) { 206 SelectionKeyImpl ski = (SelectionKeyImpl)i.next(); 207 deregister(ski); 208 SelectableChannel selch = ski.channel(); 209 if (!selch.isOpen() && !selch.isRegistered()) 210 ((SelChImpl)selch).kill(); 211 i.remove(); 212 } 213 totalChannels = 0; 214 } 215 fd0 = -1; 216 fd1 = -1; 217 } 218 } 219 220 221 protected void implRegister(SelectionKeyImpl ski) { 222 if (closed) 223 throw new ClosedSelectorException(); 224 int fd = IOUtil.fdVal(ski.channel.getFD()); 225 fdMap.put(Integer.valueOf(fd), new MapEntry(ski)); 226 totalChannels++; 227 keys.add(ski); 228 } 229 230 231 protected void implDereg(SelectionKeyImpl ski) throws IOException { 232 int fd = ski.channel.getFDVal(); 233 fdMap.remove(Integer.valueOf(fd)); 234 kqueueWrapper.release(ski.channel); 235 totalChannels--; 236 keys.remove(ski); 237 selectedKeys.remove(ski); 238 deregister(ski); 239 SelectableChannel selch = ski.channel(); 240 if (!selch.isOpen() && !selch.isRegistered()) 241 ((SelChImpl)selch).kill(); 242 } 243 244 245 public void putEventOps(SelectionKeyImpl ski, int ops) { 246 if (closed) 247 throw new ClosedSelectorException(); 248 kqueueWrapper.setInterest(ski.channel, ops); 249 } 250 251 252 public Selector wakeup() { 253 synchronized (interruptLock) { 254 if (!interruptTriggered) { 255 kqueueWrapper.interrupt(); 256 interruptTriggered = true; 257 } 258 } 259 return this; 260 } 261 }