Home | History | Annotate | Download | only in ch
      1 /*
      2  * Copyright (c) 2000, 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.FileDescriptor;
     29 import java.io.IOException;
     30 import java.net.*;
     31 import java.nio.channels.*;
     32 import java.nio.channels.spi.*;
     33 import java.util.*;
     34 import sun.net.NetHooks;
     35 
     36 
     37 /**
     38  * An implementation of ServerSocketChannels
     39  */
     40 
     41 class ServerSocketChannelImpl
     42     extends ServerSocketChannel
     43     implements SelChImpl
     44 {
     45 
     46     // Used to make native close and configure calls
     47     private static NativeDispatcher nd;
     48 
     49     // Our file descriptor
     50     private final FileDescriptor fd;
     51 
     52     // fd value needed for dev/poll. This value will remain valid
     53     // even after the value in the file descriptor object has been set to -1
     54     private int fdVal;
     55 
     56     // ID of native thread currently blocked in this channel, for signalling
     57     private volatile long thread = 0;
     58 
     59     // Lock held by thread currently blocked in this channel
     60     private final Object lock = new Object();
     61 
     62     // Lock held by any thread that modifies the state fields declared below
     63     // DO NOT invoke a blocking I/O operation while holding this lock!
     64     private final Object stateLock = new Object();
     65 
     66     // -- The following fields are protected by stateLock
     67 
     68     // Channel state, increases monotonically
     69     private static final int ST_UNINITIALIZED = -1;
     70     private static final int ST_INUSE = 0;
     71     private static final int ST_KILLED = 1;
     72     private int state = ST_UNINITIALIZED;
     73 
     74     // Binding
     75     private InetSocketAddress localAddress; // null => unbound
     76 
     77     // set true when exclusive binding is on and SO_REUSEADDR is emulated
     78     private boolean isReuseAddress;
     79 
     80     // Our socket adaptor, if any
     81     ServerSocket socket;
     82 
     83     // -- End of fields protected by stateLock
     84 
     85 
     86     ServerSocketChannelImpl(SelectorProvider sp) throws IOException {
     87         super(sp);
     88         this.fd =  Net.serverSocket(true);
     89         this.fdVal = IOUtil.fdVal(fd);
     90         this.state = ST_INUSE;
     91     }
     92 
     93     ServerSocketChannelImpl(SelectorProvider sp,
     94                             FileDescriptor fd,
     95                             boolean bound)
     96         throws IOException
     97     {
     98         super(sp);
     99         this.fd =  fd;
    100         this.fdVal = IOUtil.fdVal(fd);
    101         this.state = ST_INUSE;
    102         if (bound)
    103             localAddress = Net.localAddress(fd);
    104     }
    105 
    106     public ServerSocket socket() {
    107         synchronized (stateLock) {
    108             if (socket == null)
    109 
    110                 socket = ServerSocketAdaptor.create(this);
    111             return socket;
    112         }
    113     }
    114 
    115     @Override
    116     public SocketAddress getLocalAddress() throws IOException {
    117         synchronized (stateLock) {
    118             if (!isOpen())
    119                 throw new ClosedChannelException();
    120             return localAddress == null ? localAddress
    121                     : Net.getRevealedLocalAddress(
    122                           Net.asInetSocketAddress(localAddress));
    123         }
    124     }
    125 
    126     @Override
    127     public <T> ServerSocketChannel setOption(SocketOption<T> name, T value)
    128         throws IOException
    129     {
    130         if (name == null)
    131             throw new NullPointerException();
    132         if (!supportedOptions().contains(name))
    133             throw new UnsupportedOperationException("'" + name + "' not supported");
    134         synchronized (stateLock) {
    135             if (!isOpen())
    136                 throw new ClosedChannelException();
    137 
    138             if (name == StandardSocketOptions.IP_TOS) {
    139                 ProtocolFamily family = Net.isIPv6Available() ?
    140                     StandardProtocolFamily.INET6 : StandardProtocolFamily.INET;
    141                 Net.setSocketOption(fd, family, name, value);
    142                 return this;
    143             }
    144 
    145             if (name == StandardSocketOptions.SO_REUSEADDR &&
    146                     Net.useExclusiveBind())
    147             {
    148                 // SO_REUSEADDR emulated when using exclusive bind
    149                 isReuseAddress = (Boolean)value;
    150             } else {
    151                 // no options that require special handling
    152                 Net.setSocketOption(fd, Net.UNSPEC, name, value);
    153             }
    154             return this;
    155         }
    156     }
    157 
    158     @Override
    159     @SuppressWarnings("unchecked")
    160     public <T> T getOption(SocketOption<T> name)
    161         throws IOException
    162     {
    163         if (name == null)
    164             throw new NullPointerException();
    165         if (!supportedOptions().contains(name))
    166             throw new UnsupportedOperationException("'" + name + "' not supported");
    167 
    168         synchronized (stateLock) {
    169             if (!isOpen())
    170                 throw new ClosedChannelException();
    171             if (name == StandardSocketOptions.SO_REUSEADDR &&
    172                     Net.useExclusiveBind())
    173             {
    174                 // SO_REUSEADDR emulated when using exclusive bind
    175                 return (T)Boolean.valueOf(isReuseAddress);
    176             }
    177             // no options that require special handling
    178             return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
    179         }
    180     }
    181 
    182     private static class DefaultOptionsHolder {
    183         static final Set<SocketOption<?>> defaultOptions = defaultOptions();
    184 
    185         private static Set<SocketOption<?>> defaultOptions() {
    186             HashSet<SocketOption<?>> set = new HashSet<SocketOption<?>>(2);
    187             set.add(StandardSocketOptions.SO_RCVBUF);
    188             set.add(StandardSocketOptions.SO_REUSEADDR);
    189             set.add(StandardSocketOptions.IP_TOS);
    190             return Collections.unmodifiableSet(set);
    191         }
    192     }
    193 
    194     @Override
    195     public final Set<SocketOption<?>> supportedOptions() {
    196         return DefaultOptionsHolder.defaultOptions;
    197     }
    198 
    199     public boolean isBound() {
    200         synchronized (stateLock) {
    201             return localAddress != null;
    202         }
    203     }
    204 
    205     public InetSocketAddress localAddress() {
    206         synchronized (stateLock) {
    207             return localAddress;
    208         }
    209     }
    210 
    211     @Override
    212     public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException {
    213         synchronized (lock) {
    214             if (!isOpen())
    215                 throw new ClosedChannelException();
    216             if (isBound())
    217                 throw new AlreadyBoundException();
    218             InetSocketAddress isa = (local == null) ? new InetSocketAddress(0) :
    219                 Net.checkAddress(local);
    220             SecurityManager sm = System.getSecurityManager();
    221             if (sm != null)
    222                 sm.checkListen(isa.getPort());
    223             NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
    224             Net.bind(fd, isa.getAddress(), isa.getPort());
    225             Net.listen(fd, backlog < 1 ? 50 : backlog);
    226             synchronized (stateLock) {
    227                 localAddress = Net.localAddress(fd);
    228             }
    229         }
    230         return this;
    231     }
    232 
    233     public SocketChannel accept() throws IOException {
    234         synchronized (lock) {
    235             if (!isOpen())
    236                 throw new ClosedChannelException();
    237             if (!isBound())
    238                 throw new NotYetBoundException();
    239             SocketChannel sc = null;
    240 
    241             int n = 0;
    242             FileDescriptor newfd = new FileDescriptor();
    243             InetSocketAddress[] isaa = new InetSocketAddress[1];
    244 
    245             try {
    246                 begin();
    247                 if (!isOpen())
    248                     return null;
    249                 thread = NativeThread.current();
    250                 for (;;) {
    251                     n = accept(this.fd, newfd, isaa);
    252                     if ((n == IOStatus.INTERRUPTED) && isOpen())
    253                         continue;
    254                     break;
    255                 }
    256             } finally {
    257                 thread = 0;
    258                 end(n > 0);
    259                 assert IOStatus.check(n);
    260             }
    261 
    262             if (n < 1)
    263                 return null;
    264 
    265             IOUtil.configureBlocking(newfd, true);
    266             InetSocketAddress isa = isaa[0];
    267             sc = new SocketChannelImpl(provider(), newfd, isa);
    268             SecurityManager sm = System.getSecurityManager();
    269             if (sm != null) {
    270                 try {
    271                     sm.checkAccept(isa.getAddress().getHostAddress(),
    272                                    isa.getPort());
    273                 } catch (SecurityException x) {
    274                     sc.close();
    275                     throw x;
    276                 }
    277             }
    278             return sc;
    279 
    280         }
    281     }
    282 
    283     protected void implConfigureBlocking(boolean block) throws IOException {
    284         IOUtil.configureBlocking(fd, block);
    285     }
    286 
    287     protected void implCloseSelectableChannel() throws IOException {
    288         synchronized (stateLock) {
    289             if (state != ST_KILLED)
    290                 nd.preClose(fd);
    291             long th = thread;
    292             if (th != 0)
    293                 NativeThread.signal(th);
    294             if (!isRegistered())
    295                 kill();
    296         }
    297     }
    298 
    299     public void kill() throws IOException {
    300         synchronized (stateLock) {
    301             if (state == ST_KILLED)
    302                 return;
    303             if (state == ST_UNINITIALIZED) {
    304                 state = ST_KILLED;
    305                 return;
    306             }
    307             assert !isOpen() && !isRegistered();
    308             nd.close(fd);
    309             state = ST_KILLED;
    310         }
    311     }
    312 
    313     /**
    314      * Translates native poll revent set into a ready operation set
    315      */
    316     public boolean translateReadyOps(int ops, int initialOps,
    317                                      SelectionKeyImpl sk) {
    318         int intOps = sk.nioInterestOps(); // Do this just once, it synchronizes
    319         int oldOps = sk.nioReadyOps();
    320         int newOps = initialOps;
    321 
    322         if ((ops & Net.POLLNVAL) != 0) {
    323             // This should only happen if this channel is pre-closed while a
    324             // selection operation is in progress
    325             // ## Throw an error if this channel has not been pre-closed
    326             return false;
    327         }
    328 
    329         if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) {
    330             newOps = intOps;
    331             sk.nioReadyOps(newOps);
    332             return (newOps & ~oldOps) != 0;
    333         }
    334 
    335         if (((ops & Net.POLLIN) != 0) &&
    336             ((intOps & SelectionKey.OP_ACCEPT) != 0))
    337                 newOps |= SelectionKey.OP_ACCEPT;
    338 
    339         sk.nioReadyOps(newOps);
    340         return (newOps & ~oldOps) != 0;
    341     }
    342 
    343     public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
    344         return translateReadyOps(ops, sk.nioReadyOps(), sk);
    345     }
    346 
    347     public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
    348         return translateReadyOps(ops, 0, sk);
    349     }
    350 
    351     // package-private
    352     int poll(int events, long timeout) throws IOException {
    353         assert Thread.holdsLock(blockingLock()) && !isBlocking();
    354 
    355         synchronized (lock) {
    356             int n = 0;
    357             try {
    358                 begin();
    359                 synchronized (stateLock) {
    360                     if (!isOpen())
    361                         return 0;
    362                     thread = NativeThread.current();
    363                 }
    364                 n = Net.poll(fd, events, timeout);
    365             } finally {
    366                 thread = 0;
    367                 end(n > 0);
    368             }
    369             return n;
    370         }
    371     }
    372 
    373     /**
    374      * Translates an interest operation set into a native poll event set
    375      */
    376     public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
    377         int newOps = 0;
    378 
    379         // Translate ops
    380         if ((ops & SelectionKey.OP_ACCEPT) != 0)
    381             newOps |= Net.POLLIN;
    382         // Place ops into pollfd array
    383         sk.selector.putEventOps(sk, newOps);
    384     }
    385 
    386     public FileDescriptor getFD() {
    387         return fd;
    388     }
    389 
    390     public int getFDVal() {
    391         return fdVal;
    392     }
    393 
    394     public String toString() {
    395         StringBuffer sb = new StringBuffer();
    396         sb.append(this.getClass().getName());
    397         sb.append('[');
    398         if (!isOpen()) {
    399             sb.append("closed");
    400         } else {
    401             synchronized (stateLock) {
    402                 InetSocketAddress addr = localAddress();
    403                 if (addr == null) {
    404                     sb.append("unbound");
    405                 } else {
    406                     sb.append(Net.getRevealedLocalAddressAsString(addr));
    407                 }
    408             }
    409         }
    410         sb.append(']');
    411         return sb.toString();
    412     }
    413 
    414     /**
    415      * Accept a connection on a socket.
    416      *
    417      * @implNote Wrap native call to allow instrumentation.
    418      */
    419     private int accept(FileDescriptor ssfd, FileDescriptor newfd,
    420                        InetSocketAddress[] isaa)
    421         throws IOException
    422     {
    423         return accept0(ssfd, newfd, isaa);
    424     }
    425 
    426     // -- Native methods --
    427 
    428     // Accepts a new connection, setting the given file descriptor to refer to
    429     // the new socket and setting isaa[0] to the socket's remote address.
    430     // Returns 1 on success, or IOStatus.UNAVAILABLE (if non-blocking and no
    431     // connections are pending) or IOStatus.INTERRUPTED.
    432     //
    433     private native int accept0(FileDescriptor ssfd, FileDescriptor newfd,
    434                                InetSocketAddress[] isaa)
    435         throws IOException;
    436 
    437     private static native void initIDs();
    438 
    439     static {
    440         initIDs();
    441         nd = new SocketDispatcher();
    442     }
    443 
    444 }
    445