/*
* 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
* 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.
*/
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;
private final int fdVal;
// Lock held by current reading thread
private final ReentrantLock readLock = new ReentrantLock();
// Lock held by any thread that modifies the state fields declared below
// DO NOT invoke a blocking I/O operation while holding this lock!
private final Object stateLock = new Object();
// -- The following fields are protected by stateLock
// Channel state
private static final int ST_INUSE = 0;
private static final int ST_CLOSING = 1;
private static final int ST_CLOSED = 2;
private int state;
// ID of native thread doing read, for signalling
private long thread;
// -- End of fields protected by stateLock
public FileDescriptor getFD() {
return fd;
}
public int getFDVal() {
return fdVal;
}
SourceChannelImpl(SelectorProvider sp, FileDescriptor fd) {
super(sp);
this.fd = fd;
this.fdVal = IOUtil.fdVal(fd);
}
Closes the read end of the pipe if there are no read operation in
progress and the channel is not registered with a Selector.
/**
* Closes the read end of the pipe if there are no read operation in
* progress and the channel is not registered with a Selector.
*/
private boolean tryClose() throws IOException {
assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
if (thread == 0 && !isRegistered()) {
state = ST_CLOSED;
nd.close(fd);
return true;
} else {
return false;
}
}
Invokes tryClose to attempt to close the read end of the pipe.
This method is used for deferred closing by I/O and Selector operations.
/**
* Invokes tryClose to attempt to close the read end of the pipe.
*
* This method is used for deferred closing by I/O and Selector operations.
*/
private void tryFinishClose() {
try {
tryClose();
} catch (IOException ignore) { }
}
Closes this channel when configured in blocking mode.
If there is a read operation in progress then the read-end of the pipe
is pre-closed and the reader is signalled, in which case the final close
is deferred until the reader aborts.
/**
* Closes this channel when configured in blocking mode.
*
* If there is a read operation in progress then the read-end of the pipe
* is pre-closed and the reader is signalled, in which case the final close
* is deferred until the reader aborts.
*/
private void implCloseBlockingMode() throws IOException {
synchronized (stateLock) {
assert state < ST_CLOSING;
state = ST_CLOSING;
if (!tryClose()) {
long th = thread;
if (th != 0) {
nd.preClose(fd);
NativeThread.signal(th);
}
}
}
}
Closes this channel when configured in non-blocking mode.
If the channel is registered with a Selector then the close is deferred
until the channel is flushed from all Selectors.
/**
* Closes this channel when configured in non-blocking mode.
*
* If the channel is registered with a Selector then the close is deferred
* until the channel is flushed from all Selectors.
*/
private void implCloseNonBlockingMode() throws IOException {
synchronized (stateLock) {
assert state < ST_CLOSING;
state = ST_CLOSING;
}
// wait for any read operation to complete before trying to close
readLock.lock();
readLock.unlock();
synchronized (stateLock) {
if (state == ST_CLOSING) {
tryClose();
}
}
}
Invoked by implCloseChannel to close the channel.
/**
* Invoked by implCloseChannel to close the channel.
*/
@Override
protected void implCloseSelectableChannel() throws IOException {
assert !isOpen();
if (isBlocking()) {
implCloseBlockingMode();
} else {
implCloseNonBlockingMode();
}
}
@Override
public void kill() {
synchronized (stateLock) {
assert !isOpen();
if (state == ST_CLOSING) {
tryFinishClose();
}
}
}
@Override
protected void implConfigureBlocking(boolean block) throws IOException {
readLock.lock();
try {
synchronized (stateLock) {
if (!isOpen())
throw new ClosedChannelException();
IOUtil.configureBlocking(fd, block);
}
} finally {
readLock.unlock();
}
}
public boolean translateReadyOps(int ops, int initialOps, SelectionKeyImpl ski) {
int intOps = ski.nioInterestOps();
int oldOps = ski.nioReadyOps();
int newOps = initialOps;
if ((ops & Net.POLLNVAL) != 0)
throw new Error("POLLNVAL detected");
if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) {
newOps = intOps;
ski.nioReadyOps(newOps);
return (newOps & ~oldOps) != 0;
}
if (((ops & Net.POLLIN) != 0) &&
((intOps & SelectionKey.OP_READ) != 0))
newOps |= SelectionKey.OP_READ;
ski.nioReadyOps(newOps);
return (newOps & ~oldOps) != 0;
}
public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl ski) {
return translateReadyOps(ops, ski.nioReadyOps(), ski);
}
public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl ski) {
return translateReadyOps(ops, 0, ski);
}
public int translateInterestOps(int ops) {
int newOps = 0;
if (ops == SelectionKey.OP_READ)
newOps |= Net.POLLIN;
return newOps;
}
Marks the beginning of a read operation that might block.
Throws: - ClosedChannelException – if the channel is closed
- NotYetConnectedException – if the channel is not yet connected
/**
* 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.
/**
* 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;
if (state == ST_CLOSING) {
tryFinishClose();
}
}
// remove hook for Thread.interrupt
end(completed);
}
}
@Override
public int read(ByteBuffer dst) throws IOException {
Objects.requireNonNull(dst);
readLock.lock();
try {
boolean blocking = isBlocking();
int n = 0;
try {
beginRead(blocking);
n = IOUtil.read(fd, dst, -1, nd);
if (blocking) {
while (IOStatus.okayToRetry(n) && isOpen()) {
park(Net.POLLIN);
n = IOUtil.read(fd, dst, -1, nd);
}
}
} finally {
endRead(blocking, n > 0);
assert IOStatus.check(n);
}
return IOStatus.normalize(n);
} finally {
readLock.unlock();
}
}
@Override
public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
Objects.checkFromIndexSize(offset, length, dsts.length);
readLock.lock();
try {
boolean blocking = isBlocking();
long n = 0;
try {
beginRead(blocking);
n = IOUtil.read(fd, dsts, offset, length, nd);
if (blocking) {
while (IOStatus.okayToRetry(n) && isOpen()) {
park(Net.POLLIN);
n = IOUtil.read(fd, dsts, offset, length, nd);
}
}
} finally {
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);
}
}