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