1 /* 2 * Copyright (c) 2001, 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 package sun.nio.ch; 27 28 import java.io.IOException; 29 import java.nio.channels.ClosedSelectorException; 30 import java.nio.channels.SelectionKey; 31 import java.nio.channels.Selector; 32 import java.nio.channels.spi.SelectorProvider; 33 import java.util.ArrayDeque; 34 import java.util.Deque; 35 import java.util.HashMap; 36 import java.util.Map; 37 import java.util.concurrent.TimeUnit; 38 import java.util.function.Consumer; 39 40 import static sun.nio.ch.DevPollArrayWrapper.NUM_POLLFDS; 41 import static sun.nio.ch.DevPollArrayWrapper.POLLREMOVE; 42 43 /** 44 * Solaris /dev/poll based Selector implementation 45 */ 46 47 class DevPollSelectorImpl 48 extends SelectorImpl 49 { 50 // provides access to /dev/poll driver 51 private final DevPollArrayWrapper pollWrapper; 52 53 // file descriptors used for interrupt 54 private final int fd0; 55 private final int fd1; 56 57 // maps file descriptor to selection key, synchronize on selector 58 private final Map<Integer, SelectionKeyImpl> fdToKey = new HashMap<>(); 59 60 // pending new registrations/updates, queued by setEventOps 61 private final Object updateLock = new Object(); 62 private final Deque<SelectionKeyImpl> updateKeys = new ArrayDeque<>(); 63 64 // interrupt triggering and clearing 65 private final Object interruptLock = new Object(); 66 private boolean interruptTriggered; 67 68 DevPollSelectorImpl(SelectorProvider sp) throws IOException { 69 super(sp); 70 this.pollWrapper = new DevPollArrayWrapper(); 71 try { 72 long fds = IOUtil.makePipe(false); 73 this.fd0 = (int) (fds >>> 32); 74 this.fd1 = (int) fds; 75 } catch (IOException ioe) { 76 pollWrapper.close(); 77 throw ioe; 78 } 79 80 // register one end of the socket pair for wakeups 81 pollWrapper.register(fd0, Net.POLLIN); 82 } 83 84 private void ensureOpen() { 85 if (!isOpen()) 86 throw new ClosedSelectorException(); 87 } 88 89 @Override 90 protected int doSelect(Consumer<SelectionKey> action, long timeout) 91 throws IOException 92 { 93 assert Thread.holdsLock(this); 94 95 long to = timeout; 96 boolean blocking = (to != 0); 97 boolean timedPoll = (to > 0); 98 99 int numEntries; 100 processUpdateQueue(); 101 processDeregisterQueue(); 102 try { 103 begin(blocking); 104 105 do { 106 long startTime = timedPoll ? System.nanoTime() : 0; 107 numEntries = pollWrapper.poll(to); 108 if (numEntries == IOStatus.INTERRUPTED && timedPoll) { 109 // timed poll interrupted so need to adjust timeout 110 long adjust = System.nanoTime() - startTime; 111 to -= TimeUnit.MILLISECONDS.convert(adjust, TimeUnit.NANOSECONDS); 112 if (to <= 0) { 113 // timeout expired so no retry 114 numEntries = 0; 115 } 116 } 117 } while (numEntries == IOStatus.INTERRUPTED); 118 assert IOStatus.check(numEntries); 119 120 } finally { 121 end(blocking); 122 } 123 processDeregisterQueue(); 124 return processEvents(numEntries, action); 125 } 126 127 /** 128 * Process changes to the interest ops. 129 */ 130 private void processUpdateQueue() throws IOException { 131 assert Thread.holdsLock(this); 132 133 synchronized (updateLock) { 134 SelectionKeyImpl ski; 135 136 // Translate the queued updates to changes to the set of monitored 137 // file descriptors. The changes are written to the /dev/poll driver 138 // in bulk. 139 int index = 0; 140 while ((ski = updateKeys.pollFirst()) != null) { 141 if (ski.isValid()) { 142 int fd = ski.getFDVal(); 143 // add to fdToKey if needed 144 SelectionKeyImpl previous = fdToKey.putIfAbsent(fd, ski); 145 assert (previous == null) || (previous == ski); 146 147 int newEvents = ski.translateInterestOps(); 148 int registeredEvents = ski.registeredEvents(); 149 if (newEvents != registeredEvents) { 150 if (registeredEvents != 0) 151 pollWrapper.putPollFD(index++, fd, POLLREMOVE); 152 if (newEvents != 0) 153 pollWrapper.putPollFD(index++, fd, (short)newEvents); 154 ski.registeredEvents(newEvents); 155 156 // write to /dev/poll 157 if (index > (NUM_POLLFDS-2)) { 158 pollWrapper.registerMultiple(index); 159 index = 0; 160 } 161 } 162 } 163 } 164 165 // write any remaining changes 166 if (index > 0) 167 pollWrapper.registerMultiple(index); 168 } 169 } 170 171 /** 172 * Process the polled events. 173 * If the interrupt fd has been selected, drain it and clear the interrupt. 174 */ 175 private int processEvents(int numEntries, Consumer<SelectionKey> action) 176 throws IOException 177 { 178 assert Thread.holdsLock(this); 179 180 boolean interrupted = false; 181 int numKeysUpdated = 0; 182 for (int i=0; i<numEntries; i++) { 183 int fd = pollWrapper.getDescriptor(i); 184 if (fd == fd0) { 185 interrupted = true; 186 } else { 187 SelectionKeyImpl ski = fdToKey.get(fd); 188 if (ski != null) { 189 int rOps = pollWrapper.getReventOps(i); 190 numKeysUpdated += processReadyEvents(rOps, ski, action); 191 } 192 } 193 } 194 195 if (interrupted) { 196 clearInterrupt(); 197 } 198 199 return numKeysUpdated; 200 } 201 202 @Override 203 protected void implClose() throws IOException { 204 assert !isOpen(); 205 assert Thread.holdsLock(this); 206 207 // prevent further wakeup 208 synchronized (interruptLock) { 209 interruptTriggered = true; 210 } 211 212 pollWrapper.close(); 213 FileDispatcherImpl.closeIntFD(fd0); 214 FileDispatcherImpl.closeIntFD(fd1); 215 } 216 217 218 @Override 219 protected void implDereg(SelectionKeyImpl ski) throws IOException { 220 assert !ski.isValid(); 221 assert Thread.holdsLock(this); 222 223 int fd = ski.getFDVal(); 224 if (fdToKey.remove(fd) != null) { 225 if (ski.registeredEvents() != 0) { 226 pollWrapper.register(fd, POLLREMOVE); 227 ski.registeredEvents(0); 228 } 229 } else { 230 assert ski.registeredEvents() == 0; 231 } 232 } 233 234 @Override 235 public void setEventOps(SelectionKeyImpl ski) { 236 ensureOpen(); 237 synchronized (updateLock) { 238 updateKeys.addLast(ski); 239 } 240 } 241 242 @Override 243 public Selector wakeup() { 244 synchronized (interruptLock) { 245 if (!interruptTriggered) { 246 try { 247 IOUtil.write1(fd1, (byte)0); 248 } catch (IOException ioe) { 249 throw new InternalError(ioe); 250 } 251 interruptTriggered = true; 252 } 253 } 254 return this; 255 } 256 257 private void clearInterrupt() throws IOException { 258 synchronized (interruptLock) { 259 IOUtil.drain(fd0); 260 interruptTriggered = false; 261 } 262 } 263 }