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