/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* An implementation of SocketChannels
*/
class SocketChannelImpl
extends SocketChannel
implements SelChImpl
{
// Used to make native read and write calls
// Our file descriptor object
// even after the value in the file descriptor object has been set to -1
private final int fdVal;
// IDs of native threads doing reads and writes, for signalling
// Lock held by current reading or connecting thread
// Lock held by current writing or connecting thread
// Lock held by any thread that modifies the state fields declared below
// DO NOT invoke a blocking I/O operation while holding this lock!
// -- The following fields are protected by stateLock
// set true when exclusive binding is on and SO_REUSEADDR is emulated
private boolean isReuseAddress;
// State, increases monotonically
// Binding
private boolean isInputOpen = true;
private boolean isOutputOpen = true;
private boolean readyToConnect = false;
// Socket adaptor, created on demand
// -- End of fields protected by stateLock
// Constructor for normal connecting sockets
//
super(sp);
this.state = ST_UNCONNECTED;
}
boolean bound)
throws IOException
{
super(sp);
this.state = ST_UNCONNECTED;
if (bound)
}
// Constructor for sockets obtained from server sockets
//
throws IOException
{
super(sp);
this.state = ST_CONNECTED;
this.remoteAddress = remote;
}
synchronized (stateLock) {
return socket;
}
}
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
}
}
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
return remoteAddress;
}
}
throws IOException
{
throw new NullPointerException();
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
// special handling for IP_TOS: no-op when IPv6
if (!Net.isIPv6Available())
return this;
{
// SO_REUSEADDR emulated when using exclusive bind
return this;
}
// no options that require special handling
return this;
}
}
@SuppressWarnings("unchecked")
throws IOException
{
throw new NullPointerException();
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
{
// SO_REUSEADDR emulated when using exclusive bind
}
// special handling for IP_TOS: always return 0 when IPv6
}
// no options that require special handling
}
}
private static class DefaultOptionsHolder {
// additional options required by socket adaptor
}
}
return DefaultOptionsHolder.defaultOptions;
}
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
if (!isConnected())
throw new NotYetConnectedException();
if (!isInputOpen)
return false;
else
return true;
}
}
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
if (!isOutputOpen)
throw new ClosedChannelException();
if (!isConnected())
throw new NotYetConnectedException();
}
}
synchronized (stateLock) {
readerThread = 0;
if (state == ST_KILLPENDING)
kill();
}
}
synchronized (stateLock) {
writerThread = 0;
if (state == ST_KILLPENDING)
kill();
}
}
throw new NullPointerException();
synchronized (readLock) {
if (!ensureReadOpen())
return -1;
if (isBlocking()) {
}
int n = 0;
try {
// Set up the interruption machinery; see
// AbstractInterruptibleChannel for details
//
begin();
synchronized (stateLock) {
if (!isOpen()) {
// Either the current thread is already interrupted, so
// begin() closed the channel, or another thread closed the
// channel since we checked it a few bytecodes ago. In
// either case the value returned here is irrelevant since
// the invocation of end() in the finally block will throw
// an appropriate exception.
//
return 0;
}
// Save this thread so that it can be signalled on those
// platforms that require it
//
}
// Between the previous test of isOpen() and the return of the
// IOUtil.read invocation below, this channel might be closed
// or this thread might be interrupted. We rely upon the
// implicit synchronization point in the kernel read() call to
// make sure that the right thing happens. In either case the
// implCloseSelectableChannel method is ultimately invoked in
// some other thread, so there are three possibilities:
//
// - implCloseSelectableChannel() invokes nd.preClose()
// before this thread invokes read(), in which case the
// read returns immediately with either EOF or an error,
// the latter of which will cause an IOException to be
// thrown.
//
// - implCloseSelectableChannel() invokes nd.preClose() after
// this thread is blocked in read(). On some operating
// systems (e.g., Solaris and Windows) this causes the read
// to return immediately with either EOF or an error
// indication.
//
// - implCloseSelectableChannel() invokes nd.preClose() after
// this thread is blocked in read() but the operating
// system (e.g., Linux) doesn't support preemptive close,
// so implCloseSelectableChannel() proceeds to signal this
// thread, thereby causing the read to return immediately
// with IOStatus.INTERRUPTED.
//
// In all three cases the invocation of end() in the finally
// clause will notice that the channel has been closed and
// throw an appropriate exception (AsynchronousCloseException
// or ClosedByInterruptException) if necessary.
//
// *There is A fourth possibility. implCloseSelectableChannel()
// moves on to nd.close() in kill(), which does a real close.
// Then a third thread accepts a new connection, opens file or
// whatever that causes the released "fd" to be recycled. All
// above happens just between our last isOpen() check and the
// next kernel read reached, with the recycled "fd". The solution
// to the reader or writer thread. (the preClose() still happens
// so the connection gets cut off as usual).
//
// For socket channels there is the additional wrinkle that
// asynchronous shutdown works much like asynchronous close,
// except that the channel is shutdown rather than completely
// closed. This is analogous to the first two cases above,
// except that the shutdown operation plays the role of
// nd.preClose().
for (;;) {
// The system call was interrupted but the channel
// is still open, so retry
continue;
}
}
} finally {
readerCleanup(); // Clear reader thread
if (isBlocking()) {
}
// The end method, which is defined in our superclass
// AbstractInterruptibleChannel, resets the interruption
// machinery. If its argument is true then it returns
// normally; otherwise it checks the interrupt and open state
// of this channel and throws an appropriate exception if
// necessary.
//
// So, if we actually managed to do any I/O in the above try
// block then we pass true to the end method. We also pass
// true if the channel was in non-blocking mode when the I/O
// operation was initiated but no data could be transferred;
// this prevents spurious exceptions from being thrown in the
// rare event that a channel is closed or a thread is
// interrupted at the exact moment that a non-blocking I/O
// request is made.
//
// Extra case for socket channels: Asynchronous shutdown
//
synchronized (stateLock) {
if ((n <= 0) && (!isInputOpen))
}
}
}
}
throws IOException
{
throw new IndexOutOfBoundsException();
synchronized (readLock) {
if (!ensureReadOpen())
return -1;
long n = 0;
if (isBlocking()) {
}
try {
begin();
synchronized (stateLock) {
if (!isOpen())
return 0;
}
for (;;) {
continue;
}
} finally {
if (isBlocking()) {
}
synchronized (stateLock) {
if ((n <= 0) && (!isInputOpen))
}
}
}
}
throw new NullPointerException();
synchronized (writeLock) {
int n = 0;
try {
begin();
synchronized (stateLock) {
if (!isOpen())
return 0;
}
for (;;) {
continue;
}
} finally {
synchronized (stateLock) {
if ((n <= 0) && (!isOutputOpen))
throw new AsynchronousCloseException();
}
}
}
}
throws IOException
{
throw new IndexOutOfBoundsException();
synchronized (writeLock) {
long n = 0;
try {
begin();
synchronized (stateLock) {
if (!isOpen())
return 0;
}
for (;;) {
continue;
}
} finally {
synchronized (stateLock) {
if ((n <= 0) && (!isOutputOpen))
throw new AsynchronousCloseException();
}
}
}
}
// package-private
synchronized (writeLock) {
int n = 0;
try {
begin();
synchronized (stateLock) {
if (!isOpen())
return 0;
}
for (;;) {
n = sendOutOfBandData(fd, b);
continue;
}
} finally {
synchronized (stateLock) {
if ((n <= 0) && (!isOutputOpen))
throw new AsynchronousCloseException();
}
}
}
}
}
synchronized (stateLock) {
return localAddress;
}
}
synchronized (stateLock) {
return remoteAddress;
}
}
synchronized (readLock) {
synchronized (writeLock) {
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
if (state == ST_PENDING)
throw new ConnectionPendingException();
if (localAddress != null)
throw new AlreadyBoundException();
}
}
}
return this;
}
public boolean isConnected() {
synchronized (stateLock) {
return (state == ST_CONNECTED);
}
}
public boolean isConnectionPending() {
synchronized (stateLock) {
return (state == ST_PENDING);
}
}
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
if (state == ST_CONNECTED)
throw new AlreadyConnectedException();
if (state == ST_PENDING)
throw new ConnectionPendingException();
}
}
int localPort = 0;
synchronized (readLock) {
synchronized (writeLock) {
synchronized (blockingLock()) {
int n = 0;
try {
try {
begin();
synchronized (stateLock) {
if (!isOpen()) {
return false;
}
// notify hook only if unbound
if (localAddress == null) {
isa.getAddress(),
}
}
for (;;) {
if (ia.isAnyLocalAddress())
ia,
if ( (n == IOStatus.INTERRUPTED)
&& isOpen())
continue;
break;
}
} finally {
}
} catch (IOException x) {
// If an exception was thrown, close the channel after
// invoking end() so as to avoid bogus
// AsynchronousCloseExceptions
close();
throw x;
}
synchronized (stateLock) {
remoteAddress = isa;
if (n > 0) {
// Connection succeeded; disallow further
// invocation
if (isOpen())
return true;
}
// If nonblocking and no exception then connection
// pending; disallow another invocation
if (!isBlocking())
state = ST_PENDING;
else
assert false;
}
}
return false;
}
}
}
synchronized (readLock) {
synchronized (writeLock) {
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
if (state == ST_CONNECTED)
return true;
if (state != ST_PENDING)
throw new NoConnectionPendingException();
}
int n = 0;
try {
try {
begin();
synchronized (blockingLock()) {
synchronized (stateLock) {
if (!isOpen()) {
return false;
}
}
if (!isBlocking()) {
for (;;) {
n = checkConnect(fd, false,
if ( (n == IOStatus.INTERRUPTED)
&& isOpen())
continue;
break;
}
} else {
for (;;) {
n = checkConnect(fd, true,
if (n == 0) {
// Loop in case of
// spurious notifications
continue;
}
if ( (n == IOStatus.INTERRUPTED)
&& isOpen())
continue;
break;
}
}
}
} finally {
synchronized (stateLock) {
readerThread = 0;
if (state == ST_KILLPENDING) {
kill();
// poll()/getsockopt() does not report
// error (throws exception, with n = 0)
// on Linux platform after dup2 and
// signal-wakeup. Force n to 0 so the
// end() can throw appropriate exception
n = 0;
}
}
}
} catch (IOException x) {
// If an exception was thrown, close the channel after
// invoking end() so as to avoid bogus
// AsynchronousCloseExceptions
close();
throw x;
}
if (n > 0) {
synchronized (stateLock) {
if (isOpen())
}
return true;
}
return false;
}
}
}
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
if (!isConnected())
throw new NotYetConnectedException();
if (isInputOpen) {
if (readerThread != 0)
isInputOpen = false;
}
return this;
}
}
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
if (!isConnected())
throw new NotYetConnectedException();
if (isOutputOpen) {
if (writerThread != 0)
isOutputOpen = false;
}
return this;
}
}
public boolean isInputOpen() {
synchronized (stateLock) {
return isInputOpen;
}
}
public boolean isOutputOpen() {
synchronized (stateLock) {
return isOutputOpen;
}
}
// AbstractInterruptibleChannel synchronizes invocations of this method
// using AbstractInterruptibleChannel.closeLock, and also ensures that this
// method is only ever invoked once. Before we get to this method, isOpen
// (which is volatile) will have been set to false.
//
synchronized (stateLock) {
isInputOpen = false;
isOutputOpen = false;
// Close the underlying file descriptor and dup it to a known fd
// that's already closed. This prevents other operations on this
// channel from using the old fd, which might be recycled in the
// meantime and allocated to an entirely different channel.
//
// Signal native threads, if needed. If a target thread is not
// currently blocked in an I/O operation then no harm is done since
// the signal handler doesn't actually do anything.
//
if (readerThread != 0)
if (writerThread != 0)
// If this channel is not registered then it's safe to close the fd
// immediately since we know at this point that no thread is
// blocked in an I/O operation upon the channel and, since the
// channel is marked closed, no thread will start another such
// operation. If this channel is registered then we don't close
// the fd since it might be in use by a selector. In that case
// closing this channel caused its keys to be cancelled, so the
// last selector to deregister a key for this channel will invoke
// kill() to close the fd.
//
if (!isRegistered())
kill();
}
}
synchronized (stateLock) {
return;
if (state == ST_UNINITIALIZED) {
return;
}
assert !isOpen() && !isRegistered();
// Postpone the kill if there is a waiting reader
// or writer thread. See the comments in read() for
// more detailed explanation.
} else {
}
}
}
/**
* Translates native poll revent ops into a ready operation ops
*/
int newOps = initialOps;
// This should only happen if this channel is pre-closed while a
// selection operation is in progress
// ## Throw an error if this channel has not been pre-closed
return false;
}
// No need to poll again in checkConnect,
// the error will be detected there
readyToConnect = true;
}
(state == ST_CONNECTED))
readyToConnect = true;
}
(state == ST_CONNECTED))
}
}
}
/**
* Translates an interest operation set into a native poll event set
*/
int newOps = 0;
}
return fd;
}
public int getFDVal() {
return fdVal;
}
if (!isOpen())
else {
synchronized (stateLock) {
switch (state) {
case ST_UNCONNECTED:
break;
case ST_PENDING:
break;
case ST_CONNECTED:
if (!isInputOpen)
if (!isOutputOpen)
break;
}
}
if (remoteAddress() != null) {
}
}
}
}
// -- Native methods --
throws IOException;
throws IOException;
static {
nd = new SocketDispatcher();
}
}