397 lines
14 KiB
Java
397 lines
14 KiB
Java
![]() |
/*
|
||
|
* Copyright (c) 2008, 2013, 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.security.AccessController;
|
||
|
import java.security.PrivilegedAction;
|
||
|
import java.io.FileDescriptor;
|
||
|
import java.io.IOException;
|
||
|
|
||
|
/**
|
||
|
* "Portable" implementation of AsynchronousFileChannel for use on operating
|
||
|
* systems that don't support asynchronous file I/O.
|
||
|
*/
|
||
|
|
||
|
public class SimpleAsynchronousFileChannelImpl
|
||
|
extends AsynchronousFileChannelImpl
|
||
|
{
|
||
|
// lazy initialization of default thread pool for file I/O
|
||
|
private static class DefaultExecutorHolder {
|
||
|
static final ExecutorService defaultExecutor =
|
||
|
ThreadPool.createDefault().executor();
|
||
|
}
|
||
|
|
||
|
// Used to make native read and write calls
|
||
|
private static final FileDispatcher nd = new FileDispatcherImpl();
|
||
|
|
||
|
// Thread-safe set of IDs of native threads, for signalling
|
||
|
private final NativeThreadSet threads = new NativeThreadSet(2);
|
||
|
|
||
|
|
||
|
SimpleAsynchronousFileChannelImpl(FileDescriptor fdObj,
|
||
|
boolean reading,
|
||
|
boolean writing,
|
||
|
ExecutorService executor)
|
||
|
{
|
||
|
super(fdObj, reading, writing, executor);
|
||
|
}
|
||
|
|
||
|
public static AsynchronousFileChannel open(FileDescriptor fdo,
|
||
|
boolean reading,
|
||
|
boolean writing,
|
||
|
ThreadPool pool)
|
||
|
{
|
||
|
// Executor is either default or based on pool parameters
|
||
|
ExecutorService executor = (pool == null) ?
|
||
|
DefaultExecutorHolder.defaultExecutor : pool.executor();
|
||
|
return new SimpleAsynchronousFileChannelImpl(fdo, reading, writing, executor);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void close() throws IOException {
|
||
|
// mark channel as closed
|
||
|
synchronized (fdObj) {
|
||
|
if (closed)
|
||
|
return; // already closed
|
||
|
closed = true;
|
||
|
// from this point on, if another thread invokes the begin() method
|
||
|
// then it will throw ClosedChannelException
|
||
|
}
|
||
|
|
||
|
// Invalidate and release any locks that we still hold
|
||
|
invalidateAllLocks();
|
||
|
|
||
|
// signal any threads blocked on this channel
|
||
|
threads.signalAndWait();
|
||
|
|
||
|
// wait until all async I/O operations have completely gracefully
|
||
|
closeLock.writeLock().lock();
|
||
|
try {
|
||
|
// do nothing
|
||
|
} finally {
|
||
|
closeLock.writeLock().unlock();
|
||
|
}
|
||
|
|
||
|
// close file
|
||
|
nd.close(fdObj);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public long size() throws IOException {
|
||
|
int ti = threads.add();
|
||
|
try {
|
||
|
long n = 0L;
|
||
|
try {
|
||
|
begin();
|
||
|
do {
|
||
|
n = nd.size(fdObj);
|
||
|
} while ((n == IOStatus.INTERRUPTED) && isOpen());
|
||
|
return n;
|
||
|
} finally {
|
||
|
end(n >= 0L);
|
||
|
}
|
||
|
} finally {
|
||
|
threads.remove(ti);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public AsynchronousFileChannel truncate(long size) throws IOException {
|
||
|
if (size < 0L)
|
||
|
throw new IllegalArgumentException("Negative size");
|
||
|
if (!writing)
|
||
|
throw new NonWritableChannelException();
|
||
|
int ti = threads.add();
|
||
|
try {
|
||
|
long n = 0L;
|
||
|
try {
|
||
|
begin();
|
||
|
do {
|
||
|
n = nd.size(fdObj);
|
||
|
} while ((n == IOStatus.INTERRUPTED) && isOpen());
|
||
|
|
||
|
// truncate file if 'size' less than current size
|
||
|
if (size < n && isOpen()) {
|
||
|
do {
|
||
|
n = nd.truncate(fdObj, size);
|
||
|
} while ((n == IOStatus.INTERRUPTED) && isOpen());
|
||
|
}
|
||
|
return this;
|
||
|
} finally {
|
||
|
end(n > 0);
|
||
|
}
|
||
|
} finally {
|
||
|
threads.remove(ti);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void force(boolean metaData) throws IOException {
|
||
|
int ti = threads.add();
|
||
|
try {
|
||
|
int n = 0;
|
||
|
try {
|
||
|
begin();
|
||
|
do {
|
||
|
n = nd.force(fdObj, metaData);
|
||
|
} while ((n == IOStatus.INTERRUPTED) && isOpen());
|
||
|
} finally {
|
||
|
end(n >= 0);
|
||
|
}
|
||
|
} finally {
|
||
|
threads.remove(ti);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
<A> Future<FileLock> implLock(final long position,
|
||
|
final long size,
|
||
|
final boolean shared,
|
||
|
final A attachment,
|
||
|
final CompletionHandler<FileLock,? super A> handler)
|
||
|
{
|
||
|
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) {
|
||
|
Throwable exc = new ClosedChannelException();
|
||
|
if (handler == null)
|
||
|
return CompletedFuture.withFailure(exc);
|
||
|
Invoker.invokeIndirectly(handler, attachment, null, exc, executor);
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
final PendingFuture<FileLock,A> result = (handler == null) ?
|
||
|
new PendingFuture<FileLock,A>(this) : null;
|
||
|
Runnable task = new Runnable() {
|
||
|
public void run() {
|
||
|
Throwable exc = null;
|
||
|
|
||
|
int ti = threads.add();
|
||
|
try {
|
||
|
int n;
|
||
|
try {
|
||
|
begin();
|
||
|
do {
|
||
|
n = nd.lock(fdObj, true, position, size, shared);
|
||
|
} while ((n == FileDispatcher.INTERRUPTED) && isOpen());
|
||
|
if (n != FileDispatcher.LOCKED || !isOpen()) {
|
||
|
throw new AsynchronousCloseException();
|
||
|
}
|
||
|
} catch (IOException x) {
|
||
|
removeFromFileLockTable(fli);
|
||
|
if (!isOpen())
|
||
|
x = new AsynchronousCloseException();
|
||
|
exc = x;
|
||
|
} finally {
|
||
|
end();
|
||
|
}
|
||
|
} finally {
|
||
|
threads.remove(ti);
|
||
|
}
|
||
|
if (handler == null) {
|
||
|
result.setResult(fli, exc);
|
||
|
} else {
|
||
|
Invoker.invokeUnchecked(handler, attachment, fli, exc);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
boolean executed = false;
|
||
|
try {
|
||
|
executor.execute(task);
|
||
|
executed = true;
|
||
|
} finally {
|
||
|
if (!executed) {
|
||
|
// rollback
|
||
|
removeFromFileLockTable(fli);
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
@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
|
||
|
FileLockImpl fli = addToFileLockTable(position, size, shared);
|
||
|
if (fli == null)
|
||
|
throw new ClosedChannelException();
|
||
|
|
||
|
int ti = threads.add();
|
||
|
boolean gotLock = false;
|
||
|
try {
|
||
|
begin();
|
||
|
int n;
|
||
|
do {
|
||
|
n = nd.lock(fdObj, false, position, size, shared);
|
||
|
} while ((n == FileDispatcher.INTERRUPTED) && isOpen());
|
||
|
if (n == FileDispatcher.LOCKED && isOpen()) {
|
||
|
gotLock = true;
|
||
|
return fli; // lock acquired
|
||
|
}
|
||
|
if (n == FileDispatcher.NO_LOCK)
|
||
|
return null; // locked by someone else
|
||
|
if (n == FileDispatcher.INTERRUPTED)
|
||
|
throw new AsynchronousCloseException();
|
||
|
// should not get here
|
||
|
throw new AssertionError();
|
||
|
} finally {
|
||
|
if (!gotLock)
|
||
|
removeFromFileLockTable(fli);
|
||
|
end();
|
||
|
threads.remove(ti);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void implRelease(FileLockImpl fli) throws IOException {
|
||
|
nd.release(fdObj, fli.position(), fli.size());
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
<A> Future<Integer> implRead(final ByteBuffer dst,
|
||
|
final long position,
|
||
|
final A attachment,
|
||
|
final CompletionHandler<Integer,? super A> handler)
|
||
|
{
|
||
|
if (position < 0)
|
||
|
throw new IllegalArgumentException("Negative position");
|
||
|
if (!reading)
|
||
|
throw new NonReadableChannelException();
|
||
|
if (dst.isReadOnly())
|
||
|
throw new IllegalArgumentException("Read-only buffer");
|
||
|
|
||
|
// complete immediately if channel closed or no space remaining
|
||
|
if (!isOpen() || (dst.remaining() == 0)) {
|
||
|
Throwable exc = (isOpen()) ? null : new ClosedChannelException();
|
||
|
if (handler == null)
|
||
|
return CompletedFuture.withResult(0, exc);
|
||
|
Invoker.invokeIndirectly(handler, attachment, 0, exc, executor);
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
final PendingFuture<Integer,A> result = (handler == null) ?
|
||
|
new PendingFuture<Integer,A>(this) : null;
|
||
|
Runnable task = new Runnable() {
|
||
|
public void run() {
|
||
|
int n = 0;
|
||
|
Throwable exc = null;
|
||
|
|
||
|
int ti = threads.add();
|
||
|
try {
|
||
|
begin();
|
||
|
do {
|
||
|
n = IOUtil.read(fdObj, dst, position, nd);
|
||
|
} while ((n == IOStatus.INTERRUPTED) && isOpen());
|
||
|
if (n < 0 && !isOpen())
|
||
|
throw new AsynchronousCloseException();
|
||
|
} catch (IOException x) {
|
||
|
if (!isOpen())
|
||
|
x = new AsynchronousCloseException();
|
||
|
exc = x;
|
||
|
} finally {
|
||
|
end();
|
||
|
threads.remove(ti);
|
||
|
}
|
||
|
if (handler == null) {
|
||
|
result.setResult(n, exc);
|
||
|
} else {
|
||
|
Invoker.invokeUnchecked(handler, attachment, n, exc);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
executor.execute(task);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
<A> Future<Integer> implWrite(final ByteBuffer src,
|
||
|
final long position,
|
||
|
final A attachment,
|
||
|
final CompletionHandler<Integer,? super A> handler)
|
||
|
{
|
||
|
if (position < 0)
|
||
|
throw new IllegalArgumentException("Negative position");
|
||
|
if (!writing)
|
||
|
throw new NonWritableChannelException();
|
||
|
|
||
|
// complete immediately if channel is closed or no bytes remaining
|
||
|
if (!isOpen() || (src.remaining() == 0)) {
|
||
|
Throwable exc = (isOpen()) ? null : new ClosedChannelException();
|
||
|
if (handler == null)
|
||
|
return CompletedFuture.withResult(0, exc);
|
||
|
Invoker.invokeIndirectly(handler, attachment, 0, exc, executor);
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
final PendingFuture<Integer,A> result = (handler == null) ?
|
||
|
new PendingFuture<Integer,A>(this) : null;
|
||
|
Runnable task = new Runnable() {
|
||
|
public void run() {
|
||
|
int n = 0;
|
||
|
Throwable exc = null;
|
||
|
|
||
|
int ti = threads.add();
|
||
|
try {
|
||
|
begin();
|
||
|
do {
|
||
|
n = IOUtil.write(fdObj, src, position, nd);
|
||
|
} while ((n == IOStatus.INTERRUPTED) && isOpen());
|
||
|
if (n < 0 && !isOpen())
|
||
|
throw new AsynchronousCloseException();
|
||
|
} catch (IOException x) {
|
||
|
if (!isOpen())
|
||
|
x = new AsynchronousCloseException();
|
||
|
exc = x;
|
||
|
} finally {
|
||
|
end();
|
||
|
threads.remove(ti);
|
||
|
}
|
||
|
if (handler == null) {
|
||
|
result.setResult(n, exc);
|
||
|
} else {
|
||
|
Invoker.invokeUnchecked(handler, attachment, n, exc);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
executor.execute(task);
|
||
|
return result;
|
||
|
}
|
||
|
}
|