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