linux-packaging-mono/external/ikvm/openjdk/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java

690 lines
22 KiB
Java
Raw Normal View History

/*
* Copyright (c) 2008, 2011, 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.nio.channels.*;
import java.util.concurrent.*;
import java.nio.ByteBuffer;
import java.nio.BufferOverflowException;
import java.io.IOException;
import java.io.FileDescriptor;
import cli.System.AsyncCallback;
import cli.System.IAsyncResult;
import cli.System.IO.FileStream;
import cli.System.IO.SeekOrigin;
/**
* Windows implementation of AsynchronousFileChannel using overlapped I/O.
*/
public class WindowsAsynchronousFileChannelImpl
extends AsynchronousFileChannelImpl
implements Groupable
{
// error when EOF is detected asynchronously.
private static final int ERROR_HANDLE_EOF = 38;
// Lazy initialization of default I/O completion port
private static class DefaultIocpHolder {
static final Iocp defaultIocp = defaultIocp();
private static Iocp defaultIocp() {
try {
return new Iocp(null, ThreadPool.createDefault()).start();
} catch (IOException ioe) {
InternalError e = new InternalError();
e.initCause(ioe);
throw e;
}
}
}
// Used for force/truncate/size methods
private static final FileDispatcher nd = new FileDispatcherImpl();
// I/O completion port (group)
private final Iocp iocp;
private final boolean isDefaultIocp;
private WindowsAsynchronousFileChannelImpl(FileDescriptor fdObj,
boolean reading,
boolean writing,
Iocp iocp,
boolean isDefaultIocp)
throws IOException
{
super(fdObj, reading, writing, iocp.executor());
this.iocp = iocp;
this.isDefaultIocp = isDefaultIocp;
}
public static AsynchronousFileChannel open(FileDescriptor fdo,
boolean reading,
boolean writing,
ThreadPool pool)
throws IOException
{
Iocp iocp;
boolean isDefaultIocp;
if (pool == null) {
iocp = DefaultIocpHolder.defaultIocp;
isDefaultIocp = true;
} else {
iocp = new Iocp(null, pool).start();
isDefaultIocp = false;
}
try {
return new
WindowsAsynchronousFileChannelImpl(fdo, reading, writing, iocp, isDefaultIocp);
} catch (IOException x) {
// error binding to port so need to close it (if created for this channel)
if (!isDefaultIocp)
iocp.implClose();
throw x;
}
}
@Override
public void close() throws IOException {
closeLock.writeLock().lock();
try {
if (closed)
return; // already closed
closed = true;
} finally {
closeLock.writeLock().unlock();
}
// invalidate all locks held for this channel
invalidateAllLocks();
// close the file
fdObj.close();
// for the non-default group close the port
if (!isDefaultIocp)
iocp.detachFromThreadPool();
}
@Override
public AsynchronousChannelGroupImpl group() {
return iocp;
}
/**
* Translates Throwable to IOException
*/
private static IOException toIOException(Throwable x) {
if (x instanceof cli.System.ArgumentException) {
return new IOException(x.getMessage());
}
if (x instanceof cli.System.IO.IOException) {
return new IOException(x.getMessage());
}
if (x instanceof IOException) {
if (x instanceof ClosedChannelException)
x = new AsynchronousCloseException();
return (IOException)x;
}
return new IOException(x);
}
@Override
public long size() throws IOException {
try {
begin();
return nd.size(fdObj);
} finally {
end();
}
}
@Override
public AsynchronousFileChannel truncate(long size) throws IOException {
if (size < 0)
throw new IllegalArgumentException("Negative size");
if (!writing)
throw new NonWritableChannelException();
try {
begin();
if (size > nd.size(fdObj))
return this;
nd.truncate(fdObj, size);
} finally {
end();
}
return this;
}
@Override
public void force(boolean metaData) throws IOException {
try {
begin();
nd.force(fdObj, metaData);
} finally {
end();
}
}
// -- file locking --
/**
* Task that initiates locking operation and handles completion result.
*/
private class LockTask<A> implements Runnable, Iocp.ResultHandler {
private final long position;
private final FileLockImpl fli;
private final PendingFuture<FileLock,A> result;
LockTask(long position,
FileLockImpl fli,
PendingFuture<FileLock,A> result)
{
this.position = position;
this.fli = fli;
this.result = result;
}
@Override
public void run() {
FileStream fs = (FileStream)fdObj.getStream();
for (;;) {
try {
begin();
try {
if (false) throw new cli.System.IO.IOException();
fs.Lock(position, fli.size());
result.setResult(fli);
break;
} catch (cli.System.IO.IOException _) {
// we failed to acquire the lock, try again next iteration
}
} catch (Throwable x) {
// lock failed or channel closed
removeFromFileLockTable(fli);
result.setFailure(toIOException(x));
} finally {
end();
}
cli.System.Threading.Thread.Sleep(100);
}
// invoke completion handler
Invoker.invoke(result);
}
@Override
public void completed(int bytesTransferred, boolean canInvokeDirect) {
// release waiters and invoke completion handler
result.setResult(fli);
if (canInvokeDirect) {
Invoker.invokeUnchecked(result);
} else {
Invoker.invoke(result);
}
}
@Override
public void failed(int error, IOException x) {
// lock not acquired so remove from lock table
removeFromFileLockTable(fli);
// release waiters
if (isOpen()) {
result.setFailure(x);
} else {
result.setFailure(new AsynchronousCloseException());
}
Invoker.invoke(result);
}
}
@Override
<A> Future<FileLock> implLock(final long position,
final long size,
final boolean shared,
A attachment,
final CompletionHandler<FileLock,? super A> handler)
{
if (shared && !reading)
throw new NonReadableChannelException();
if (!shared && !writing)
throw new NonWritableChannelException();
// add to lock table
FileLockImpl fli = addToFileLockTable(position, size, shared);
if (fli == null) {
Throwable exc = new ClosedChannelException();
if (handler == null)
return CompletedFuture.withFailure(exc);
Invoker.invoke(this, handler, attachment, null, exc);
return null;
}
// create Future and task that will be invoked to acquire lock
PendingFuture<FileLock,A> result =
new PendingFuture<FileLock,A>(this, handler, attachment);
LockTask lockTask = new LockTask<A>(position, fli, result);
result.setContext(lockTask);
// initiate I/O
if (false) {
lockTask.run();
} else {
boolean executed = false;
try {
Invoker.invokeOnThreadInThreadPool(this, lockTask);
executed = true;
} finally {
if (!executed) {
// rollback
removeFromFileLockTable(fli);
}
}
}
return result;
}
static final int NO_LOCK = -1; // Failed to lock
static final int LOCKED = 0; // Obtained requested lock
@Override
public FileLock tryLock(long position, long size, boolean shared)
throws IOException
{
if (shared && !reading)
throw new NonReadableChannelException();
if (!shared && !writing)
throw new NonWritableChannelException();
// add to lock table
final FileLockImpl fli = addToFileLockTable(position, size, shared);
if (fli == null)
throw new ClosedChannelException();
boolean gotLock = false;
try {
begin();
// try to acquire the lock
int res;
try {
if (false) throw new cli.System.IO.IOException();
FileStream fs = (FileStream)fdObj.getStream();
fs.Lock(position, size);
res = LOCKED;
} catch (cli.System.IO.IOException _) {
res = NO_LOCK;
}
if (res == NO_LOCK)
return null;
gotLock = true;
return fli;
} finally {
if (!gotLock)
removeFromFileLockTable(fli);
end();
}
}
@Override
protected void implRelease(FileLockImpl fli) throws IOException {
try {
if (false) throw new cli.System.IO.IOException();
FileStream fs = (FileStream)fdObj.getStream();
fs.Unlock(fli.position(), fli.size());
} catch (cli.System.IO.IOException x) {
if (!FileDispatcherImpl.NotLockedHack.isErrorNotLocked(x)) {
throw new IOException(x.getMessage());
}
}
}
/**
* Task that initiates read operation and handles completion result.
*/
private class ReadTask<A> implements Runnable, Iocp.ResultHandler, AsyncCallback.Method {
private final ByteBuffer dst;
private final int pos, rem; // buffer position/remaining
private final long position; // file position
private final PendingFuture<Integer,A> result;
// set to dst if direct; otherwise set to substituted direct buffer
private volatile ByteBuffer buf;
ReadTask(ByteBuffer dst,
int pos,
int rem,
long position,
PendingFuture<Integer,A> result)
{
this.dst = dst;
this.pos = pos;
this.rem = rem;
this.position = position;
this.result = result;
}
void updatePosition(int bytesTransferred) {
// if the I/O succeeded then adjust buffer position
if (bytesTransferred > 0) {
if (buf == dst) {
try {
dst.position(pos + bytesTransferred);
} catch (IllegalArgumentException x) {
// someone has changed the position; ignore
}
} else {
// had to substitute direct buffer
buf.position(bytesTransferred).flip();
try {
dst.put(buf);
} catch (BufferOverflowException x) {
// someone has changed the position; ignore
}
}
}
}
@Override
public void run() {
// Substitute an array backed buffer if not
if (dst.hasArray()) {
buf = dst;
} else {
buf = ByteBuffer.allocate(rem);
}
try {
begin();
// initiate read
FileStream fs = (FileStream)fdObj.getStream();
fs.Seek(position, SeekOrigin.wrap(SeekOrigin.Begin));
fs.BeginRead(buf.array(), buf.arrayOffset() + pos, rem, new AsyncCallback(this), null);
return;
} catch (Throwable x) {
// failed to initiate read
result.setFailure(toIOException(x));
} finally {
end();
}
// invoke completion handler
Invoker.invoke(result);
}
public void Invoke(IAsyncResult ar) {
try {
FileStream fs = (FileStream)fdObj.getStream();
completed(fs.EndRead(ar), false);
} catch (Throwable x) {
failed(0, toIOException(x));
}
}
/**
* Executed when the I/O has completed
*/
@Override
public void completed(int bytesTransferred, boolean canInvokeDirect) {
updatePosition(bytesTransferred);
// release waiters and invoke completion handler
result.setResult(bytesTransferred);
if (canInvokeDirect) {
Invoker.invokeUnchecked(result);
} else {
Invoker.invoke(result);
}
}
@Override
public void failed(int error, IOException x) {
// if EOF detected asynchronously then it is reported as error
if (error == ERROR_HANDLE_EOF) {
completed(-1, false);
} else {
// release waiters
if (isOpen()) {
result.setFailure(x);
} else {
result.setFailure(new AsynchronousCloseException());
}
Invoker.invoke(result);
}
}
}
@Override
<A> Future<Integer> implRead(ByteBuffer dst,
long position,
A attachment,
CompletionHandler<Integer,? super A> handler)
{
if (!reading)
throw new NonReadableChannelException();
if (position < 0)
throw new IllegalArgumentException("Negative position");
if (dst.isReadOnly())
throw new IllegalArgumentException("Read-only buffer");
// check if channel is closed
if (!isOpen()) {
Throwable exc = new ClosedChannelException();
if (handler == null)
return CompletedFuture.withFailure(exc);
Invoker.invoke(this, handler, attachment, null, exc);
return null;
}
int pos = dst.position();
int lim = dst.limit();
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
// no space remaining
if (rem == 0) {
if (handler == null)
return CompletedFuture.withResult(0);
Invoker.invoke(this, handler, attachment, 0, null);
return null;
}
// create Future and task that initiates read
PendingFuture<Integer,A> result =
new PendingFuture<Integer,A>(this, handler, attachment);
ReadTask readTask = new ReadTask<A>(dst, pos, rem, position, result);
result.setContext(readTask);
// initiate I/O
if (Iocp.supportsThreadAgnosticIo()) {
readTask.run();
} else {
Invoker.invokeOnThreadInThreadPool(this, readTask);
}
return result;
}
/**
* Task that initiates write operation and handles completion result.
*/
private class WriteTask<A> implements Runnable, Iocp.ResultHandler, AsyncCallback.Method {
private final ByteBuffer src;
private final int pos, rem; // buffer position/remaining
private final long position; // file position
private final PendingFuture<Integer,A> result;
// set to src if direct; otherwise set to substituted direct buffer
private volatile ByteBuffer buf;
WriteTask(ByteBuffer src,
int pos,
int rem,
long position,
PendingFuture<Integer,A> result)
{
this.src = src;
this.pos = pos;
this.rem = rem;
this.position = position;
this.result = result;
}
void updatePosition(int bytesTransferred) {
// if the I/O succeeded then adjust buffer position
if (bytesTransferred > 0) {
try {
src.position(pos + bytesTransferred);
} catch (IllegalArgumentException x) {
// someone has changed the position
}
}
}
@Override
public void run() {
// Substitute an array backed buffer if not
if (src.hasArray()) {
buf = src;
} else {
buf = ByteBuffer.allocate(rem);
buf.put(src);
buf.flip();
// temporarily restore position as we don't know how many bytes
// will be written
src.position(pos);
}
try {
begin();
// initiate the write
FileStream fs = (FileStream)fdObj.getStream();
fs.Seek(position, SeekOrigin.wrap(SeekOrigin.Begin));
fs.BeginWrite(buf.array(), buf.arrayOffset() + pos, rem, new AsyncCallback(this), null);
return;
} catch (Throwable x) {
// failed to initiate read:
result.setFailure(toIOException(x));
} finally {
end();
}
// invoke completion handler
Invoker.invoke(result);
}
public void Invoke(IAsyncResult ar) {
try {
FileStream fs = (FileStream)fdObj.getStream();
fs.EndWrite(ar);
completed(rem, false);
} catch (Throwable x) {
failed(0, toIOException(x));
}
}
/**
* Executed when the I/O has completed
*/
@Override
public void completed(int bytesTransferred, boolean canInvokeDirect) {
updatePosition(bytesTransferred);
// release waiters and invoke completion handler
result.setResult(bytesTransferred);
if (canInvokeDirect) {
Invoker.invokeUnchecked(result);
} else {
Invoker.invoke(result);
}
}
@Override
public void failed(int error, IOException x) {
// release waiters and invoker completion handler
if (isOpen()) {
result.setFailure(x);
} else {
result.setFailure(new AsynchronousCloseException());
}
Invoker.invoke(result);
}
}
<A> Future<Integer> implWrite(ByteBuffer src,
long position,
A attachment,
CompletionHandler<Integer,? super A> handler)
{
if (!writing)
throw new NonWritableChannelException();
if (position < 0)
throw new IllegalArgumentException("Negative position");
// check if channel is closed
if (!isOpen()) {
Throwable exc = new ClosedChannelException();
if (handler == null)
return CompletedFuture.withFailure(exc);
Invoker.invoke(this, handler, attachment, null, exc);
return null;
}
int pos = src.position();
int lim = src.limit();
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
// nothing to write
if (rem == 0) {
if (handler == null)
return CompletedFuture.withResult(0);
Invoker.invoke(this, handler, attachment, 0, null);
return null;
}
// create Future and task to initiate write
PendingFuture<Integer,A> result =
new PendingFuture<Integer,A>(this, handler, attachment);
WriteTask writeTask = new WriteTask<A>(src, pos, rem, position, result);
result.setContext(writeTask);
// initiate I/O
if (Iocp.supportsThreadAgnosticIo()) {
writeTask.run();
} else {
Invoker.invokeOnThreadInThreadPool(this, writeTask);
}
return result;
}
}