8286763: [REDO] (fc) Tune FileChannel.transferFrom()

Reviewed-by: alanb
This commit is contained in:
Brian Burkhalter 2022-05-18 15:55:21 +00:00
parent ac7e019232
commit d8b0b32f9f
4 changed files with 131 additions and 14 deletions
src/java.base
share/classes/sun/nio/ch
unix/native/libnio/ch
windows/native/libnio/ch
test/jdk/java/nio/channels/FileChannel

@ -541,9 +541,9 @@ public class FileChannelImpl
} }
// Assume at first that the underlying kernel supports sendfile(); // Assume at first that the underlying kernel supports sendfile();
// set this to false if we find out later that it doesn't // set this to true if we find out later that it doesn't
// //
private static volatile boolean transferSupported = true; private static volatile boolean transferToNotSupported;
// Assume that the underlying kernel sendfile() will work if the target // Assume that the underlying kernel sendfile() will work if the target
// fd is a pipe; set this to false if we find out later that it doesn't // fd is a pipe; set this to false if we find out later that it doesn't
@ -587,7 +587,7 @@ public class FileChannelImpl
} }
if (n == IOStatus.UNSUPPORTED) { if (n == IOStatus.UNSUPPORTED) {
// Don't bother trying again // Don't bother trying again
transferSupported = false; transferToNotSupported = true;
return IOStatus.UNSUPPORTED; return IOStatus.UNSUPPORTED;
} }
return IOStatus.normalize(n); return IOStatus.normalize(n);
@ -601,7 +601,7 @@ public class FileChannelImpl
WritableByteChannel target) WritableByteChannel target)
throws IOException throws IOException
{ {
if (!transferSupported) if (transferToNotSupported)
return IOStatus.UNSUPPORTED; return IOStatus.UNSUPPORTED;
FileDescriptor targetFD = null; FileDescriptor targetFD = null;
@ -646,8 +646,9 @@ public class FileChannelImpl
} }
// Size threshold above which to use a mapped buffer; // Size threshold above which to use a mapped buffer;
// transferToArbitraryChannel() is faster for smaller transfers // transferToArbitraryChannel() and transferFromArbitraryChannel()
private static final long TRUSTED_TRANSFER_THRESHOLD = 16L*1024L; // are faster for smaller transfers
private static final long MAPPED_TRANSFER_THRESHOLD = 16L*1024L;
// Maximum size to map when using a mapped buffer // Maximum size to map when using a mapped buffer
private static final long MAPPED_TRANSFER_SIZE = 8L*1024L*1024L; private static final long MAPPED_TRANSFER_SIZE = 8L*1024L*1024L;
@ -656,7 +657,7 @@ public class FileChannelImpl
WritableByteChannel target) WritableByteChannel target)
throws IOException throws IOException
{ {
if (count < TRUSTED_TRANSFER_THRESHOLD) if (count < MAPPED_TRANSFER_THRESHOLD)
return IOStatus.UNSUPPORTED_CASE; return IOStatus.UNSUPPORTED_CASE;
boolean isSelChImpl = (target instanceof SelChImpl); boolean isSelChImpl = (target instanceof SelChImpl);
@ -781,12 +782,66 @@ public class FileChannelImpl
return transferToArbitraryChannel(position, count, target); return transferToArbitraryChannel(position, count, target);
} }
// Assume at first that the underlying kernel supports copy_file_range();
// set this to true if we find out later that it doesn't
//
private static volatile boolean transferFromNotSupported;
private long transferFromDirectlyInternal(FileDescriptor srcFD,
long position, long count)
throws IOException
{
long n = -1;
int ti = -1;
try {
beginBlocking();
ti = threads.add();
if (!isOpen())
return -1;
do {
long comp = Blocker.begin();
try {
n = transferFrom0(srcFD, fd, position, count);
} finally {
Blocker.end(comp);
}
} while ((n == IOStatus.INTERRUPTED) && isOpen());
if (n == IOStatus.UNSUPPORTED) {
// Don't bother trying again
transferFromNotSupported = true;
return IOStatus.UNSUPPORTED;
}
return IOStatus.normalize(n);
} finally {
threads.remove(ti);
end (n > -1);
}
}
private long transferFromDirectly(FileChannelImpl src,
long position, long count)
throws IOException
{
if (!src.readable)
throw new NonReadableChannelException();
if (transferFromNotSupported)
return IOStatus.UNSUPPORTED;
FileDescriptor srcFD = src.fd;
if (srcFD == null)
return IOStatus.UNSUPPORTED_CASE;
return transferFromDirectlyInternal(srcFD, position, count);
}
private long transferFromFileChannel(FileChannelImpl src, private long transferFromFileChannel(FileChannelImpl src,
long position, long count) long position, long count)
throws IOException throws IOException
{ {
if (!src.readable) if (!src.readable)
throw new NonReadableChannelException(); throw new NonReadableChannelException();
if (count < MAPPED_TRANSFER_THRESHOLD)
return IOStatus.UNSUPPORTED_CASE;
synchronized (src.positionLock) { synchronized (src.positionLock) {
long pos = src.position(); long pos = src.position();
long max = Math.min(count, src.size() - pos); long max = Math.min(count, src.size() - pos);
@ -876,8 +931,10 @@ public class FileChannelImpl
return 0; return 0;
if (src instanceof FileChannelImpl fci) { if (src instanceof FileChannelImpl fci) {
long n = transferFromFileChannel(fci, position, count); long n;
if (n >= 0) if ((n = transferFromDirectly(fci, position, count)) >= 0)
return n;
if ((n = transferFromFileChannel(fci, position, count)) >= 0)
return n; return n;
} }
@ -1516,6 +1573,10 @@ public class FileChannelImpl
private static native long transferTo0(FileDescriptor src, long position, private static native long transferTo0(FileDescriptor src, long position,
long count, FileDescriptor dst); long count, FileDescriptor dst);
private static native long transferFrom0(FileDescriptor src,
FileDescriptor dst,
long position, long count);
// Retrieves the maximum size of a transfer // Retrieves the maximum size of a transfer
private static native int maxDirectTransferSize0(); private static native int maxDirectTransferSize0();

@ -31,6 +31,7 @@
#if defined(__linux__) #if defined(__linux__)
#include <sys/sendfile.h> #include <sys/sendfile.h>
#include <dlfcn.h>
#elif defined(_AIX) #elif defined(_AIX)
#include <string.h> #include <string.h>
#include <sys/socket.h> #include <sys/socket.h>
@ -50,10 +51,20 @@
#include "java_lang_Integer.h" #include "java_lang_Integer.h"
#include <assert.h> #include <assert.h>
#if defined(__linux__)
typedef ssize_t copy_file_range_func(int, loff_t*, int, loff_t*, size_t,
unsigned int);
static copy_file_range_func* my_copy_file_range_func = NULL;
#endif
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
Java_sun_nio_ch_FileChannelImpl_allocationGranularity0(JNIEnv *env, jclass clazz) Java_sun_nio_ch_FileChannelImpl_allocationGranularity0(JNIEnv *env, jclass clazz)
{ {
jlong pageSize = sysconf(_SC_PAGESIZE); jlong pageSize = sysconf(_SC_PAGESIZE);
#if defined(__linux__)
my_copy_file_range_func =
(copy_file_range_func*) dlsym(RTLD_DEFAULT, "copy_file_range");
#endif
return pageSize; return pageSize;
} }
@ -248,6 +259,38 @@ Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this,
#endif #endif
} }
JNIEXPORT jlong JNICALL
Java_sun_nio_ch_FileChannelImpl_transferFrom0(JNIEnv *env, jobject this,
jobject srcFDO, jobject dstFDO,
jlong position, jlong count)
{
#if defined(__linux__)
if (my_copy_file_range_func == NULL)
return IOS_UNSUPPORTED;
jint srcFD = fdval(env, srcFDO);
jint dstFD = fdval(env, dstFDO);
off64_t offset = (off64_t)position;
jlong n = my_copy_file_range_func(srcFD, NULL, dstFD, &offset, count, 0);
if (n < 0) {
if (errno == EAGAIN)
return IOS_UNAVAILABLE;
if ((errno == EBADF || errno == EINVAL || errno == EXDEV) &&
((ssize_t)count >= 0))
return IOS_UNSUPPORTED_CASE;
if (errno == EINTR) {
return IOS_INTERRUPTED;
}
JNU_ThrowIOExceptionWithLastError(env, "Transfer failed");
return IOS_THROWN;
}
return n;
#else
return IOS_UNSUPPORTED;
#endif
}
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_sun_nio_ch_FileChannelImpl_maxDirectTransferSize0(JNIEnv* env, jobject this) Java_sun_nio_ch_FileChannelImpl_maxDirectTransferSize0(JNIEnv* env, jobject this)
{ {

@ -188,6 +188,13 @@ Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this,
return chunkSize; return chunkSize;
} }
JNIEXPORT jlong JNICALL
Java_sun_nio_ch_FileChannelImpl_transferFrom0(JNIEnv *env, jobject this,
jobject srcFDO, jobject dstFDO,
jlong position, jlong count)
{
return IOS_UNSUPPORTED;
}
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_sun_nio_ch_FileChannelImpl_maxDirectTransferSize0(JNIEnv* env, jobject this) Java_sun_nio_ch_FileChannelImpl_maxDirectTransferSize0(JNIEnv* env, jobject this)

@ -23,7 +23,7 @@
/* @test /* @test
* @summary Comprehensive test for FileChannel.transfer{From,To} * @summary Comprehensive test for FileChannel.transfer{From,To}
* @bug 4708120 * @bug 4708120 8274113
* @author Mark Reinhold * @author Mark Reinhold
* @run main/timeout=300 Transfers * @run main/timeout=300 Transfers
*/ */
@ -468,10 +468,16 @@ public class Transfers {
int pos = (int)seed & 0xfff; int pos = (int)seed & 0xfff;
fc.position(pos); fc.position(pos);
int n = (int)fc.transferFrom(src.channel(), off, len); long position = off;
if (n != len) long count = len;
throw new Failure("Incorrect transfer length: " + n while (count > 0) {
+ " (expected " + len + ")"); int n = (int)fc.transferFrom(src.channel(), position, count);
if (n < 0 || n > count)
throw new Failure("Incorrect transfer length n = : " + n
+ " (expected 0 <= n <= " + len + ")");
position += n;
count -= n;
}
// Check that source didn't change, and was read correctly // Check that source didn't change, and was read correctly
src.verify(); src.verify();