|
1 /* |
|
2 * Copyright (c) 2019, 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.io.InputStream; |
|
31 import java.io.OutputStream; |
|
32 import java.io.UncheckedIOException; |
|
33 import java.lang.invoke.MethodHandles; |
|
34 import java.lang.invoke.VarHandle; |
|
35 import java.net.InetAddress; |
|
36 import java.net.InetSocketAddress; |
|
37 import java.net.ProtocolFamily; |
|
38 import java.net.SocketAddress; |
|
39 import java.net.SocketException; |
|
40 import java.net.SocketImpl; |
|
41 import java.net.SocketOption; |
|
42 import java.net.SocketTimeoutException; |
|
43 import java.net.StandardProtocolFamily; |
|
44 import java.net.StandardSocketOptions; |
|
45 import java.net.UnknownHostException; |
|
46 import java.nio.ByteBuffer; |
|
47 import java.util.Collections; |
|
48 import java.util.HashSet; |
|
49 import java.util.Objects; |
|
50 import java.util.Set; |
|
51 import java.util.concurrent.TimeUnit; |
|
52 import java.util.concurrent.locks.ReentrantLock; |
|
53 |
|
54 import jdk.internal.ref.CleanerFactory; |
|
55 import sun.net.ConnectionResetException; |
|
56 import sun.net.NetHooks; |
|
57 import sun.net.PlatformSocketImpl; |
|
58 import sun.net.ResourceManager; |
|
59 import sun.net.ext.ExtendedSocketOptions; |
|
60 import sun.net.util.SocketExceptions; |
|
61 |
|
62 import static java.util.concurrent.TimeUnit.MILLISECONDS; |
|
63 import static java.util.concurrent.TimeUnit.NANOSECONDS; |
|
64 |
|
65 /** |
|
66 * NIO based SocketImpl. |
|
67 * |
|
68 * This implementation attempts to be compatible with legacy PlainSocketImpl, |
|
69 * including behavior and exceptions that are not specified by SocketImpl. |
|
70 * |
|
71 * The underlying socket used by this SocketImpl is initially configured |
|
72 * blocking. If the connect method is used to establish a connection with a |
|
73 * timeout then the socket is configured non-blocking for the connect attempt, |
|
74 * and then restored to blocking mode when the connection is established. |
|
75 * If the accept or read methods are used with a timeout then the socket is |
|
76 * configured non-blocking and is never restored. When in non-blocking mode, |
|
77 * operations that don't complete immediately will poll the socket and preserve |
|
78 * the semantics of blocking operations. |
|
79 */ |
|
80 |
|
81 public final class NioSocketImpl extends SocketImpl implements PlatformSocketImpl { |
|
82 private static final NativeDispatcher nd = new SocketDispatcher(); |
|
83 |
|
84 // The maximum number of bytes to read/write per syscall to avoid needing |
|
85 // a huge buffer from the temporary buffer cache |
|
86 private static final int MAX_BUFFER_SIZE = 128 * 1024; |
|
87 |
|
88 // true if this is a SocketImpl for a ServerSocket |
|
89 private final boolean server; |
|
90 |
|
91 // Lock held when reading (also used when accepting or connecting) |
|
92 private final ReentrantLock readLock = new ReentrantLock(); |
|
93 |
|
94 // Lock held when writing |
|
95 private final ReentrantLock writeLock = new ReentrantLock(); |
|
96 |
|
97 // The stateLock for read/changing state |
|
98 private final Object stateLock = new Object(); |
|
99 private static final int ST_NEW = 0; |
|
100 private static final int ST_UNCONNECTED = 1; |
|
101 private static final int ST_CONNECTING = 2; |
|
102 private static final int ST_CONNECTED = 3; |
|
103 private static final int ST_CLOSING = 4; |
|
104 private static final int ST_CLOSED = 5; |
|
105 private volatile int state; // need stateLock to change |
|
106 |
|
107 // set by SocketImpl.create, protected by stateLock |
|
108 private boolean stream; |
|
109 private FileDescriptorCloser closer; |
|
110 |
|
111 // set to true when the socket is in non-blocking mode |
|
112 private volatile boolean nonBlocking; |
|
113 |
|
114 // used by connect/read/write/accept, protected by stateLock |
|
115 private long readerThread; |
|
116 private long writerThread; |
|
117 |
|
118 // used when SO_REUSEADDR is emulated, protected by stateLock |
|
119 private boolean isReuseAddress; |
|
120 |
|
121 // read or accept timeout in millis |
|
122 private volatile int timeout; |
|
123 |
|
124 // flags to indicate if the connection is shutdown for input and output |
|
125 private volatile boolean isInputClosed; |
|
126 private volatile boolean isOutputClosed; |
|
127 |
|
128 // used by read to emulate legacy behavior, protected by readLock |
|
129 private boolean readEOF; |
|
130 private boolean connectionReset; |
|
131 |
|
132 /** |
|
133 * Creates an instance of this SocketImpl. |
|
134 * @param server true if this is a SocketImpl for a ServerSocket |
|
135 */ |
|
136 public NioSocketImpl(boolean server) { |
|
137 this.server = server; |
|
138 } |
|
139 |
|
140 /** |
|
141 * Returns true if the socket is open. |
|
142 */ |
|
143 private boolean isOpen() { |
|
144 return state < ST_CLOSING; |
|
145 } |
|
146 |
|
147 /** |
|
148 * Throws SocketException if the socket is not open. |
|
149 */ |
|
150 private void ensureOpen() throws SocketException { |
|
151 int state = this.state; |
|
152 if (state == ST_NEW) |
|
153 throw new SocketException("Socket not created"); |
|
154 if (state >= ST_CLOSING) |
|
155 throw new SocketException("Socket closed"); |
|
156 } |
|
157 |
|
158 /** |
|
159 * Throws SocketException if the socket is not open and connected. |
|
160 */ |
|
161 private void ensureOpenAndConnected() throws SocketException { |
|
162 int state = this.state; |
|
163 if (state < ST_CONNECTED) |
|
164 throw new SocketException("Not connected"); |
|
165 if (state > ST_CONNECTED) |
|
166 throw new SocketException("Socket closed"); |
|
167 } |
|
168 |
|
169 /** |
|
170 * Disables the current thread for scheduling purposes until the socket is |
|
171 * ready for I/O, or is asynchronously closed, for up to the specified |
|
172 * waiting time. |
|
173 * @throws IOException if an I/O error occurs |
|
174 */ |
|
175 private void park(FileDescriptor fd, int event, long nanos) throws IOException { |
|
176 long millis; |
|
177 if (nanos == 0) { |
|
178 millis = -1; |
|
179 } else { |
|
180 millis = NANOSECONDS.toMillis(nanos); |
|
181 } |
|
182 Net.poll(fd, event, millis); |
|
183 } |
|
184 |
|
185 /** |
|
186 * Disables the current thread for scheduling purposes until the socket is |
|
187 * ready for I/O or is asynchronously closed. |
|
188 * @throws IOException if an I/O error occurs |
|
189 */ |
|
190 private void park(FileDescriptor fd, int event) throws IOException { |
|
191 park(fd, event, 0); |
|
192 } |
|
193 |
|
194 /** |
|
195 * Configures the socket to blocking mode. This method is a no-op if the |
|
196 * socket is already in blocking mode. |
|
197 * @throws IOException if closed or there is an I/O error changing the mode |
|
198 */ |
|
199 private void configureBlocking(FileDescriptor fd) throws IOException { |
|
200 assert readLock.isHeldByCurrentThread(); |
|
201 if (nonBlocking) { |
|
202 synchronized (stateLock) { |
|
203 ensureOpen(); |
|
204 IOUtil.configureBlocking(fd, true); |
|
205 nonBlocking = false; |
|
206 } |
|
207 } |
|
208 } |
|
209 |
|
210 /** |
|
211 * Configures the socket to non-blocking mode. This method is a no-op if the |
|
212 * socket is already in non-blocking mode. |
|
213 * @throws IOException if closed or there is an I/O error changing the mode |
|
214 */ |
|
215 private void configureNonBlocking(FileDescriptor fd) throws IOException { |
|
216 assert readLock.isHeldByCurrentThread(); |
|
217 if (!nonBlocking) { |
|
218 synchronized (stateLock) { |
|
219 ensureOpen(); |
|
220 IOUtil.configureBlocking(fd, false); |
|
221 nonBlocking = true; |
|
222 } |
|
223 } |
|
224 } |
|
225 |
|
226 /** |
|
227 * Marks the beginning of a read operation that might block. |
|
228 * @throws SocketException if the socket is closed or not connected |
|
229 */ |
|
230 private FileDescriptor beginRead() throws SocketException { |
|
231 synchronized (stateLock) { |
|
232 ensureOpenAndConnected(); |
|
233 readerThread = NativeThread.current(); |
|
234 return fd; |
|
235 } |
|
236 } |
|
237 |
|
238 /** |
|
239 * Marks the end of a read operation that may have blocked. |
|
240 * @throws SocketException is the socket is closed |
|
241 */ |
|
242 private void endRead(boolean completed) throws SocketException { |
|
243 synchronized (stateLock) { |
|
244 readerThread = 0; |
|
245 int state = this.state; |
|
246 if (state == ST_CLOSING) |
|
247 tryFinishClose(); |
|
248 if (!completed && state >= ST_CLOSING) |
|
249 throw new SocketException("Socket closed"); |
|
250 } |
|
251 } |
|
252 |
|
253 /** |
|
254 * Attempts to read bytes from the socket into the given byte array. |
|
255 */ |
|
256 private int tryRead(FileDescriptor fd, byte[] b, int off, int len) |
|
257 throws IOException |
|
258 { |
|
259 ByteBuffer dst = Util.getTemporaryDirectBuffer(len); |
|
260 assert dst.position() == 0; |
|
261 try { |
|
262 int n = nd.read(fd, ((DirectBuffer)dst).address(), len); |
|
263 if (n > 0) { |
|
264 dst.get(b, off, n); |
|
265 } |
|
266 return n; |
|
267 } finally { |
|
268 Util.offerFirstTemporaryDirectBuffer(dst); |
|
269 } |
|
270 } |
|
271 |
|
272 /** |
|
273 * Reads bytes from the socket into the given byte array with a timeout. |
|
274 * @throws SocketTimeoutException if the read timeout elapses |
|
275 */ |
|
276 private int timedRead(FileDescriptor fd, byte[] b, int off, int len, long nanos) |
|
277 throws IOException |
|
278 { |
|
279 long startNanos = System.nanoTime(); |
|
280 int n = tryRead(fd, b, off, len); |
|
281 while (n == IOStatus.UNAVAILABLE && isOpen()) { |
|
282 long remainingNanos = nanos - (System.nanoTime() - startNanos); |
|
283 if (remainingNanos <= 0) { |
|
284 throw new SocketTimeoutException("Read timed out"); |
|
285 } |
|
286 park(fd, Net.POLLIN, remainingNanos); |
|
287 n = tryRead(fd, b, off, len); |
|
288 } |
|
289 return n; |
|
290 } |
|
291 |
|
292 /** |
|
293 * Reads bytes from the socket into the given byte array. |
|
294 * @return the number of bytes read or -1 at EOF |
|
295 * @throws SocketException if the socket is closed or a socket I/O error occurs |
|
296 * @throws SocketTimeoutException if the read timeout elapses |
|
297 */ |
|
298 private int implRead(byte[] b, int off, int len) throws IOException { |
|
299 int n = 0; |
|
300 FileDescriptor fd = beginRead(); |
|
301 try { |
|
302 if (connectionReset) |
|
303 throw new SocketException("Connection reset"); |
|
304 if (isInputClosed) |
|
305 return -1; |
|
306 int timeout = this.timeout; |
|
307 if (timeout > 0) { |
|
308 // read with timeout |
|
309 configureNonBlocking(fd); |
|
310 n = timedRead(fd, b, off, len, MILLISECONDS.toNanos(timeout)); |
|
311 } else { |
|
312 // read, no timeout |
|
313 n = tryRead(fd, b, off, len); |
|
314 while (IOStatus.okayToRetry(n) && isOpen()) { |
|
315 park(fd, Net.POLLIN); |
|
316 n = tryRead(fd, b, off, len); |
|
317 } |
|
318 } |
|
319 return n; |
|
320 } catch (SocketTimeoutException e) { |
|
321 throw e; |
|
322 } catch (ConnectionResetException e) { |
|
323 connectionReset = true; |
|
324 throw new SocketException("Connection reset"); |
|
325 } catch (IOException ioe) { |
|
326 throw new SocketException(ioe.getMessage()); |
|
327 } finally { |
|
328 endRead(n > 0); |
|
329 } |
|
330 } |
|
331 |
|
332 /** |
|
333 * Reads bytes from the socket into the given byte array. |
|
334 * @return the number of bytes read or -1 at EOF |
|
335 * @throws IndexOutOfBoundsException if the bound checks fail |
|
336 * @throws SocketException if the socket is closed or a socket I/O error occurs |
|
337 * @throws SocketTimeoutException if the read timeout elapses |
|
338 */ |
|
339 private int read(byte[] b, int off, int len) throws IOException { |
|
340 Objects.checkFromIndexSize(off, len, b.length); |
|
341 if (len == 0) { |
|
342 return 0; |
|
343 } else { |
|
344 readLock.lock(); |
|
345 try { |
|
346 // emulate legacy behavior to return -1, even if socket is closed |
|
347 if (readEOF) |
|
348 return -1; |
|
349 // read up to MAX_BUFFER_SIZE bytes |
|
350 int size = Math.min(len, MAX_BUFFER_SIZE); |
|
351 int n = implRead(b, off, size); |
|
352 if (n == -1) |
|
353 readEOF = true; |
|
354 return n; |
|
355 } finally { |
|
356 readLock.unlock(); |
|
357 } |
|
358 } |
|
359 } |
|
360 |
|
361 /** |
|
362 * Marks the beginning of a write operation that might block. |
|
363 * @throws SocketException if the socket is closed or not connected |
|
364 */ |
|
365 private FileDescriptor beginWrite() throws SocketException { |
|
366 synchronized (stateLock) { |
|
367 ensureOpenAndConnected(); |
|
368 writerThread = NativeThread.current(); |
|
369 return fd; |
|
370 } |
|
371 } |
|
372 |
|
373 /** |
|
374 * Marks the end of a write operation that may have blocked. |
|
375 * @throws SocketException is the socket is closed |
|
376 */ |
|
377 private void endWrite(boolean completed) throws SocketException { |
|
378 synchronized (stateLock) { |
|
379 writerThread = 0; |
|
380 int state = this.state; |
|
381 if (state == ST_CLOSING) |
|
382 tryFinishClose(); |
|
383 if (!completed && state >= ST_CLOSING) |
|
384 throw new SocketException("Socket closed"); |
|
385 } |
|
386 } |
|
387 |
|
388 /** |
|
389 * Attempts to write a sequence of bytes to the socket from the given |
|
390 * byte array. |
|
391 */ |
|
392 private int tryWrite(FileDescriptor fd, byte[] b, int off, int len) |
|
393 throws IOException |
|
394 { |
|
395 ByteBuffer src = Util.getTemporaryDirectBuffer(len); |
|
396 assert src.position() == 0; |
|
397 try { |
|
398 src.put(b, off, len); |
|
399 return nd.write(fd, ((DirectBuffer)src).address(), len); |
|
400 } finally { |
|
401 Util.offerFirstTemporaryDirectBuffer(src); |
|
402 } |
|
403 } |
|
404 |
|
405 /** |
|
406 * Writes a sequence of bytes to the socket from the given byte array. |
|
407 * @return the number of bytes written |
|
408 * @throws SocketException if the socket is closed or a socket I/O error occurs |
|
409 */ |
|
410 private int implWrite(byte[] b, int off, int len) throws IOException { |
|
411 int n = 0; |
|
412 FileDescriptor fd = beginWrite(); |
|
413 try { |
|
414 n = tryWrite(fd, b, off, len); |
|
415 while (IOStatus.okayToRetry(n) && isOpen()) { |
|
416 park(fd, Net.POLLOUT); |
|
417 n = tryWrite(fd, b, off, len); |
|
418 } |
|
419 return n; |
|
420 } catch (IOException ioe) { |
|
421 throw new SocketException(ioe.getMessage()); |
|
422 } finally { |
|
423 endWrite(n > 0); |
|
424 } |
|
425 } |
|
426 |
|
427 /** |
|
428 * Writes a sequence of bytes to the socket from the given byte array. |
|
429 * @throws SocketException if the socket is closed or a socket I/O error occurs |
|
430 */ |
|
431 private void write(byte[] b, int off, int len) throws IOException { |
|
432 Objects.checkFromIndexSize(off, len, b.length); |
|
433 if (len > 0) { |
|
434 writeLock.lock(); |
|
435 try { |
|
436 int pos = off; |
|
437 int end = off + len; |
|
438 while (pos < end) { |
|
439 // write up to MAX_BUFFER_SIZE bytes |
|
440 int size = Math.min((end - pos), MAX_BUFFER_SIZE); |
|
441 int n = implWrite(b, pos, size); |
|
442 pos += n; |
|
443 } |
|
444 } finally { |
|
445 writeLock.unlock(); |
|
446 } |
|
447 } |
|
448 } |
|
449 |
|
450 /** |
|
451 * Creates the socket. |
|
452 * @param stream {@code true} for a streams socket |
|
453 */ |
|
454 @Override |
|
455 protected void create(boolean stream) throws IOException { |
|
456 synchronized (stateLock) { |
|
457 if (state != ST_NEW) |
|
458 throw new IOException("Already created"); |
|
459 if (!stream) |
|
460 ResourceManager.beforeUdpCreate(); |
|
461 FileDescriptor fd; |
|
462 try { |
|
463 if (server) { |
|
464 assert stream; |
|
465 fd = Net.serverSocket(true); |
|
466 } else { |
|
467 fd = Net.socket(stream); |
|
468 } |
|
469 } catch (IOException ioe) { |
|
470 if (!stream) |
|
471 ResourceManager.afterUdpClose(); |
|
472 throw ioe; |
|
473 } |
|
474 this.fd = fd; |
|
475 this.stream = stream; |
|
476 this.closer = FileDescriptorCloser.create(this); |
|
477 this.state = ST_UNCONNECTED; |
|
478 } |
|
479 } |
|
480 |
|
481 /** |
|
482 * Marks the beginning of a connect operation that might block. |
|
483 * @throws SocketException if the socket is closed or already connected |
|
484 */ |
|
485 private FileDescriptor beginConnect(InetAddress address, int port) |
|
486 throws IOException |
|
487 { |
|
488 synchronized (stateLock) { |
|
489 int state = this.state; |
|
490 if (state != ST_UNCONNECTED) { |
|
491 if (state == ST_NEW) |
|
492 throw new SocketException("Not created"); |
|
493 if (state == ST_CONNECTING) |
|
494 throw new SocketException("Connection in progress"); |
|
495 if (state == ST_CONNECTED) |
|
496 throw new SocketException("Already connected"); |
|
497 if (state >= ST_CLOSING) |
|
498 throw new SocketException("Socket closed"); |
|
499 assert false; |
|
500 } |
|
501 this.state = ST_CONNECTING; |
|
502 |
|
503 // invoke beforeTcpConnect hook if not already bound |
|
504 if (localport == 0) { |
|
505 NetHooks.beforeTcpConnect(fd, address, port); |
|
506 } |
|
507 |
|
508 // save the remote address/port |
|
509 this.address = address; |
|
510 this.port = port; |
|
511 |
|
512 readerThread = NativeThread.current(); |
|
513 return fd; |
|
514 } |
|
515 } |
|
516 |
|
517 /** |
|
518 * Marks the end of a connect operation that may have blocked. |
|
519 * @throws SocketException is the socket is closed |
|
520 */ |
|
521 private void endConnect(FileDescriptor fd, boolean completed) throws IOException { |
|
522 synchronized (stateLock) { |
|
523 readerThread = 0; |
|
524 int state = this.state; |
|
525 if (state == ST_CLOSING) |
|
526 tryFinishClose(); |
|
527 if (completed && state == ST_CONNECTING) { |
|
528 this.state = ST_CONNECTED; |
|
529 localport = Net.localAddress(fd).getPort(); |
|
530 } else if (!completed && state >= ST_CLOSING) { |
|
531 throw new SocketException("Socket closed"); |
|
532 } |
|
533 } |
|
534 } |
|
535 |
|
536 /** |
|
537 * Waits for a connection attempt to finish with a timeout |
|
538 * @throws SocketTimeoutException if the connect timeout elapses |
|
539 */ |
|
540 private boolean timedFinishConnect(FileDescriptor fd, long nanos) throws IOException { |
|
541 long startNanos = System.nanoTime(); |
|
542 boolean polled = Net.pollConnectNow(fd); |
|
543 while (!polled && isOpen()) { |
|
544 long remainingNanos = nanos - (System.nanoTime() - startNanos); |
|
545 if (remainingNanos <= 0) { |
|
546 throw new SocketTimeoutException("Connect timed out"); |
|
547 } |
|
548 park(fd, Net.POLLOUT, remainingNanos); |
|
549 polled = Net.pollConnectNow(fd); |
|
550 } |
|
551 return polled && isOpen(); |
|
552 } |
|
553 |
|
554 /** |
|
555 * Attempts to establish a connection to the given socket address with a |
|
556 * timeout. Closes the socket if connection cannot be established. |
|
557 * @throws IOException if the address is not a resolved InetSocketAddress or |
|
558 * the connection cannot be established |
|
559 */ |
|
560 @Override |
|
561 protected void connect(SocketAddress remote, int millis) throws IOException { |
|
562 // SocketImpl connect only specifies IOException |
|
563 if (!(remote instanceof InetSocketAddress)) |
|
564 throw new IOException("Unsupported address type"); |
|
565 InetSocketAddress isa = (InetSocketAddress) remote; |
|
566 if (isa.isUnresolved()) { |
|
567 throw new UnknownHostException(isa.getHostName()); |
|
568 } |
|
569 |
|
570 InetAddress address = isa.getAddress(); |
|
571 if (address.isAnyLocalAddress()) |
|
572 address = InetAddress.getLocalHost(); |
|
573 int port = isa.getPort(); |
|
574 |
|
575 ReentrantLock connectLock = readLock; |
|
576 try { |
|
577 connectLock.lock(); |
|
578 try { |
|
579 boolean connected = false; |
|
580 FileDescriptor fd = beginConnect(address, port); |
|
581 try { |
|
582 |
|
583 // configure socket to non-blocking mode when there is a timeout |
|
584 if (millis > 0) { |
|
585 configureNonBlocking(fd); |
|
586 } |
|
587 |
|
588 int n = Net.connect(fd, address, port); |
|
589 if (n > 0) { |
|
590 // connection established |
|
591 connected = true; |
|
592 } else { |
|
593 assert IOStatus.okayToRetry(n); |
|
594 if (millis > 0) { |
|
595 // finish connect with timeout |
|
596 long nanos = MILLISECONDS.toNanos(millis); |
|
597 connected = timedFinishConnect(fd, nanos); |
|
598 } else { |
|
599 // finish connect, no timeout |
|
600 boolean polled = false; |
|
601 while (!polled && isOpen()) { |
|
602 park(fd, Net.POLLOUT); |
|
603 polled = Net.pollConnectNow(fd); |
|
604 } |
|
605 connected = polled && isOpen(); |
|
606 } |
|
607 } |
|
608 |
|
609 // restore socket to blocking mode |
|
610 if (connected && millis > 0) { |
|
611 configureBlocking(fd); |
|
612 } |
|
613 |
|
614 } finally { |
|
615 endConnect(fd, connected); |
|
616 } |
|
617 } finally { |
|
618 connectLock.unlock(); |
|
619 } |
|
620 } catch (IOException ioe) { |
|
621 close(); |
|
622 throw SocketExceptions.of(ioe, isa); |
|
623 } |
|
624 } |
|
625 |
|
626 @Override |
|
627 protected void connect(String host, int port) throws IOException { |
|
628 connect(new InetSocketAddress(host, port), 0); |
|
629 } |
|
630 |
|
631 @Override |
|
632 protected void connect(InetAddress address, int port) throws IOException { |
|
633 connect(new InetSocketAddress(address, port), 0); |
|
634 } |
|
635 |
|
636 @Override |
|
637 protected void bind(InetAddress host, int port) throws IOException { |
|
638 synchronized (stateLock) { |
|
639 ensureOpen(); |
|
640 if (localport != 0) |
|
641 throw new SocketException("Already bound"); |
|
642 NetHooks.beforeTcpBind(fd, host, port); |
|
643 Net.bind(fd, host, port); |
|
644 // set the address field to the given host address to keep |
|
645 // compatibility with PlainSocketImpl. When binding to 0.0.0.0 |
|
646 // then the actual local address will be ::0 when IPv6 is enabled. |
|
647 address = host; |
|
648 localport = Net.localAddress(fd).getPort(); |
|
649 } |
|
650 } |
|
651 |
|
652 @Override |
|
653 protected void listen(int backlog) throws IOException { |
|
654 synchronized (stateLock) { |
|
655 ensureOpen(); |
|
656 if (localport == 0) |
|
657 throw new SocketException("Not bound"); |
|
658 Net.listen(fd, backlog < 1 ? 50 : backlog); |
|
659 } |
|
660 } |
|
661 |
|
662 /** |
|
663 * Marks the beginning of an accept operation that might block. |
|
664 * @throws SocketException if the socket is closed |
|
665 */ |
|
666 private FileDescriptor beginAccept() throws SocketException { |
|
667 synchronized (stateLock) { |
|
668 ensureOpen(); |
|
669 if (!stream) |
|
670 throw new SocketException("Not a stream socket"); |
|
671 if (localport == 0) |
|
672 throw new SocketException("Not bound"); |
|
673 readerThread = NativeThread.current(); |
|
674 return fd; |
|
675 } |
|
676 } |
|
677 |
|
678 /** |
|
679 * Marks the end of an accept operation that may have blocked. |
|
680 * @throws SocketException is the socket is closed |
|
681 */ |
|
682 private void endAccept(boolean completed) throws SocketException { |
|
683 synchronized (stateLock) { |
|
684 int state = this.state; |
|
685 readerThread = 0; |
|
686 if (state == ST_CLOSING) |
|
687 tryFinishClose(); |
|
688 if (!completed && state >= ST_CLOSING) |
|
689 throw new SocketException("Socket closed"); |
|
690 } |
|
691 } |
|
692 |
|
693 /** |
|
694 * Accepts a new connection with a timeout. |
|
695 * @throws SocketTimeoutException if the accept timeout elapses |
|
696 */ |
|
697 private int timedAccept(FileDescriptor fd, |
|
698 FileDescriptor newfd, |
|
699 InetSocketAddress[] isaa, |
|
700 long nanos) |
|
701 throws IOException |
|
702 { |
|
703 long startNanos = System.nanoTime(); |
|
704 int n = Net.accept(fd, newfd, isaa); |
|
705 while (n == IOStatus.UNAVAILABLE && isOpen()) { |
|
706 long remainingNanos = nanos - (System.nanoTime() - startNanos); |
|
707 if (remainingNanos <= 0) { |
|
708 throw new SocketTimeoutException("Accept timed out"); |
|
709 } |
|
710 park(fd, Net.POLLIN, remainingNanos); |
|
711 n = Net.accept(fd, newfd, isaa); |
|
712 } |
|
713 return n; |
|
714 } |
|
715 |
|
716 /** |
|
717 * Accepts a new connection so that the given SocketImpl is connected to |
|
718 * the peer. The SocketImpl must be a newly created NioSocketImpl. |
|
719 */ |
|
720 @Override |
|
721 protected void accept(SocketImpl si) throws IOException { |
|
722 NioSocketImpl nsi = (NioSocketImpl) si; |
|
723 if (nsi.state != ST_NEW) |
|
724 throw new SocketException("Not a newly created SocketImpl"); |
|
725 |
|
726 FileDescriptor newfd = new FileDescriptor(); |
|
727 InetSocketAddress[] isaa = new InetSocketAddress[1]; |
|
728 |
|
729 // acquire the lock, adjusting the timeout for cases where several |
|
730 // threads are accepting connections and there is a timeout set |
|
731 ReentrantLock acceptLock = readLock; |
|
732 int timeout = this.timeout; |
|
733 long remainingNanos = 0; |
|
734 if (timeout > 0) { |
|
735 remainingNanos = tryLock(acceptLock, timeout, MILLISECONDS); |
|
736 if (remainingNanos <= 0) { |
|
737 assert !acceptLock.isHeldByCurrentThread(); |
|
738 throw new SocketTimeoutException("Accept timed out"); |
|
739 } |
|
740 } else { |
|
741 acceptLock.lock(); |
|
742 } |
|
743 |
|
744 // accept a connection |
|
745 try { |
|
746 int n = 0; |
|
747 FileDescriptor fd = beginAccept(); |
|
748 try { |
|
749 if (remainingNanos > 0) { |
|
750 // accept with timeout |
|
751 configureNonBlocking(fd); |
|
752 n = timedAccept(fd, newfd, isaa, remainingNanos); |
|
753 } else { |
|
754 // accept, no timeout |
|
755 n = Net.accept(fd, newfd, isaa); |
|
756 while (IOStatus.okayToRetry(n) && isOpen()) { |
|
757 park(fd, Net.POLLIN); |
|
758 n = Net.accept(fd, newfd, isaa); |
|
759 } |
|
760 } |
|
761 } finally { |
|
762 endAccept(n > 0); |
|
763 assert IOStatus.check(n); |
|
764 } |
|
765 } finally { |
|
766 acceptLock.unlock(); |
|
767 } |
|
768 |
|
769 // get local address and configure accepted socket to blocking mode |
|
770 InetSocketAddress localAddress; |
|
771 try { |
|
772 localAddress = Net.localAddress(newfd); |
|
773 IOUtil.configureBlocking(newfd, true); |
|
774 } catch (IOException ioe) { |
|
775 nd.close(newfd); |
|
776 throw ioe; |
|
777 } |
|
778 |
|
779 // set the fields |
|
780 synchronized (nsi.stateLock) { |
|
781 nsi.fd = newfd; |
|
782 nsi.stream = true; |
|
783 nsi.closer = FileDescriptorCloser.create(nsi); |
|
784 nsi.localport = localAddress.getPort(); |
|
785 nsi.address = isaa[0].getAddress(); |
|
786 nsi.port = isaa[0].getPort(); |
|
787 nsi.state = ST_CONNECTED; |
|
788 } |
|
789 } |
|
790 |
|
791 @Override |
|
792 protected InputStream getInputStream() { |
|
793 return new InputStream() { |
|
794 @Override |
|
795 public int read() throws IOException { |
|
796 byte[] a = new byte[1]; |
|
797 int n = read(a, 0, 1); |
|
798 return (n > 0) ? (a[0] & 0xff) : -1; |
|
799 } |
|
800 @Override |
|
801 public int read(byte[] b, int off, int len) throws IOException { |
|
802 return NioSocketImpl.this.read(b, off, len); |
|
803 } |
|
804 @Override |
|
805 public int available() throws IOException { |
|
806 return NioSocketImpl.this.available(); |
|
807 } |
|
808 @Override |
|
809 public void close() throws IOException { |
|
810 NioSocketImpl.this.close(); |
|
811 } |
|
812 }; |
|
813 } |
|
814 |
|
815 @Override |
|
816 protected OutputStream getOutputStream() { |
|
817 return new OutputStream() { |
|
818 @Override |
|
819 public void write(int b) throws IOException { |
|
820 byte[] a = new byte[]{(byte) b}; |
|
821 write(a, 0, 1); |
|
822 } |
|
823 @Override |
|
824 public void write(byte[] b, int off, int len) throws IOException { |
|
825 NioSocketImpl.this.write(b, off, len); |
|
826 } |
|
827 @Override |
|
828 public void close() throws IOException { |
|
829 NioSocketImpl.this.close(); |
|
830 } |
|
831 }; |
|
832 } |
|
833 |
|
834 @Override |
|
835 protected int available() throws IOException { |
|
836 synchronized (stateLock) { |
|
837 ensureOpenAndConnected(); |
|
838 if (isInputClosed) { |
|
839 return 0; |
|
840 } else { |
|
841 return Net.available(fd); |
|
842 } |
|
843 } |
|
844 } |
|
845 |
|
846 /** |
|
847 * Closes the socket if there are no I/O operations in progress. |
|
848 */ |
|
849 private boolean tryClose() throws IOException { |
|
850 assert Thread.holdsLock(stateLock) && state == ST_CLOSING; |
|
851 if (readerThread == 0 && writerThread == 0) { |
|
852 try { |
|
853 closer.run(); |
|
854 } catch (UncheckedIOException ioe) { |
|
855 throw ioe.getCause(); |
|
856 } finally { |
|
857 state = ST_CLOSED; |
|
858 } |
|
859 return true; |
|
860 } else { |
|
861 return false; |
|
862 } |
|
863 } |
|
864 |
|
865 /** |
|
866 * Invokes tryClose to attempt to close the socket. |
|
867 * |
|
868 * This method is used for deferred closing by I/O operations. |
|
869 */ |
|
870 private void tryFinishClose() { |
|
871 try { |
|
872 tryClose(); |
|
873 } catch (IOException ignore) { } |
|
874 } |
|
875 |
|
876 /** |
|
877 * Closes the socket. If there are I/O operations in progress then the |
|
878 * socket is pre-closed and the threads are signalled. The socket will be |
|
879 * closed when the last I/O operation aborts. |
|
880 */ |
|
881 @Override |
|
882 protected void close() throws IOException { |
|
883 synchronized (stateLock) { |
|
884 int state = this.state; |
|
885 if (state >= ST_CLOSING) |
|
886 return; |
|
887 if (state == ST_NEW) { |
|
888 // stillborn |
|
889 this.state = ST_CLOSED; |
|
890 return; |
|
891 } |
|
892 this.state = ST_CLOSING; |
|
893 |
|
894 // shutdown output when linger interval not set to 0 |
|
895 try { |
|
896 var SO_LINGER = StandardSocketOptions.SO_LINGER; |
|
897 if ((int) Net.getSocketOption(fd, SO_LINGER) != 0) { |
|
898 Net.shutdown(fd, Net.SHUT_WR); |
|
899 } |
|
900 } catch (IOException ignore) { } |
|
901 |
|
902 // attempt to close the socket. If there are I/O operations in progress |
|
903 // then the socket is pre-closed and the thread(s) signalled. The |
|
904 // last thread will close the file descriptor. |
|
905 if (!tryClose()) { |
|
906 nd.preClose(fd); |
|
907 long reader = readerThread; |
|
908 if (reader != 0) |
|
909 NativeThread.signal(reader); |
|
910 long writer = writerThread; |
|
911 if (writer != 0) |
|
912 NativeThread.signal(writer); |
|
913 } |
|
914 } |
|
915 } |
|
916 |
|
917 // the socket options supported by client and server sockets |
|
918 private static volatile Set<SocketOption<?>> clientSocketOptions; |
|
919 private static volatile Set<SocketOption<?>> serverSocketOptions; |
|
920 |
|
921 @Override |
|
922 protected Set<SocketOption<?>> supportedOptions() { |
|
923 Set<SocketOption<?>> options = (server) ? serverSocketOptions : clientSocketOptions; |
|
924 if (options == null) { |
|
925 options = new HashSet<>(); |
|
926 options.add(StandardSocketOptions.SO_RCVBUF); |
|
927 options.add(StandardSocketOptions.SO_REUSEADDR); |
|
928 if (server) { |
|
929 // IP_TOS added for server socket to maintain compatibility |
|
930 options.add(StandardSocketOptions.IP_TOS); |
|
931 options.addAll(ExtendedSocketOptions.serverSocketOptions()); |
|
932 } else { |
|
933 options.add(StandardSocketOptions.IP_TOS); |
|
934 options.add(StandardSocketOptions.SO_KEEPALIVE); |
|
935 options.add(StandardSocketOptions.SO_SNDBUF); |
|
936 options.add(StandardSocketOptions.SO_LINGER); |
|
937 options.add(StandardSocketOptions.TCP_NODELAY); |
|
938 options.addAll(ExtendedSocketOptions.clientSocketOptions()); |
|
939 } |
|
940 if (Net.isReusePortAvailable()) |
|
941 options.add(StandardSocketOptions.SO_REUSEPORT); |
|
942 options = Collections.unmodifiableSet(options); |
|
943 if (server) { |
|
944 serverSocketOptions = options; |
|
945 } else { |
|
946 clientSocketOptions = options; |
|
947 } |
|
948 } |
|
949 return options; |
|
950 } |
|
951 |
|
952 @Override |
|
953 protected <T> void setOption(SocketOption<T> opt, T value) throws IOException { |
|
954 if (!supportedOptions().contains(opt)) |
|
955 throw new UnsupportedOperationException("'" + opt + "' not supported"); |
|
956 if (!opt.type().isInstance(value)) |
|
957 throw new IllegalArgumentException("Invalid value '" + value + "'"); |
|
958 synchronized (stateLock) { |
|
959 ensureOpen(); |
|
960 if (opt == StandardSocketOptions.IP_TOS) { |
|
961 // maps to IP_TOS or IPV6_TCLASS |
|
962 Net.setSocketOption(fd, family(), opt, value); |
|
963 } else if (opt == StandardSocketOptions.SO_REUSEADDR) { |
|
964 boolean b = (boolean) value; |
|
965 if (Net.useExclusiveBind()) { |
|
966 isReuseAddress = b; |
|
967 } else { |
|
968 Net.setSocketOption(fd, opt, b); |
|
969 } |
|
970 } else { |
|
971 // option does not need special handling |
|
972 Net.setSocketOption(fd, opt, value); |
|
973 } |
|
974 } |
|
975 } |
|
976 |
|
977 @SuppressWarnings("unchecked") |
|
978 protected <T> T getOption(SocketOption<T> opt) throws IOException { |
|
979 if (!supportedOptions().contains(opt)) |
|
980 throw new UnsupportedOperationException("'" + opt + "' not supported"); |
|
981 synchronized (stateLock) { |
|
982 ensureOpen(); |
|
983 if (opt == StandardSocketOptions.IP_TOS) { |
|
984 return (T) Net.getSocketOption(fd, family(), opt); |
|
985 } else if (opt == StandardSocketOptions.SO_REUSEADDR) { |
|
986 if (Net.useExclusiveBind()) { |
|
987 return (T) Boolean.valueOf(isReuseAddress); |
|
988 } else { |
|
989 return (T) Net.getSocketOption(fd, opt); |
|
990 } |
|
991 } else { |
|
992 // option does not need special handling |
|
993 return (T) Net.getSocketOption(fd, opt); |
|
994 } |
|
995 } |
|
996 } |
|
997 |
|
998 private boolean booleanValue(Object value, String desc) throws SocketException { |
|
999 if (!(value instanceof Boolean)) |
|
1000 throw new SocketException("Bad value for " + desc); |
|
1001 return (boolean) value; |
|
1002 } |
|
1003 |
|
1004 private int intValue(Object value, String desc) throws SocketException { |
|
1005 if (!(value instanceof Integer)) |
|
1006 throw new SocketException("Bad value for " + desc); |
|
1007 return (int) value; |
|
1008 } |
|
1009 |
|
1010 @Override |
|
1011 public void setOption(int opt, Object value) throws SocketException { |
|
1012 synchronized (stateLock) { |
|
1013 ensureOpen(); |
|
1014 try { |
|
1015 switch (opt) { |
|
1016 case SO_LINGER: { |
|
1017 // the value is "false" to disable, or linger interval to enable |
|
1018 int i; |
|
1019 if (value instanceof Boolean && ((boolean) value) == false) { |
|
1020 i = -1; |
|
1021 } else { |
|
1022 i = intValue(value, "SO_LINGER"); |
|
1023 } |
|
1024 Net.setSocketOption(fd, StandardSocketOptions.SO_LINGER, i); |
|
1025 break; |
|
1026 } |
|
1027 case SO_TIMEOUT: { |
|
1028 int i = intValue(value, "SO_TIMEOUT"); |
|
1029 if (i < 0) |
|
1030 throw new IllegalArgumentException("timeout < 0"); |
|
1031 timeout = i; |
|
1032 break; |
|
1033 } |
|
1034 case IP_TOS: { |
|
1035 int i = intValue(value, "IP_TOS"); |
|
1036 Net.setSocketOption(fd, family(), StandardSocketOptions.IP_TOS, i); |
|
1037 break; |
|
1038 } |
|
1039 case TCP_NODELAY: { |
|
1040 boolean b = booleanValue(value, "TCP_NODELAY"); |
|
1041 Net.setSocketOption(fd, StandardSocketOptions.TCP_NODELAY, b); |
|
1042 break; |
|
1043 } |
|
1044 case SO_SNDBUF: { |
|
1045 int i = intValue(value, "SO_SNDBUF"); |
|
1046 if (i <= 0) |
|
1047 throw new SocketException("SO_SNDBUF <= 0"); |
|
1048 Net.setSocketOption(fd, StandardSocketOptions.SO_SNDBUF, i); |
|
1049 break; |
|
1050 } |
|
1051 case SO_RCVBUF: { |
|
1052 int i = intValue(value, "SO_RCVBUF"); |
|
1053 if (i <= 0) |
|
1054 throw new SocketException("SO_RCVBUF <= 0"); |
|
1055 Net.setSocketOption(fd, StandardSocketOptions.SO_RCVBUF, i); |
|
1056 break; |
|
1057 } |
|
1058 case SO_KEEPALIVE: { |
|
1059 boolean b = booleanValue(value, "SO_KEEPALIVE"); |
|
1060 Net.setSocketOption(fd, StandardSocketOptions.SO_KEEPALIVE, b); |
|
1061 break; |
|
1062 } |
|
1063 case SO_OOBINLINE: { |
|
1064 boolean b = booleanValue(value, "SO_OOBINLINE"); |
|
1065 Net.setSocketOption(fd, ExtendedSocketOption.SO_OOBINLINE, b); |
|
1066 break; |
|
1067 } |
|
1068 case SO_REUSEADDR: { |
|
1069 boolean b = booleanValue(value, "SO_REUSEADDR"); |
|
1070 if (Net.useExclusiveBind()) { |
|
1071 isReuseAddress = b; |
|
1072 } else { |
|
1073 Net.setSocketOption(fd, StandardSocketOptions.SO_REUSEADDR, b); |
|
1074 } |
|
1075 break; |
|
1076 } |
|
1077 case SO_REUSEPORT: { |
|
1078 if (!Net.isReusePortAvailable()) |
|
1079 throw new SocketException("SO_REUSEPORT not supported"); |
|
1080 boolean b = booleanValue(value, "SO_REUSEPORT"); |
|
1081 Net.setSocketOption(fd, StandardSocketOptions.SO_REUSEPORT, b); |
|
1082 break; |
|
1083 } |
|
1084 default: |
|
1085 throw new SocketException("Unknown option " + opt); |
|
1086 } |
|
1087 } catch (SocketException e) { |
|
1088 throw e; |
|
1089 } catch (IllegalArgumentException | IOException e) { |
|
1090 throw new SocketException(e.getMessage()); |
|
1091 } |
|
1092 } |
|
1093 } |
|
1094 |
|
1095 @Override |
|
1096 public Object getOption(int opt) throws SocketException { |
|
1097 synchronized (stateLock) { |
|
1098 ensureOpen(); |
|
1099 try { |
|
1100 switch (opt) { |
|
1101 case SO_TIMEOUT: |
|
1102 return timeout; |
|
1103 case TCP_NODELAY: |
|
1104 return Net.getSocketOption(fd, StandardSocketOptions.TCP_NODELAY); |
|
1105 case SO_OOBINLINE: |
|
1106 return Net.getSocketOption(fd, ExtendedSocketOption.SO_OOBINLINE); |
|
1107 case SO_LINGER: { |
|
1108 // return "false" when disabled, linger interval when enabled |
|
1109 int i = (int) Net.getSocketOption(fd, StandardSocketOptions.SO_LINGER); |
|
1110 if (i == -1) { |
|
1111 return Boolean.FALSE; |
|
1112 } else { |
|
1113 return i; |
|
1114 } |
|
1115 } |
|
1116 case SO_REUSEADDR: |
|
1117 if (Net.useExclusiveBind()) { |
|
1118 return isReuseAddress; |
|
1119 } else { |
|
1120 return Net.getSocketOption(fd, StandardSocketOptions.SO_REUSEADDR); |
|
1121 } |
|
1122 case SO_BINDADDR: |
|
1123 return Net.localAddress(fd).getAddress(); |
|
1124 case SO_SNDBUF: |
|
1125 return Net.getSocketOption(fd, StandardSocketOptions.SO_SNDBUF); |
|
1126 case SO_RCVBUF: |
|
1127 return Net.getSocketOption(fd, StandardSocketOptions.SO_RCVBUF); |
|
1128 case IP_TOS: |
|
1129 return Net.getSocketOption(fd, family(), StandardSocketOptions.IP_TOS); |
|
1130 case SO_KEEPALIVE: |
|
1131 return Net.getSocketOption(fd, StandardSocketOptions.SO_KEEPALIVE); |
|
1132 case SO_REUSEPORT: |
|
1133 if (!Net.isReusePortAvailable()) |
|
1134 throw new SocketException("SO_REUSEPORT not supported"); |
|
1135 return Net.getSocketOption(fd, StandardSocketOptions.SO_REUSEPORT); |
|
1136 default: |
|
1137 throw new SocketException("Unknown option " + opt); |
|
1138 } |
|
1139 } catch (SocketException e) { |
|
1140 throw e; |
|
1141 } catch (IllegalArgumentException | IOException e) { |
|
1142 throw new SocketException(e.getMessage()); |
|
1143 } |
|
1144 } |
|
1145 } |
|
1146 |
|
1147 @Override |
|
1148 protected void shutdownInput() throws IOException { |
|
1149 synchronized (stateLock) { |
|
1150 ensureOpenAndConnected(); |
|
1151 if (!isInputClosed) { |
|
1152 Net.shutdown(fd, Net.SHUT_RD); |
|
1153 isInputClosed = true; |
|
1154 } |
|
1155 } |
|
1156 } |
|
1157 |
|
1158 @Override |
|
1159 protected void shutdownOutput() throws IOException { |
|
1160 synchronized (stateLock) { |
|
1161 ensureOpenAndConnected(); |
|
1162 if (!isOutputClosed) { |
|
1163 Net.shutdown(fd, Net.SHUT_WR); |
|
1164 isOutputClosed = true; |
|
1165 } |
|
1166 } |
|
1167 } |
|
1168 |
|
1169 @Override |
|
1170 protected boolean supportsUrgentData() { |
|
1171 return true; |
|
1172 } |
|
1173 |
|
1174 @Override |
|
1175 protected void sendUrgentData(int data) throws IOException { |
|
1176 writeLock.lock(); |
|
1177 try { |
|
1178 int n = 0; |
|
1179 FileDescriptor fd = beginWrite(); |
|
1180 try { |
|
1181 do { |
|
1182 n = Net.sendOOB(fd, (byte) data); |
|
1183 } while (n == IOStatus.INTERRUPTED && isOpen()); |
|
1184 if (n == IOStatus.UNAVAILABLE) { |
|
1185 throw new SocketException("No buffer space available"); |
|
1186 } |
|
1187 } finally { |
|
1188 endWrite(n > 0); |
|
1189 } |
|
1190 } finally { |
|
1191 writeLock.unlock(); |
|
1192 } |
|
1193 } |
|
1194 |
|
1195 /** |
|
1196 * A task that closes a SocketImpl's file descriptor. The task runs when the |
|
1197 * SocketImpl is explicitly closed and when the SocketImpl becomes phantom |
|
1198 * reachable. |
|
1199 */ |
|
1200 private static class FileDescriptorCloser implements Runnable { |
|
1201 private static final VarHandle CLOSED; |
|
1202 static { |
|
1203 try { |
|
1204 MethodHandles.Lookup l = MethodHandles.lookup(); |
|
1205 CLOSED = l.findVarHandle(FileDescriptorCloser.class, |
|
1206 "closed", |
|
1207 boolean.class); |
|
1208 } catch (Exception e) { |
|
1209 throw new InternalError(e); |
|
1210 } |
|
1211 } |
|
1212 |
|
1213 private final FileDescriptor fd; |
|
1214 private final boolean stream; |
|
1215 private volatile boolean closed; |
|
1216 |
|
1217 FileDescriptorCloser(FileDescriptor fd, boolean stream) { |
|
1218 this.fd = fd; |
|
1219 this.stream = stream; |
|
1220 } |
|
1221 |
|
1222 static FileDescriptorCloser create(NioSocketImpl impl) { |
|
1223 assert Thread.holdsLock(impl.stateLock); |
|
1224 var closer = new FileDescriptorCloser(impl.fd, impl.stream); |
|
1225 CleanerFactory.cleaner().register(impl, closer); |
|
1226 return closer; |
|
1227 } |
|
1228 |
|
1229 @Override |
|
1230 public void run() { |
|
1231 if (CLOSED.compareAndSet(this, false, true)) { |
|
1232 try { |
|
1233 nd.close(fd); |
|
1234 } catch (IOException ioe) { |
|
1235 throw new UncheckedIOException(ioe); |
|
1236 } finally { |
|
1237 if (!stream) { |
|
1238 // decrement |
|
1239 ResourceManager.afterUdpClose(); |
|
1240 } |
|
1241 } |
|
1242 } |
|
1243 } |
|
1244 } |
|
1245 |
|
1246 /** |
|
1247 * Attempts to acquire the given lock within the given waiting time. |
|
1248 * @return the remaining time in nanoseconds when the lock is acquired, zero |
|
1249 * or less if the lock was not acquired before the timeout expired |
|
1250 */ |
|
1251 private static long tryLock(ReentrantLock lock, long timeout, TimeUnit unit) { |
|
1252 assert timeout > 0; |
|
1253 boolean interrupted = false; |
|
1254 long nanos = NANOSECONDS.convert(timeout, unit); |
|
1255 long remainingNanos = nanos; |
|
1256 long startNanos = System.nanoTime(); |
|
1257 boolean acquired = false; |
|
1258 while (!acquired && (remainingNanos > 0)) { |
|
1259 try { |
|
1260 acquired = lock.tryLock(remainingNanos, NANOSECONDS); |
|
1261 } catch (InterruptedException e) { |
|
1262 interrupted = true; |
|
1263 } |
|
1264 remainingNanos = nanos - (System.nanoTime() - startNanos); |
|
1265 } |
|
1266 if (acquired && remainingNanos <= 0L) |
|
1267 lock.unlock(); // release lock if timeout has expired |
|
1268 if (interrupted) |
|
1269 Thread.currentThread().interrupt(); |
|
1270 return remainingNanos; |
|
1271 } |
|
1272 |
|
1273 /** |
|
1274 * Returns the socket protocol family. |
|
1275 */ |
|
1276 private static ProtocolFamily family() { |
|
1277 if (Net.isIPv6Available()) { |
|
1278 return StandardProtocolFamily.INET6; |
|
1279 } else { |
|
1280 return StandardProtocolFamily.INET; |
|
1281 } |
|
1282 } |
|
1283 } |