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