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