1 /* 2 * Copyright (c) 2011, 2015, 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.io.FileDescriptor; 36 import java.nio.channels.*; 37 import java.nio.channels.spi.*; 38 import java.util.*; 39 40 class KQueueSelectorImpl 41 extends SelectorImpl 42 { 43 // File descriptors used for interrupt 44 protected int fd0; 45 protected int fd1; 46 47 // The kqueue manipulator 48 KQueueArrayWrapper kqueueWrapper; 49 50 // Count of registered descriptors (including interrupt) 51 private int totalChannels; 52 53 // Map from a file descriptor to an entry containing the selection key 54 private HashMap<Integer,MapEntry> fdMap; 55 56 // True if this Selector has been closed 57 private boolean closed = false; 58 59 // Lock for interrupt triggering and clearing 60 private Object interruptLock = new Object(); 61 private boolean interruptTriggered = false; 62 63 // used by updateSelectedKeys to handle cases where the same file 64 // descriptor is polled by more than one filter 65 private long updateCount; 66 67 // Used to map file descriptors to a selection key and "update count" 68 // (see updateSelectedKeys for usage). 69 private static class MapEntry { 70 SelectionKeyImpl ski; 71 long updateCount; 72 MapEntry(SelectionKeyImpl ski) { 73 this.ski = ski; 74 } 75 } 76 77 /** 78 * Package private constructor called by factory method in 79 * the abstract superclass Selector. 80 */ 81 KQueueSelectorImpl(SelectorProvider sp) { 82 super(sp); 83 long fds = IOUtil.makePipe(false); 84 fd0 = (int)(fds >>> 32); 85 fd1 = (int)fds; 86 try { 87 kqueueWrapper = new KQueueArrayWrapper(); 88 kqueueWrapper.initInterrupt(fd0, fd1); 89 fdMap = new HashMap<>(); 90 totalChannels = 1; 91 } catch (Throwable t) { 92 try { 93 FileDispatcherImpl.closeIntFD(fd0); 94 } catch (IOException ioe0) { 95 t.addSuppressed(ioe0); 96 } 97 try { 98 FileDispatcherImpl.closeIntFD(fd1); 99 } catch (IOException ioe1) { 100 t.addSuppressed(ioe1); 101 } 102 throw t; 103 } 104 } 105 106 107 protected int doSelect(long timeout) 108 throws IOException 109 { 110 int entries = 0; 111 if (closed) 112 throw new ClosedSelectorException(); 113 processDeregisterQueue(); 114 try { 115 begin(); 116 entries = kqueueWrapper.poll(timeout); 117 } finally { 118 end(); 119 } 120 processDeregisterQueue(); 121 return updateSelectedKeys(entries); 122 } 123 124 /** 125 * Update the keys whose fd's have been selected by kqueue. 126 * Add the ready keys to the selected key set. 127 * If the interrupt fd has been selected, drain it and clear the interrupt. 128 */ 129 private int updateSelectedKeys(int entries) 130 throws IOException 131 { 132 int numKeysUpdated = 0; 133 boolean interrupted = false; 134 135 // A file descriptor may be registered with kqueue with more than one 136 // filter and so there may be more than one event for a fd. The update 137 // count in the MapEntry tracks when the fd was last updated and this 138 // ensures that the ready ops are updated rather than replaced by a 139 // second or subsequent event. 140 updateCount++; 141 142 for (int i = 0; i < entries; i++) { 143 int nextFD = kqueueWrapper.getDescriptor(i); 144 if (nextFD == fd0) { 145 interrupted = true; 146 } else { 147 MapEntry me = fdMap.get(Integer.valueOf(nextFD)); 148 149 // entry is null in the case of an interrupt 150 if (me != null) { 151 int rOps = kqueueWrapper.getReventOps(i); 152 SelectionKeyImpl ski = me.ski; 153 if (selectedKeys.contains(ski)) { 154 // first time this file descriptor has been encountered on this 155 // update? 156 if (me.updateCount != updateCount) { 157 if (ski.channel.translateAndSetReadyOps(rOps, ski)) { 158 numKeysUpdated++; 159 me.updateCount = updateCount; 160 } 161 } else { 162 // ready ops have already been set on this update 163 ski.channel.translateAndUpdateReadyOps(rOps, ski); 164 } 165 } else { 166 ski.channel.translateAndSetReadyOps(rOps, ski); 167 if ((ski.nioReadyOps() & ski.nioInterestOps()) != 0) { 168 selectedKeys.add(ski); 169 numKeysUpdated++; 170 me.updateCount = updateCount; 171 } 172 } 173 } 174 } 175 } 176 177 if (interrupted) { 178 // Clear the wakeup pipe 179 synchronized (interruptLock) { 180 IOUtil.drain(fd0); 181 interruptTriggered = false; 182 } 183 } 184 return numKeysUpdated; 185 } 186 187 188 protected void implClose() throws IOException { 189 if (!closed) { 190 closed = true; 191 192 // prevent further wakeup 193 synchronized (interruptLock) { 194 interruptTriggered = true; 195 } 196 197 FileDispatcherImpl.closeIntFD(fd0); 198 FileDispatcherImpl.closeIntFD(fd1); 199 if (kqueueWrapper != null) { 200 kqueueWrapper.close(); 201 kqueueWrapper = null; 202 selectedKeys = null; 203 204 // Deregister channels 205 Iterator<SelectionKey> i = keys.iterator(); 206 while (i.hasNext()) { 207 SelectionKeyImpl ski = (SelectionKeyImpl)i.next(); 208 deregister(ski); 209 SelectableChannel selch = ski.channel(); 210 if (!selch.isOpen() && !selch.isRegistered()) 211 ((SelChImpl)selch).kill(); 212 i.remove(); 213 } 214 totalChannels = 0; 215 } 216 fd0 = -1; 217 fd1 = -1; 218 } 219 } 220 221 222 protected void implRegister(SelectionKeyImpl ski) { 223 if (closed) 224 throw new ClosedSelectorException(); 225 int fd = IOUtil.fdVal(ski.channel.getFD()); 226 fdMap.put(Integer.valueOf(fd), new MapEntry(ski)); 227 totalChannels++; 228 keys.add(ski); 229 } 230 231 232 protected void implDereg(SelectionKeyImpl ski) throws IOException { 233 int fd = ski.channel.getFDVal(); 234 fdMap.remove(Integer.valueOf(fd)); 235 kqueueWrapper.release(ski.channel); 236 totalChannels--; 237 keys.remove(ski); 238 selectedKeys.remove(ski); 239 deregister((AbstractSelectionKey)ski); 240 SelectableChannel selch = ski.channel(); 241 if (!selch.isOpen() && !selch.isRegistered()) 242 ((SelChImpl)selch).kill(); 243 } 244 245 246 public void putEventOps(SelectionKeyImpl ski, int ops) { 247 if (closed) 248 throw new ClosedSelectorException(); 249 kqueueWrapper.setInterest(ski.channel, ops); 250 } 251 252 253 public Selector wakeup() { 254 synchronized (interruptLock) { 255 if (!interruptTriggered) { 256 kqueueWrapper.interrupt(); 257 interruptTriggered = true; 258 } 259 } 260 return this; 261 } 262 }