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