8198562: (ch) Separate blocking and non-blocking code paths (part 1)

8198754: (ch) Separate blocking and non-blocking code paths (part 2)

Reviewed-by: bpb
This commit is contained in:
Alan Bateman 2018-02-28 09:54:38 +00:00
parent 3918ed17a5
commit 13dd8888d2
16 changed files with 1645 additions and 1279 deletions

View File

@ -2080,8 +2080,8 @@ public final class System {
E[] getEnumConstantsShared(Class<E> klass) {
return klass.getEnumConstantsShared();
}
public void blockedOn(Thread t, Interruptible b) {
t.blockedOn(b);
public void blockedOn(Interruptible b) {
Thread.blockedOn(b);
}
public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) {
Shutdown.add(slot, registerShutdownInProgress, hook);

View File

@ -231,9 +231,10 @@ class Thread implements Runnable {
/* Set the blocker field; invoked via jdk.internal.misc.SharedSecrets
* from java.nio code
*/
void blockedOn(Interruptible b) {
synchronized (blockerLock) {
blocker = b;
static void blockedOn(Interruptible b) {
Thread me = Thread.currentThread();
synchronized (me.blockerLock) {
me.blocker = b;
}
}
@ -1006,18 +1007,22 @@ class Thread implements Runnable {
* @spec JSR-51
*/
public void interrupt() {
if (this != Thread.currentThread())
Thread me = Thread.currentThread();
if (this != me)
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
// set interrupt status
interrupt0();
// thread may be blocked in an I/O operation
if (this != me && blocker != null) {
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
b.interrupt(this);
}
}
}
interrupt0();
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -205,6 +205,6 @@ public abstract class AbstractInterruptibleChannel
// -- jdk.internal.misc.SharedSecrets --
static void blockedOn(Interruptible intr) { // package-private
SharedSecrets.getJavaLangAccess().blockedOn(Thread.currentThread(), intr);
SharedSecrets.getJavaLangAccess().blockedOn(intr);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -100,9 +100,9 @@ public interface JavaLangAccess {
<E extends Enum<E>> E[] getEnumConstantsShared(Class<E> klass);
/**
* Set thread's blocker field.
* Set current thread's blocker field.
*/
void blockedOn(Thread t, Interruptible b);
void blockedOn(Interruptible b);
/**
* Registers a shutdown hook.

View File

@ -41,6 +41,7 @@ import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.DatagramChannel;
import java.nio.channels.IllegalBlockingModeException;
import java.util.Objects;
// Make a datagram-socket channel look like a datagram socket.
@ -53,7 +54,6 @@ import java.nio.channels.IllegalBlockingModeException;
public class DatagramSocketAdaptor
extends DatagramSocket
{
// The channel being adapted
private final DatagramChannelImpl dc;
@ -63,7 +63,7 @@ public class DatagramSocketAdaptor
// ## super will create a useless impl
private DatagramSocketAdaptor(DatagramChannelImpl dc) throws IOException {
// Invoke the DatagramSocketAdaptor(SocketAddress) constructor,
// passing a dummy DatagramSocketImpl object to aovid any native
// passing a dummy DatagramSocketImpl object to avoid any native
// resource allocation in super class and invoking our bind method
// before the dc field is initialized.
super(dummyDatagramSocket);
@ -87,10 +87,10 @@ public class DatagramSocketAdaptor
throw new IllegalArgumentException("connect: " + port);
if (remote == null)
throw new IllegalArgumentException("connect: null address");
if (isClosed())
return;
try {
dc.connect(remote);
} catch (ClosedChannelException e) {
// ignore
} catch (Exception x) {
Net.translateToSocketException(x);
}
@ -115,8 +115,7 @@ public class DatagramSocketAdaptor
}
public void connect(SocketAddress remote) throws SocketException {
if (remote == null)
throw new IllegalArgumentException("Address can't be null");
Objects.requireNonNull(remote, "Address can't be null");
connectInternal(remote);
}
@ -137,15 +136,13 @@ public class DatagramSocketAdaptor
}
public InetAddress getInetAddress() {
return (isConnected()
? Net.asInetSocketAddress(dc.remoteAddress()).getAddress()
: null);
InetSocketAddress remote = dc.remoteAddress();
return (remote != null) ? remote.getAddress() : null;
}
public int getPort() {
return (isConnected()
? Net.asInetSocketAddress(dc.remoteAddress()).getPort()
: -1);
InetSocketAddress remote = dc.remoteAddress();
return (remote != null) ? remote.getPort() : -1;
}
public void send(DatagramPacket p) throws IOException {
@ -161,8 +158,7 @@ public class DatagramSocketAdaptor
if (p.getAddress() == null) {
// Legacy DatagramSocket will send in this case
// and set address and port of the packet
InetSocketAddress isa = (InetSocketAddress)
dc.remoteAddress();
InetSocketAddress isa = dc.remoteAddress();
p.setPort(isa.getPort());
p.setAddress(isa.getAddress());
dc.write(bb);
@ -181,36 +177,24 @@ public class DatagramSocketAdaptor
}
}
// Must hold dc.blockingLock()
//
private SocketAddress receive(ByteBuffer bb) throws IOException {
if (timeout == 0) {
return dc.receive(bb);
}
assert Thread.holdsLock(dc.blockingLock()) && dc.isBlocking();
dc.configureBlocking(false);
try {
SocketAddress sender;
if ((sender = dc.receive(bb)) != null)
return sender;
long to = timeout;
long to = this.timeout;
if (to == 0) {
return dc.receive(bb);
} else {
for (;;) {
if (!dc.isOpen())
throw new ClosedChannelException();
throw new ClosedChannelException();
long st = System.currentTimeMillis();
int result = dc.poll(Net.POLLIN, to);
if (result > 0 && ((result & Net.POLLIN) != 0)) {
if ((sender = dc.receive(bb)) != null)
return sender;
if (dc.pollRead(to)) {
return dc.receive(bb);
}
to -= System.currentTimeMillis() - st;
if (to <= 0)
throw new SocketTimeoutException();
}
} finally {
try {
dc.configureBlocking(true);
} catch (ClosedChannelException e) { }
}
}
@ -236,10 +220,10 @@ public class DatagramSocketAdaptor
public InetAddress getLocalAddress() {
if (isClosed())
return null;
SocketAddress local = dc.localAddress();
InetSocketAddress local = dc.localAddress();
if (local == null)
local = new InetSocketAddress(0);
InetAddress result = ((InetSocketAddress)local).getAddress();
InetAddress result = local.getAddress();
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
try {
@ -255,9 +239,9 @@ public class DatagramSocketAdaptor
if (isClosed())
return -1;
try {
SocketAddress local = dc.getLocalAddress();
InetSocketAddress local = dc.localAddress();
if (local != null) {
return ((InetSocketAddress)local).getPort();
return local.getPort();
}
} catch (Exception x) {
}

View File

@ -55,8 +55,7 @@ public class IOUtil {
throws IOException
{
if (src instanceof DirectBuffer) {
return writeFromNativeBuffer(fd, src, position,
directIO, alignment, nd);
return writeFromNativeBuffer(fd, src, position, directIO, alignment, nd);
}
// Substitute a native buffer
@ -77,8 +76,7 @@ public class IOUtil {
// Do not update src until we see how many bytes were written
src.position(pos);
int n = writeFromNativeBuffer(fd, bb, position,
directIO, alignment, nd);
int n = writeFromNativeBuffer(fd, bb, position, directIO, alignment, nd);
if (n > 0) {
// now update src
src.position(pos + n);
@ -232,8 +230,7 @@ public class IOUtil {
if (dst.isReadOnly())
throw new IllegalArgumentException("Read-only buffer");
if (dst instanceof DirectBuffer)
return readIntoNativeBuffer(fd, dst, position,
directIO, alignment, nd);
return readIntoNativeBuffer(fd, dst, position, directIO, alignment, nd);
// Substitute a native buffer
ByteBuffer bb;
@ -245,8 +242,7 @@ public class IOUtil {
bb = Util.getTemporaryDirectBuffer(rem);
}
try {
int n = readIntoNativeBuffer(fd, bb, position,
directIO, alignment,nd);
int n = readIntoNativeBuffer(fd, bb, position, directIO, alignment,nd);
bb.flip();
if (n > 0)
dst.put(bb);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,10 +25,11 @@
package sun.nio.ch;
import java.nio.channels.*;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.io.IOException;
import java.nio.channels.MembershipKey;
import java.nio.channels.MulticastChannel;
import java.util.HashSet;
/**
@ -46,7 +47,7 @@ class MembershipKeyImpl
private volatile boolean invalid;
// lock used when creating or accessing blockedSet
private Object stateLock = new Object();
private final Object stateLock = new Object();
// set of source addresses that are blocked
private HashSet<InetAddress> blockedSet;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -25,12 +25,30 @@
package sun.nio.ch;
import java.io.*;
import java.net.*;
import java.nio.channels.*;
import java.util.*;
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.ProtocolFamily;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketOption;
import java.net.StandardProtocolFamily;
import java.net.StandardSocketOptions;
import java.net.UnknownHostException;
import java.nio.channels.AlreadyBoundException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.NotYetBoundException;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.UnresolvedAddressException;
import java.nio.channels.UnsupportedAddressTypeException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Enumeration;
import sun.net.ext.ExtendedSocketOptions;
import sun.security.action.GetPropertyAction;
@ -116,6 +134,16 @@ public class Net {
return isa;
}
static InetSocketAddress checkAddress(SocketAddress sa, ProtocolFamily family) {
InetSocketAddress isa = checkAddress(sa);
if (family == StandardProtocolFamily.INET) {
InetAddress addr = isa.getAddress();
if (!(addr instanceof Inet4Address))
throw new UnsupportedAddressTypeException();
}
return isa;
}
static InetSocketAddress asInetSocketAddress(SocketAddress sa) {
if (!(sa instanceof InetSocketAddress))
throw new UnsupportedAddressTypeException();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -29,7 +29,6 @@ import java.nio.channels.Channel;
import java.io.FileDescriptor;
import java.io.IOException;
/**
* An interface that allows translation (and more!).
*
@ -50,7 +49,7 @@ public interface SelChImpl extends Channel {
* contains at least one bit that the previous value did not
* contain
*/
public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk);
boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk);
/**
* Sets the specified ops if present in interestOps. The specified
@ -60,7 +59,7 @@ public interface SelChImpl extends Channel {
* contains at least one bit that the previous value did not
* contain
*/
public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk);
boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk);
void translateAndSetInterestOps(int ops, SelectionKeyImpl sk);

View File

@ -34,7 +34,6 @@ import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.StandardSocketOptions;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.IllegalBlockingModeException;
import java.nio.channels.NotYetBoundException;
import java.nio.channels.ServerSocketChannel;
@ -51,7 +50,6 @@ import java.nio.channels.SocketChannel;
class ServerSocketAdaptor // package-private
extends ServerSocket
{
// The channel being adapted
private final ServerSocketChannelImpl ssc;
@ -67,13 +65,10 @@ class ServerSocketAdaptor // package-private
}
// ## super will create a useless impl
private ServerSocketAdaptor(ServerSocketChannelImpl ssc)
throws IOException
{
private ServerSocketAdaptor(ServerSocketChannelImpl ssc) throws IOException {
this.ssc = ssc;
}
public void bind(SocketAddress local) throws IOException {
bind(local, 50);
}
@ -89,26 +84,31 @@ class ServerSocketAdaptor // package-private
}
public InetAddress getInetAddress() {
if (!ssc.isBound())
InetSocketAddress local = ssc.localAddress();
if (local == null) {
return null;
return Net.getRevealedLocalAddress(ssc.localAddress()).getAddress();
} else {
return Net.getRevealedLocalAddress(local).getAddress();
}
}
public int getLocalPort() {
if (!ssc.isBound())
InetSocketAddress local = ssc.localAddress();
if (local == null) {
return -1;
return Net.asInetSocketAddress(ssc.localAddress()).getPort();
} else {
return local.getPort();
}
}
public Socket accept() throws IOException {
synchronized (ssc.blockingLock()) {
try {
if (!ssc.isBound())
throw new NotYetBoundException();
if (timeout == 0) {
long to = this.timeout;
if (to == 0) {
// for compatibility reasons: accept connection if available
// when configured non-blocking
SocketChannel sc = ssc.accept();
@ -119,28 +119,15 @@ class ServerSocketAdaptor // package-private
if (!ssc.isBlocking())
throw new IllegalBlockingModeException();
ssc.configureBlocking(false);
try {
SocketChannel sc;
if ((sc = ssc.accept()) != null)
return sc.socket();
long to = timeout;
for (;;) {
if (!ssc.isOpen())
throw new ClosedChannelException();
long st = System.currentTimeMillis();
int result = ssc.poll(Net.POLLIN, to);
if (result > 0 && ((sc = ssc.accept()) != null))
return sc.socket();
to -= System.currentTimeMillis() - st;
if (to <= 0)
throw new SocketTimeoutException();
}
} finally {
try {
ssc.configureBlocking(true);
} catch (ClosedChannelException e) { }
for (;;) {
long st = System.currentTimeMillis();
if (ssc.pollAccept(to))
return ssc.accept().socket();
to -= System.currentTimeMillis() - st;
if (to <= 0)
throw new SocketTimeoutException();
}
} catch (Exception x) {
Net.translateException(x);
assert false;
@ -216,5 +203,4 @@ class ServerSocketAdaptor // package-private
return -1; // Never happens
}
}
}

View File

@ -35,6 +35,7 @@ import java.net.SocketOption;
import java.net.StandardProtocolFamily;
import java.net.StandardSocketOptions;
import java.nio.channels.AlreadyBoundException;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.NotYetBoundException;
import java.nio.channels.SelectionKey;
@ -43,6 +44,7 @@ import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
@ -56,7 +58,6 @@ class ServerSocketChannelImpl
extends ServerSocketChannel
implements SelChImpl
{
// Used to make native close and configure calls
private static NativeDispatcher nd;
@ -64,10 +65,7 @@ class ServerSocketChannelImpl
private final FileDescriptor fd;
private final int fdVal;
// ID of native thread currently blocked in this channel, for signalling
private volatile long thread;
// Lock held by thread currently blocked in this channel
// Lock held by thread currently blocked on this channel
private final ReentrantLock acceptLock = new ReentrantLock();
// Lock held by any thread that modifies the state fields declared below
@ -77,10 +75,14 @@ class ServerSocketChannelImpl
// -- The following fields are protected by stateLock
// Channel state, increases monotonically
private static final int ST_UNINITIALIZED = -1;
private static final int ST_INUSE = 0;
private static final int ST_KILLED = 1;
private int state = ST_UNINITIALIZED;
private static final int ST_CLOSING = 1;
private static final int ST_KILLPENDING = 2;
private static final int ST_KILLED = 3;
private int state;
// ID of native thread currently blocked in this channel, for signalling
private long thread;
// Binding
private InetSocketAddress localAddress; // null => unbound
@ -98,22 +100,28 @@ class ServerSocketChannelImpl
super(sp);
this.fd = Net.serverSocket(true);
this.fdVal = IOUtil.fdVal(fd);
this.state = ST_INUSE;
}
ServerSocketChannelImpl(SelectorProvider sp,
FileDescriptor fd,
boolean bound)
ServerSocketChannelImpl(SelectorProvider sp, FileDescriptor fd, boolean bound)
throws IOException
{
super(sp);
this.fd = fd;
this.fdVal = IOUtil.fdVal(fd);
this.state = ST_INUSE;
if (bound)
localAddress = Net.localAddress(fd);
if (bound) {
synchronized (stateLock) {
localAddress = Net.localAddress(fd);
}
}
}
// @throws ClosedChannelException if channel is closed
private void ensureOpen() throws ClosedChannelException {
if (!isOpen())
throw new ClosedChannelException();
}
@Override
public ServerSocket socket() {
synchronized (stateLock) {
if (socket == null)
@ -125,11 +133,10 @@ class ServerSocketChannelImpl
@Override
public SocketAddress getLocalAddress() throws IOException {
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
return localAddress == null ? localAddress
: Net.getRevealedLocalAddress(
Net.asInetSocketAddress(localAddress));
ensureOpen();
return (localAddress == null)
? null
: Net.getRevealedLocalAddress(localAddress);
}
}
@ -137,13 +144,11 @@ class ServerSocketChannelImpl
public <T> ServerSocketChannel setOption(SocketOption<T> name, T value)
throws IOException
{
if (name == null)
throw new NullPointerException();
Objects.requireNonNull(name);
if (!supportedOptions().contains(name))
throw new UnsupportedOperationException("'" + name + "' not supported");
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
ensureOpen();
if (name == StandardSocketOptions.IP_TOS) {
ProtocolFamily family = Net.isIPv6Available() ?
@ -152,9 +157,7 @@ class ServerSocketChannelImpl
return this;
}
if (name == StandardSocketOptions.SO_REUSEADDR &&
Net.useExclusiveBind())
{
if (name == StandardSocketOptions.SO_REUSEADDR && Net.useExclusiveBind()) {
// SO_REUSEADDR emulated when using exclusive bind
isReuseAddress = (Boolean)value;
} else {
@ -170,17 +173,13 @@ class ServerSocketChannelImpl
public <T> T getOption(SocketOption<T> name)
throws IOException
{
if (name == null)
throw new NullPointerException();
Objects.requireNonNull(name);
if (!supportedOptions().contains(name))
throw new UnsupportedOperationException("'" + name + "' not supported");
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
if (name == StandardSocketOptions.SO_REUSEADDR &&
Net.useExclusiveBind())
{
ensureOpen();
if (name == StandardSocketOptions.SO_REUSEADDR && Net.useExclusiveBind()) {
// SO_REUSEADDR emulated when using exclusive bind
return (T)Boolean.valueOf(isReuseAddress);
}
@ -193,7 +192,7 @@ class ServerSocketChannelImpl
static final Set<SocketOption<?>> defaultOptions = defaultOptions();
private static Set<SocketOption<?>> defaultOptions() {
HashSet<SocketOption<?>> set = new HashSet<>(2);
HashSet<SocketOption<?>> set = new HashSet<>();
set.add(StandardSocketOptions.SO_RCVBUF);
set.add(StandardSocketOptions.SO_REUSEADDR);
if (Net.isReusePortAvailable()) {
@ -209,35 +208,23 @@ class ServerSocketChannelImpl
return DefaultOptionsHolder.defaultOptions;
}
public boolean isBound() {
synchronized (stateLock) {
return localAddress != null;
}
}
public InetSocketAddress localAddress() {
synchronized (stateLock) {
return localAddress;
}
}
@Override
public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException {
acceptLock.lock();
try {
if (!isOpen())
throw new ClosedChannelException();
if (isBound())
throw new AlreadyBoundException();
InetSocketAddress isa = (local == null) ? new InetSocketAddress(0) :
Net.checkAddress(local);
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkListen(isa.getPort());
NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
Net.bind(fd, isa.getAddress(), isa.getPort());
Net.listen(fd, backlog < 1 ? 50 : backlog);
synchronized (stateLock) {
ensureOpen();
if (localAddress != null)
throw new AlreadyBoundException();
InetSocketAddress isa = (local == null)
? new InetSocketAddress(0)
: Net.checkAddress(local);
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkListen(isa.getPort());
NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
Net.bind(fd, isa.getAddress(), isa.getPort());
Net.listen(fd, backlog < 1 ? 50 : backlog);
localAddress = Net.localAddress(fd);
}
} finally {
@ -246,47 +233,78 @@ class ServerSocketChannelImpl
return this;
}
/**
* Marks the beginning of an I/O operation that might block.
*
* @throws ClosedChannelException if the channel is closed
* @throws NotYetBoundException if the channel's socket has not been bound yet
*/
private void begin(boolean blocking) throws ClosedChannelException {
if (blocking)
begin(); // set blocker to close channel if interrupted
synchronized (stateLock) {
ensureOpen();
if (localAddress == null)
throw new NotYetBoundException();
if (blocking)
thread = NativeThread.current();
}
}
/**
* Marks the end of an I/O operation that may have blocked.
*
* @throws AsynchronousCloseException if the channel was closed due to this
* thread being interrupted on a blocking I/O operation.
*/
private void end(boolean blocking, boolean completed)
throws AsynchronousCloseException
{
if (blocking) {
synchronized (stateLock) {
thread = 0;
// notify any thread waiting in implCloseSelectableChannel
if (state == ST_CLOSING) {
stateLock.notifyAll();
}
}
end(completed);
}
}
@Override
public SocketChannel accept() throws IOException {
acceptLock.lock();
try {
if (!isOpen())
throw new ClosedChannelException();
if (!isBound())
throw new NotYetBoundException();
SocketChannel sc = null;
int n = 0;
FileDescriptor newfd = new FileDescriptor();
InetSocketAddress[] isaa = new InetSocketAddress[1];
boolean blocking = isBlocking();
try {
begin();
if (!isOpen())
return null;
thread = NativeThread.current();
for (;;) {
begin(blocking);
do {
n = accept(this.fd, newfd, isaa);
if ((n == IOStatus.INTERRUPTED) && isOpen())
continue;
break;
}
} while (n == IOStatus.INTERRUPTED && isOpen());
} finally {
thread = 0;
end(n > 0);
end(blocking, n > 0);
assert IOStatus.check(n);
}
if (n < 1)
return null;
// newly accepted socket is initially in blocking mode
IOUtil.configureBlocking(newfd, true);
InetSocketAddress isa = isaa[0];
sc = new SocketChannelImpl(provider(), newfd, isa);
SocketChannel sc = new SocketChannelImpl(provider(), newfd, isa);
// check permitted to accept connections from the remote address
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
try {
sm.checkAccept(isa.getAddress().getHostAddress(),
isa.getPort());
sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort());
} catch (SecurityException x) {
sc.close();
throw x;
@ -299,33 +317,133 @@ class ServerSocketChannelImpl
}
}
@Override
protected void implConfigureBlocking(boolean block) throws IOException {
IOUtil.configureBlocking(fd, block);
}
protected void implCloseSelectableChannel() throws IOException {
synchronized (stateLock) {
if (state != ST_KILLED)
nd.preClose(fd);
long th = thread;
if (th != 0)
NativeThread.signal(th);
if (!isRegistered())
kill();
acceptLock.lock();
try {
synchronized (stateLock) {
ensureOpen();
IOUtil.configureBlocking(fd, block);
}
} finally {
acceptLock.unlock();
}
}
/**
* Invoked by implCloseChannel to close the channel.
*
* This method waits for outstanding I/O operations to complete. When in
* blocking mode, the socket is pre-closed and the threads in blocking I/O
* operations are signalled to ensure that the outstanding I/O operations
* complete quickly.
*
* The socket is closed by this method when it is not registered with a
* Selector. Note that a channel configured blocking may be registered with
* a Selector. This arises when a key is canceled and the channel configured
* to blocking mode before the key is flushed from the Selector.
*/
@Override
protected void implCloseSelectableChannel() throws IOException {
assert !isOpen();
boolean interrupted = false;
boolean blocking;
// set state to ST_CLOSING
synchronized (stateLock) {
assert state < ST_CLOSING;
state = ST_CLOSING;
blocking = isBlocking();
}
// wait for any outstanding accept to complete
if (blocking) {
synchronized (stateLock) {
assert state == ST_CLOSING;
long th = thread;
if (th != 0) {
nd.preClose(fd);
NativeThread.signal(th);
// wait for accept operation to end
while (thread != 0) {
try {
stateLock.wait();
} catch (InterruptedException e) {
interrupted = true;
}
}
}
}
} else {
// non-blocking mode: wait for accept to complete
acceptLock.lock();
acceptLock.unlock();
}
// set state to ST_KILLPENDING
synchronized (stateLock) {
assert state == ST_CLOSING;
state = ST_KILLPENDING;
}
// close socket if not registered with Selector
if (!isRegistered())
kill();
// restore interrupt status
if (interrupted)
Thread.currentThread().interrupt();
}
@Override
public void kill() throws IOException {
synchronized (stateLock) {
if (state == ST_KILLED)
return;
if (state == ST_UNINITIALIZED) {
if (state == ST_KILLPENDING) {
state = ST_KILLED;
return;
nd.close(fd);
}
assert !isOpen() && !isRegistered();
nd.close(fd);
state = ST_KILLED;
}
}
/**
* Returns true if channel's socket is bound
*/
boolean isBound() {
synchronized (stateLock) {
return localAddress != null;
}
}
/**
* Returns the local address, or null if not bound
*/
InetSocketAddress localAddress() {
synchronized (stateLock) {
return localAddress;
}
}
/**
* Poll this channel's socket for a new connection up to the given timeout.
* @return {@code true} if there is a connection to accept
*/
boolean pollAccept(long timeout) throws IOException {
assert Thread.holdsLock(blockingLock()) && isBlocking();
acceptLock.lock();
try {
boolean polled = false;
try {
begin(true);
int n = Net.poll(fd, Net.POLLIN, timeout);
polled = (n > 0);
} finally {
end(true, polled);
}
return polled;
} finally {
acceptLock.unlock();
}
}
@ -367,31 +485,6 @@ class ServerSocketChannelImpl
return translateReadyOps(ops, 0, sk);
}
// package-private
int poll(int events, long timeout) throws IOException {
assert Thread.holdsLock(blockingLock()) && !isBlocking();
acceptLock.lock();
try {
int n = 0;
try {
begin();
synchronized (stateLock) {
if (!isOpen())
return 0;
thread = NativeThread.current();
}
n = Net.poll(fd, events, timeout);
} finally {
thread = 0;
end(n > 0);
}
return n;
} finally {
acceptLock.unlock();
}
}
/**
* Translates an interest operation set into a native poll event set
*/
@ -421,7 +514,7 @@ class ServerSocketChannelImpl
sb.append("closed");
} else {
synchronized (stateLock) {
InetSocketAddress addr = localAddress();
InetSocketAddress addr = localAddress;
if (addr == null) {
sb.append("unbound");
} else {
@ -438,7 +531,8 @@ class ServerSocketChannelImpl
*
* @implNote Wrap native call to allow instrumentation.
*/
private int accept(FileDescriptor ssfd, FileDescriptor newfd,
private int accept(FileDescriptor ssfd,
FileDescriptor newfd,
InetSocketAddress[] isaa)
throws IOException
{
@ -452,7 +546,8 @@ class ServerSocketChannelImpl
// Returns 1 on success, or IOStatus.UNAVAILABLE (if non-blocking and no
// connections are pending) or IOStatus.INTERRUPTED.
//
private native int accept0(FileDescriptor ssfd, FileDescriptor newfd,
private native int accept0(FileDescriptor ssfd,
FileDescriptor newfd,
InetSocketAddress[] isaa)
throws IOException;

View File

@ -44,16 +44,10 @@ import java.nio.channels.IllegalBlockingModeException;
import java.nio.channels.SocketChannel;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.concurrent.TimeUnit;
import static java.util.concurrent.TimeUnit.*;
// Make a socket channel look like a socket.
//
// The only aspects of java.net.Socket-hood that we don't attempt to emulate
// here are the interrupted-I/O exceptions (which our Solaris implementations
// attempt to support) and the sending of urgent data. Otherwise an adapted
// socket should look enough like a real java.net.Socket to fool most of the
// developers most of the time, right down to the exception message strings.
//
// The methods in this class are defined in exactly the same order as in
// java.net.Socket so as to simplify tracking future changes to that class.
//
@ -61,7 +55,6 @@ import java.util.concurrent.TimeUnit;
class SocketAdaptor
extends Socket
{
// The channel being adapted
private final SocketChannelImpl sc;
@ -102,40 +95,42 @@ class SocketAdaptor
throw new IllegalBlockingModeException();
try {
// no timeout
if (timeout == 0) {
sc.connect(remote);
return;
}
// timed connect
sc.configureBlocking(false);
try {
if (sc.connect(remote))
return;
long timeoutNanos =
TimeUnit.NANOSECONDS.convert(timeout,
TimeUnit.MILLISECONDS);
for (;;) {
if (!sc.isOpen())
throw new ClosedChannelException();
long startTime = System.nanoTime();
int result = sc.poll(Net.POLLCONN, timeout);
if (result > 0 && sc.finishConnect())
break;
timeoutNanos -= System.nanoTime() - startTime;
if (timeoutNanos <= 0) {
try {
sc.close();
} catch (IOException x) { }
throw new SocketTimeoutException();
}
}
} finally {
try {
sc.configureBlocking(true);
} catch (ClosedChannelException e) { }
}
long timeoutNanos = NANOSECONDS.convert(timeout, MILLISECONDS);
long to = timeout;
for (;;) {
long startTime = System.nanoTime();
if (sc.pollConnected(to)) {
boolean connected = sc.finishConnect();
assert connected;
break;
}
timeoutNanos -= System.nanoTime() - startTime;
if (timeoutNanos <= 0) {
try {
sc.close();
} catch (IOException x) { }
throw new SocketTimeoutException();
}
to = MILLISECONDS.convert(timeoutNanos, NANOSECONDS);
}
} catch (Exception x) {
Net.translateException(x, true);
}
@ -152,11 +147,11 @@ class SocketAdaptor
}
public InetAddress getInetAddress() {
SocketAddress remote = sc.remoteAddress();
InetSocketAddress remote = sc.remoteAddress();
if (remote == null) {
return null;
} else {
return ((InetSocketAddress)remote).getAddress();
return remote.getAddress();
}
}
@ -171,20 +166,20 @@ class SocketAdaptor
}
public int getPort() {
SocketAddress remote = sc.remoteAddress();
InetSocketAddress remote = sc.remoteAddress();
if (remote == null) {
return 0;
} else {
return ((InetSocketAddress)remote).getPort();
return remote.getPort();
}
}
public int getLocalPort() {
SocketAddress local = sc.localAddress();
InetSocketAddress local = sc.localAddress();
if (local == null) {
return -1;
} else {
return ((InetSocketAddress)local).getPort();
return local.getPort();
}
}
@ -202,34 +197,22 @@ class SocketAdaptor
if (!sc.isBlocking())
throw new IllegalBlockingModeException();
if (timeout == 0)
// no timeout
long to = SocketAdaptor.this.timeout;
if (to == 0)
return sc.read(bb);
sc.configureBlocking(false);
try {
int n;
if ((n = sc.read(bb)) != 0)
return n;
long timeoutNanos =
TimeUnit.NANOSECONDS.convert(timeout,
TimeUnit.MILLISECONDS);
for (;;) {
if (!sc.isOpen())
throw new ClosedChannelException();
long startTime = System.nanoTime();
int result = sc.poll(Net.POLLIN, timeout);
if (result > 0) {
if ((n = sc.read(bb)) != 0)
return n;
}
timeoutNanos -= System.nanoTime() - startTime;
if (timeoutNanos <= 0)
throw new SocketTimeoutException();
// timed read
long timeoutNanos = NANOSECONDS.convert(to, MILLISECONDS);
for (;;) {
long startTime = System.nanoTime();
if (sc.pollRead(to)) {
return sc.read(bb);
}
} finally {
try {
sc.configureBlocking(true);
} catch (ClosedChannelException e) { }
timeoutNanos -= System.nanoTime() - startTime;
if (timeoutNanos <= 0)
throw new SocketTimeoutException();
to = MILLISECONDS.convert(timeoutNanos, NANOSECONDS);
}
}
}
@ -453,5 +436,4 @@ class SocketAdaptor
public boolean isOutputShutdown() {
return !sc.isOutputOpen();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -28,31 +28,26 @@ package sun.nio.ch;
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.Pipe;
import java.nio.channels.SelectionKey;
import java.nio.channels.spi.SelectorProvider;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantLock;
class SinkChannelImpl
extends Pipe.SinkChannel
implements SelChImpl
{
// Used to make native read and write calls
private static final NativeDispatcher nd = new FileDispatcherImpl();
// The file descriptor associated with this channel
private final FileDescriptor fd;
// fd value needed for dev/poll. This value will remain valid
// even after the value in the file descriptor object has been set to -1
private final int fdVal;
// ID of native thread doing write, for signalling
private volatile long thread;
// Lock held by current writing thread
private final ReentrantLock writeLock = new ReentrantLock();
@ -63,10 +58,14 @@ class SinkChannelImpl
// -- The following fields are protected by stateLock
// Channel state
private static final int ST_UNINITIALIZED = -1;
private static final int ST_INUSE = 0;
private static final int ST_KILLED = 1;
private volatile int state = ST_UNINITIALIZED;
private static final int ST_CLOSING = 1;
private static final int ST_KILLPENDING = 2;
private static final int ST_KILLED = 3;
private int state;
// ID of native thread doing write, for signalling
private long thread;
// -- End of fields protected by stateLock
@ -83,37 +82,86 @@ class SinkChannelImpl
super(sp);
this.fd = fd;
this.fdVal = IOUtil.fdVal(fd);
this.state = ST_INUSE;
}
/**
* Invoked by implCloseChannel to close the channel.
*/
@Override
protected void implCloseSelectableChannel() throws IOException {
assert !isOpen();
boolean interrupted = false;
boolean blocking;
// set state to ST_CLOSING
synchronized (stateLock) {
if (state != ST_KILLED)
nd.preClose(fd);
long th = thread;
if (th != 0)
NativeThread.signal(th);
if (!isRegistered())
kill();
assert state < ST_CLOSING;
state = ST_CLOSING;
blocking = isBlocking();
}
// wait for any outstanding write to complete
if (blocking) {
synchronized (stateLock) {
assert state == ST_CLOSING;
long th = thread;
if (th != 0) {
nd.preClose(fd);
NativeThread.signal(th);
// wait for write operation to end
while (thread != 0) {
try {
stateLock.wait();
} catch (InterruptedException e) {
interrupted = true;
}
}
}
}
} else {
// non-blocking mode: wait for write to complete
writeLock.lock();
writeLock.unlock();
}
// set state to ST_KILLPENDING
synchronized (stateLock) {
assert state == ST_CLOSING;
state = ST_KILLPENDING;
}
// close socket if not registered with Selector
if (!isRegistered())
kill();
// restore interrupt status
if (interrupted)
Thread.currentThread().interrupt();
}
@Override
public void kill() throws IOException {
synchronized (stateLock) {
if (state == ST_KILLED)
return;
if (state == ST_UNINITIALIZED) {
assert thread == 0;
if (state == ST_KILLPENDING) {
state = ST_KILLED;
return;
nd.close(fd);
}
assert !isOpen() && !isRegistered();
nd.close(fd);
state = ST_KILLED;
}
}
@Override
protected void implConfigureBlocking(boolean block) throws IOException {
IOUtil.configureBlocking(fd, block);
writeLock.lock();
try {
synchronized (stateLock) {
IOUtil.configureBlocking(fd, block);
}
} finally {
writeLock.unlock();
}
}
public boolean translateReadyOps(int ops, int initialOps,
@ -153,67 +201,95 @@ class SinkChannelImpl
sk.selector.putEventOps(sk, ops);
}
private void ensureOpen() throws IOException {
if (!isOpen())
throw new ClosedChannelException();
/**
* Marks the beginning of a write operation that might block.
*
* @throws ClosedChannelException if the channel is closed
* @throws NotYetConnectedException if the channel is not yet connected
*/
private void beginWrite(boolean blocking) throws ClosedChannelException {
if (blocking) {
// set hook for Thread.interrupt
begin();
}
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
if (blocking)
thread = NativeThread.current();
}
}
/**
* Marks the end of a write operation that may have blocked.
*
* @throws AsynchronousCloseException if the channel was closed due to this
* thread being interrupted on a blocking write operation.
*/
private void endWrite(boolean blocking, boolean completed)
throws AsynchronousCloseException
{
if (blocking) {
synchronized (stateLock) {
thread = 0;
// notify any thread waiting in implCloseSelectableChannel
if (state == ST_CLOSING) {
stateLock.notifyAll();
}
}
// remove hook for Thread.interrupt
end(completed);
}
}
@Override
public int write(ByteBuffer src) throws IOException {
Objects.requireNonNull(src);
writeLock.lock();
try {
ensureOpen();
boolean blocking = isBlocking();
int n = 0;
try {
begin();
if (!isOpen())
return 0;
thread = NativeThread.current();
beginWrite(blocking);
do {
n = IOUtil.write(fd, src, -1, nd);
} while ((n == IOStatus.INTERRUPTED) && isOpen());
return IOStatus.normalize(n);
} finally {
thread = 0;
end((n > 0) || (n == IOStatus.UNAVAILABLE));
endWrite(blocking, n > 0);
assert IOStatus.check(n);
}
return IOStatus.normalize(n);
} finally {
writeLock.unlock();
}
}
public long write(ByteBuffer[] srcs) throws IOException {
if (srcs == null)
throw new NullPointerException();
@Override
public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
Objects.checkFromIndexSize(offset, length, srcs.length);
writeLock.lock();
try {
ensureOpen();
boolean blocking = isBlocking();
long n = 0;
try {
begin();
if (!isOpen())
return 0;
thread = NativeThread.current();
beginWrite(blocking);
do {
n = IOUtil.write(fd, srcs, nd);
n = IOUtil.write(fd, srcs, offset, length, nd);
} while ((n == IOStatus.INTERRUPTED) && isOpen());
return IOStatus.normalize(n);
} finally {
thread = 0;
end((n > 0) || (n == IOStatus.UNAVAILABLE));
endWrite(blocking, n > 0);
assert IOStatus.check(n);
}
return IOStatus.normalize(n);
} finally {
writeLock.unlock();
}
}
public long write(ByteBuffer[] srcs, int offset, int length)
throws IOException
{
if ((offset < 0) || (length < 0) || (offset > srcs.length - length))
throw new IndexOutOfBoundsException();
return write(Util.subsequence(srcs, offset, length));
@Override
public long write(ByteBuffer[] srcs) throws IOException {
return write(srcs, 0, srcs.length);
}
}

View File

@ -28,31 +28,26 @@ package sun.nio.ch;
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.Pipe;
import java.nio.channels.SelectionKey;
import java.nio.channels.spi.SelectorProvider;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantLock;
class SourceChannelImpl
extends Pipe.SourceChannel
implements SelChImpl
{
// Used to make native read and write calls
private static final NativeDispatcher nd = new FileDispatcherImpl();
// The file descriptor associated with this channel
private final FileDescriptor fd;
// fd value needed for dev/poll. This value will remain valid
// even after the value in the file descriptor object has been set to -1
private final int fdVal;
// ID of native thread doing read, for signalling
private volatile long thread;
// Lock held by current reading thread
private final ReentrantLock readLock = new ReentrantLock();
@ -63,10 +58,14 @@ class SourceChannelImpl
// -- The following fields are protected by stateLock
// Channel state
private static final int ST_UNINITIALIZED = -1;
private static final int ST_INUSE = 0;
private static final int ST_KILLED = 1;
private volatile int state = ST_UNINITIALIZED;
private static final int ST_CLOSING = 1;
private static final int ST_KILLPENDING = 2;
private static final int ST_KILLED = 3;
private int state;
// ID of native thread doing read, for signalling
private long thread;
// -- End of fields protected by stateLock
@ -83,37 +82,86 @@ class SourceChannelImpl
super(sp);
this.fd = fd;
this.fdVal = IOUtil.fdVal(fd);
this.state = ST_INUSE;
}
/**
* Invoked by implCloseChannel to close the channel.
*/
@Override
protected void implCloseSelectableChannel() throws IOException {
assert !isOpen();
boolean interrupted = false;
boolean blocking;
// set state to ST_CLOSING
synchronized (stateLock) {
if (state != ST_KILLED)
nd.preClose(fd);
long th = thread;
if (th != 0)
NativeThread.signal(th);
if (!isRegistered())
kill();
assert state < ST_CLOSING;
state = ST_CLOSING;
blocking = isBlocking();
}
// wait for any outstanding read to complete
if (blocking) {
synchronized (stateLock) {
assert state == ST_CLOSING;
long th = thread;
if (th != 0) {
nd.preClose(fd);
NativeThread.signal(th);
// wait for read operation to end
while (thread != 0) {
try {
stateLock.wait();
} catch (InterruptedException e) {
interrupted = true;
}
}
}
}
} else {
// non-blocking mode: wait for read to complete
readLock.lock();
readLock.unlock();
}
// set state to ST_KILLPENDING
synchronized (stateLock) {
assert state == ST_CLOSING;
state = ST_KILLPENDING;
}
// close socket if not registered with Selector
if (!isRegistered())
kill();
// restore interrupt status
if (interrupted)
Thread.currentThread().interrupt();
}
@Override
public void kill() throws IOException {
synchronized (stateLock) {
if (state == ST_KILLED)
return;
if (state == ST_UNINITIALIZED) {
assert thread == 0;
if (state == ST_KILLPENDING) {
state = ST_KILLED;
return;
nd.close(fd);
}
assert !isOpen() && !isRegistered();
nd.close(fd);
state = ST_KILLED;
}
}
@Override
protected void implConfigureBlocking(boolean block) throws IOException {
IOUtil.configureBlocking(fd, block);
readLock.lock();
try {
synchronized (stateLock) {
IOUtil.configureBlocking(fd, block);
}
} finally {
readLock.unlock();
}
}
public boolean translateReadyOps(int ops, int initialOps,
@ -153,68 +201,95 @@ class SourceChannelImpl
sk.selector.putEventOps(sk, ops);
}
private void ensureOpen() throws IOException {
if (!isOpen())
throw new ClosedChannelException();
/**
* Marks the beginning of a read operation that might block.
*
* @throws ClosedChannelException if the channel is closed
* @throws NotYetConnectedException if the channel is not yet connected
*/
private void beginRead(boolean blocking) throws ClosedChannelException {
if (blocking) {
// set hook for Thread.interrupt
begin();
}
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
if (blocking)
thread = NativeThread.current();
}
}
/**
* Marks the end of a read operation that may have blocked.
*
* @throws AsynchronousCloseException if the channel was closed due to this
* thread being interrupted on a blocking read operation.
*/
private void endRead(boolean blocking, boolean completed)
throws AsynchronousCloseException
{
if (blocking) {
synchronized (stateLock) {
thread = 0;
// notify any thread waiting in implCloseSelectableChannel
if (state == ST_CLOSING) {
stateLock.notifyAll();
}
}
// remove hook for Thread.interrupt
end(completed);
}
}
@Override
public int read(ByteBuffer dst) throws IOException {
Objects.requireNonNull(dst);
readLock.lock();
try {
ensureOpen();
boolean blocking = isBlocking();
int n = 0;
try {
begin();
if (!isOpen())
return 0;
thread = NativeThread.current();
beginRead(blocking);
do {
n = IOUtil.read(fd, dst, -1, nd);
} while ((n == IOStatus.INTERRUPTED) && isOpen());
return IOStatus.normalize(n);
} finally {
thread = 0;
end((n > 0) || (n == IOStatus.UNAVAILABLE));
endRead(blocking, n > 0);
assert IOStatus.check(n);
}
return IOStatus.normalize(n);
} finally {
readLock.unlock();
}
}
public long read(ByteBuffer[] dsts, int offset, int length)
throws IOException
{
if ((offset < 0) || (length < 0) || (offset > dsts.length - length))
throw new IndexOutOfBoundsException();
return read(Util.subsequence(dsts, offset, length));
}
public long read(ByteBuffer[] dsts) throws IOException {
if (dsts == null)
throw new NullPointerException();
@Override
public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
Objects.checkFromIndexSize(offset, length, dsts.length);
readLock.lock();
try {
ensureOpen();
boolean blocking = isBlocking();
long n = 0;
try {
begin();
if (!isOpen())
return 0;
thread = NativeThread.current();
beginRead(blocking);
do {
n = IOUtil.read(fd, dsts, nd);
n = IOUtil.read(fd, dsts, offset, length, nd);
} while ((n == IOStatus.INTERRUPTED) && isOpen());
return IOStatus.normalize(n);
} finally {
thread = 0;
end((n > 0) || (n == IOStatus.UNAVAILABLE));
endRead(blocking, n > 0);
assert IOStatus.check(n);
}
return IOStatus.normalize(n);
} finally {
readLock.unlock();
}
}
@Override
public long read(ByteBuffer[] dsts) throws IOException {
return read(dsts, 0, dsts.length);
}
}