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