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.security.AccessController;
  30 import java.util.BitSet;
  31 import java.util.Map;
  32 import java.util.HashMap;
  33 import sun.security.action.GetIntegerAction;
  34 
  35 
  36 /**
  37  * Manipulates a native array of pollfd structs on Solaris:
  38  *
  39  * typedef struct pollfd {
  40  *    int fd;
  41  *    short events;
  42  *    short revents;
  43  * } pollfd_t;
  44  *
  45  * @author Mike McCloskey
  46  * @since 1.4
  47  */
  48 
  49 class DevPollArrayWrapper {
  50 
  51     // Event masks
  52     static final short POLLIN       = 0x0001;
  53     static final short POLLPRI      = 0x0002;
  54     static final short POLLOUT      = 0x0004;
  55     static final short POLLRDNORM   = 0x0040;
  56     static final short POLLWRNORM   = POLLOUT;
  57     static final short POLLRDBAND   = 0x0080;
  58     static final short POLLWRBAND   = 0x0100;
  59     static final short POLLNORM     = POLLRDNORM;
  60     static final short POLLERR      = 0x0008;
  61     static final short POLLHUP      = 0x0010;
  62     static final short POLLNVAL     = 0x0020;
  63     static final short POLLREMOVE   = 0x0800;
  64     static final short POLLCONN     = POLLOUT;
  65 
  66     // Miscellaneous constants
  67     static final short SIZE_POLLFD   = 8;
  68     static final short FD_OFFSET     = 0;
  69     static final short EVENT_OFFSET  = 4;
  70     static final short REVENT_OFFSET = 6;
  71 
  72     // Special value to indicate that an update should be ignored
  73     static final byte  IGNORE        = (byte)-1;
  74 
  75     // Maximum number of open file descriptors
  76     static final int   OPEN_MAX      = IOUtil.fdLimit();
  77 
  78     // Number of pollfd structures to create.
  79     // dpwrite/ioctl(DP_POLL) allows up to OPEN_MAX-1
  80     static final int   NUM_POLLFDS   = Math.min(OPEN_MAX-1, 8192);
  81 
  82     // Initial size of arrays for fd registration changes
  83     private static final int INITIAL_PENDING_UPDATE_SIZE = 64;
  84 
  85     // maximum size of updatesLow
  86     private static final int MAX_UPDATE_ARRAY_SIZE = AccessController.doPrivileged(
  87         new GetIntegerAction("sun.nio.ch.maxUpdateArraySize", Math.min(OPEN_MAX, 64*1024)));
  88 
  89     // The pollfd array for results from devpoll driver
  90     private final AllocatedNativeObject pollArray;
  91 
  92     // Base address of the native pollArray
  93     private final long pollArrayAddress;
  94 
  95     // The fd of the devpoll driver
  96     private int wfd;
  97 
  98     // The fd of the interrupt line going out
  99     private int outgoingInterruptFD;
 100 
 101     // The fd of the interrupt line coming in
 102     private int incomingInterruptFD;
 103 
 104     // The index of the interrupt FD
 105     private int interruptedIndex;
 106 
 107     // Number of updated pollfd entries
 108     int updated;
 109 
 110     // object to synchronize fd registration changes
 111     private final Object updateLock = new Object();
 112 
 113     // number of file descriptors with registration changes pending
 114     private int updateCount;
 115 
 116     // file descriptors with registration changes pending
 117     private int[] updateDescriptors = new int[INITIAL_PENDING_UPDATE_SIZE];
 118 
 119     // events for file descriptors with registration changes pending, indexed
 120     // by file descriptor and stored as bytes for efficiency reasons. For
 121     // file descriptors higher than MAX_UPDATE_ARRAY_SIZE (unlimited case at
 122     // least then the update is stored in a map.
 123     private final byte[] eventsLow = new byte[MAX_UPDATE_ARRAY_SIZE];
 124     private Map<Integer,Byte> eventsHigh;
 125 
 126     // Used by release and updateRegistrations to track whether a file
 127     // descriptor is registered with /dev/poll.
 128     private final BitSet registered = new BitSet();
 129 
 130     DevPollArrayWrapper() {
 131         int allocationSize = NUM_POLLFDS * SIZE_POLLFD;
 132         pollArray = new AllocatedNativeObject(allocationSize, true);
 133         pollArrayAddress = pollArray.address();
 134         wfd = init();
 135         if (OPEN_MAX > MAX_UPDATE_ARRAY_SIZE)
 136             eventsHigh = new HashMap<>();
 137     }
 138 
 139     void initInterrupt(int fd0, int fd1) {
 140         outgoingInterruptFD = fd1;
 141         incomingInterruptFD = fd0;
 142         register(wfd, fd0, POLLIN);
 143     }
 144 
 145     void putReventOps(int i, int revent) {
 146         int offset = SIZE_POLLFD * i + REVENT_OFFSET;
 147         pollArray.putShort(offset, (short)revent);
 148     }
 149 
 150     int getEventOps(int i) {
 151         int offset = SIZE_POLLFD * i + EVENT_OFFSET;
 152         return pollArray.getShort(offset);
 153     }
 154 
 155     int getReventOps(int i) {
 156         int offset = SIZE_POLLFD * i + REVENT_OFFSET;
 157         return pollArray.getShort(offset);
 158     }
 159 
 160     int getDescriptor(int i) {
 161         int offset = SIZE_POLLFD * i + FD_OFFSET;
 162         return pollArray.getInt(offset);
 163     }
 164 
 165     private void setUpdateEvents(int fd, byte events) {
 166         if (fd < MAX_UPDATE_ARRAY_SIZE) {
 167             eventsLow[fd] = events;
 168         } else {
 169             eventsHigh.put(Integer.valueOf(fd), Byte.valueOf(events));
 170         }
 171     }
 172 
 173     private byte getUpdateEvents(int fd) {
 174         if (fd < MAX_UPDATE_ARRAY_SIZE) {
 175             return eventsLow[fd];
 176         } else {
 177             Byte result = eventsHigh.get(Integer.valueOf(fd));
 178             // result should never be null
 179             return result.byteValue();
 180         }
 181     }
 182 
 183     void setInterest(int fd, int mask) {
 184         synchronized (updateLock) {
 185             // record the file descriptor and events, expanding the
 186             // respective arrays first if necessary.
 187             int oldCapacity = updateDescriptors.length;
 188             if (updateCount == oldCapacity) {
 189                 int newCapacity = oldCapacity + INITIAL_PENDING_UPDATE_SIZE;
 190                 int[] newDescriptors = new int[newCapacity];
 191                 System.arraycopy(updateDescriptors, 0, newDescriptors, 0, oldCapacity);
 192                 updateDescriptors = newDescriptors;
 193             }
 194             updateDescriptors[updateCount++] = fd;
 195 
 196             // events are stored as bytes for efficiency reasons
 197             byte b = (byte)mask;
 198             assert (b == mask) && (b != IGNORE);
 199             setUpdateEvents(fd, b);
 200         }
 201     }
 202 
 203     void release(int fd) {
 204         synchronized (updateLock) {
 205             // ignore any pending update for this file descriptor
 206             setUpdateEvents(fd, IGNORE);
 207 
 208             // remove from /dev/poll
 209             if (registered.get(fd)) {
 210                 register(wfd, fd, POLLREMOVE);
 211                 registered.clear(fd);
 212             }
 213         }
 214     }
 215 
 216     void close() throws IOException {
 217         FileDispatcherImpl.closeIntFD(wfd);
 218         pollArray.free();
 219     }
 220 
 221     int poll(long timeout) throws IOException {
 222         updateRegistrations();
 223         updated = poll0(pollArrayAddress, NUM_POLLFDS, timeout, wfd);
 224         for (int i=0; i<updated; i++) {
 225             if (getDescriptor(i) == incomingInterruptFD) {
 226                 interruptedIndex = i;
 227                 interrupted = true;
 228                 break;
 229             }
 230         }
 231         return updated;
 232     }
 233 
 234     void updateRegistrations() throws IOException {
 235         synchronized (updateLock) {
 236             // Populate pollfd array with updated masks
 237             int j = 0;
 238             int index = 0;
 239             while (j < updateCount) {
 240                 int fd = updateDescriptors[j];
 241                 short events = getUpdateEvents(fd);
 242                 boolean wasRegistered = registered.get(fd);
 243 
 244                 // events = 0 => POLLREMOVE or do-nothing
 245                 if (events != IGNORE) {
 246                     if (events == 0) {
 247                         if (wasRegistered) {
 248                             events = POLLREMOVE;
 249                             registered.clear(fd);
 250                         } else {
 251                             events = IGNORE;
 252                         }
 253                     } else {
 254                         if (!wasRegistered) {
 255                             registered.set(fd);
 256                         }
 257                     }
 258                 }
 259 
 260                 // populate pollfd array with updated event
 261                 if (events != IGNORE) {
 262                     // insert POLLREMOVE if changing events
 263                     if (wasRegistered && events != POLLREMOVE) {
 264                         putPollFD(pollArray, index, fd, POLLREMOVE);
 265                         index++;
 266                     }
 267                     putPollFD(pollArray, index, fd, events);
 268                     index++;
 269                     if (index >= (NUM_POLLFDS-1)) {
 270                         registerMultiple(wfd, pollArray.address(), index);
 271                         index = 0;
 272                     }
 273 
 274                     // events for this fd now up to date
 275                     setUpdateEvents(fd, IGNORE);
 276                 }
 277                 j++;
 278             }
 279 
 280             // write any remaining updates
 281             if (index > 0)
 282                 registerMultiple(wfd, pollArray.address(), index);
 283 
 284             updateCount = 0;
 285         }
 286     }
 287 
 288     private void putPollFD(AllocatedNativeObject array, int index, int fd,
 289                            short event)
 290     {
 291         int structIndex = SIZE_POLLFD * index;
 292         array.putInt(structIndex + FD_OFFSET, fd);
 293         array.putShort(structIndex + EVENT_OFFSET, event);
 294         array.putShort(structIndex + REVENT_OFFSET, (short)0);
 295     }
 296 
 297     boolean interrupted = false;
 298 
 299     public void interrupt() {
 300         interrupt(outgoingInterruptFD);
 301     }
 302 
 303     public int interruptedIndex() {
 304         return interruptedIndex;
 305     }
 306 
 307     boolean interrupted() {
 308         return interrupted;
 309     }
 310 
 311     void clearInterrupted() {
 312         interrupted = false;
 313     }
 314 
 315     private native int init();
 316     private native void register(int wfd, int fd, int mask);
 317     private native void registerMultiple(int wfd, long address, int len)
 318         throws IOException;
 319     private native int poll0(long pollAddress, int numfds, long timeout,
 320                              int wfd);
 321     private static native void interrupt(int fd);
 322 
 323     static {
 324         IOUtil.load();
 325     }
 326 }