1 /* 2 * Copyright (c) 2012, 2013, 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.security.AccessController; 30 import java.util.BitSet; 31 import java.util.HashMap; 32 import java.util.Map; 33 34 import jdk.internal.misc.Unsafe; 35 import sun.security.action.GetIntegerAction; 36 import static sun.nio.ch.SolarisEventPort.*; 37 38 /** 39 * Manages a Solaris event port and manipulates a native array of pollfd structs 40 * on Solaris. 41 */ 42 43 class EventPortWrapper { 44 private static final Unsafe unsafe = Unsafe.getUnsafe(); 45 private static final int addressSize = unsafe.addressSize(); 46 47 // Maximum number of open file descriptors 48 static final int OPEN_MAX = IOUtil.fdLimit(); 49 50 // Maximum number of events to retrive in one call to port_getn 51 static final int POLL_MAX = Math.min(OPEN_MAX-1, 1024); 52 53 // initial size of the array to hold pending updates 54 private final int INITIAL_PENDING_UPDATE_SIZE = 256; 55 56 // maximum size of updateArray 57 private static final int MAX_UPDATE_ARRAY_SIZE = AccessController.doPrivileged( 58 new GetIntegerAction("sun.nio.ch.maxUpdateArraySize", Math.min(OPEN_MAX, 64*1024))); 59 60 // special update status to indicate that it should be ignored 61 private static final byte IGNORE = -1; 62 63 // port file descriptor 64 private final int pfd; 65 66 // the poll array (populated by port_getn) 67 private final long pollArrayAddress; 68 private final AllocatedNativeObject pollArray; 69 70 // required when accessing the update* fields 71 private final Object updateLock = new Object(); 72 73 // the number of pending updates 74 private int updateCount; 75 76 // queue of file descriptors with updates pending 77 private int[] updateDescriptors = new int[INITIAL_PENDING_UPDATE_SIZE]; 78 79 // events for file descriptors with registration changes pending, indexed 80 // by file descriptor and stored as bytes for efficiency reasons. For 81 // file descriptors higher than MAX_UPDATE_ARRAY_SIZE (unlimited case at 82 // least then the update is stored in a map. 83 private final byte[] eventsLow = new byte[MAX_UPDATE_ARRAY_SIZE]; 84 private Map<Integer,Byte> eventsHigh; 85 // Used by release and updateRegistrations to track whether a file 86 // descriptor is registered with /dev/poll. 87 private final BitSet registered = new BitSet(); 88 89 // bit set to indicate if a file descriptor has been visited when 90 // processing updates (used to avoid duplicates calls to port_associate) 91 private BitSet visited = new BitSet(); 92 93 EventPortWrapper() throws IOException { 94 int allocationSize = POLL_MAX * SIZEOF_PORT_EVENT; 95 pollArray = new AllocatedNativeObject(allocationSize, true); 96 pollArrayAddress = pollArray.address(); 97 this.pfd = port_create(); 98 if (OPEN_MAX > MAX_UPDATE_ARRAY_SIZE) 99 eventsHigh = new HashMap<>(); 100 } 101 102 void close() throws IOException { 103 port_close(pfd); 104 pollArray.free(); 105 } 106 107 private short getSource(int i) { 108 int offset = SIZEOF_PORT_EVENT * i + OFFSETOF_SOURCE; 109 return pollArray.getShort(offset); 110 } 111 112 int getEventOps(int i) { 113 int offset = SIZEOF_PORT_EVENT * i + OFFSETOF_EVENTS; 114 return pollArray.getInt(offset); 115 } 116 117 int getDescriptor(int i) { 118 int offset = SIZEOF_PORT_EVENT * i + OFFSETOF_OBJECT; 119 if (addressSize == 4) { 120 return pollArray.getInt(offset); 121 } else { 122 return (int) pollArray.getLong(offset); 123 } 124 } 125 126 private void setDescriptor(int i, int fd) { 127 int offset = SIZEOF_PORT_EVENT * i + OFFSETOF_OBJECT; 128 if (addressSize == 4) { 129 pollArray.putInt(offset, fd); 130 } else { 131 pollArray.putLong(offset, fd); 132 } 133 } 134 135 private void setUpdate(int fd, byte events) { 136 if (fd < MAX_UPDATE_ARRAY_SIZE) { 137 eventsLow[fd] = events; 138 } else { 139 eventsHigh.put(Integer.valueOf(fd), Byte.valueOf(events)); 140 } 141 } 142 143 private byte getUpdate(int fd) { 144 if (fd < MAX_UPDATE_ARRAY_SIZE) { 145 return eventsLow[fd]; 146 } else { 147 Byte result = eventsHigh.get(Integer.valueOf(fd)); 148 // result should never be null 149 return result.byteValue(); 150 } 151 } 152 153 int poll(long timeout) throws IOException { 154 // update registrations prior to poll 155 synchronized (updateLock) { 156 157 // process newest updates first 158 int i = updateCount - 1; 159 while (i >= 0) { 160 int fd = updateDescriptors[i]; 161 if (!visited.get(fd)) { 162 short ev = getUpdate(fd); 163 if (ev != IGNORE) { 164 if (ev == 0) { 165 if (registered.get(fd)) { 166 port_dissociate(pfd, PORT_SOURCE_FD, (long)fd); 167 registered.clear(fd); 168 } 169 } else { 170 if (port_associate(pfd, PORT_SOURCE_FD, (long)fd, ev)) { 171 registered.set(fd); 172 } 173 } 174 175 } 176 visited.set(fd); 177 } 178 i--; 179 } 180 updateCount = 0; 181 } 182 183 // poll for events 184 int updated = port_getn(pfd, pollArrayAddress, POLL_MAX, timeout); 185 186 // after polling we need to queue all polled file descriptors as they 187 // are candidates to register for the next poll. 188 synchronized (updateLock) { 189 for (int i=0; i<updated; i++) { 190 if (getSource(i) == PORT_SOURCE_USER) { 191 interrupted = true; 192 setDescriptor(i, -1); 193 } else { 194 // the default is to re-associate for the next poll 195 int fd = getDescriptor(i); 196 registered.clear(fd); 197 setInterest(fd); 198 } 199 } 200 } 201 202 return updated; 203 } 204 205 private void setInterest(int fd) { 206 assert Thread.holdsLock(updateLock); 207 208 // record the file descriptor and events, expanding the 209 // respective arrays first if necessary. 210 int oldCapacity = updateDescriptors.length; 211 if (updateCount >= oldCapacity) { 212 int newCapacity = oldCapacity + INITIAL_PENDING_UPDATE_SIZE; 213 int[] newDescriptors = new int[newCapacity]; 214 System.arraycopy(updateDescriptors, 0, newDescriptors, 0, oldCapacity); 215 updateDescriptors = newDescriptors; 216 } 217 updateDescriptors[updateCount++] = fd; 218 visited.clear(fd); 219 } 220 221 void setInterest(int fd, int mask) { 222 synchronized (updateLock) { 223 setInterest(fd); 224 setUpdate(fd, (byte)mask); 225 assert getUpdate(fd) == mask; 226 } 227 } 228 229 void release(int fd) { 230 synchronized (updateLock) { 231 if (registered.get(fd)) { 232 try { 233 port_dissociate(pfd, PORT_SOURCE_FD, (long)fd); 234 } catch (IOException ioe) { 235 throw new InternalError(ioe); 236 } 237 registered.clear(fd); 238 } 239 setUpdate(fd, IGNORE); 240 } 241 } 242 243 // -- wakeup support -- 244 245 private boolean interrupted; 246 247 public void interrupt() { 248 try { 249 port_send(pfd, 0); 250 } catch (IOException ioe) { 251 throw new InternalError(ioe); 252 } 253 } 254 255 boolean interrupted() { 256 return interrupted; 257 } 258 259 void clearInterrupted() { 260 interrupted = false; 261 } 262 }