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

View File

@ -541,9 +541,9 @@ public class FileChannelImpl
}
// 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
// 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) {
// Don't bother trying again
transferSupported = false;
transferToNotSupported = true;
return IOStatus.UNSUPPORTED;
}
return IOStatus.normalize(n);
@ -601,7 +601,7 @@ public class FileChannelImpl
WritableByteChannel target)
throws IOException
{
if (!transferSupported)
if (transferToNotSupported)
return IOStatus.UNSUPPORTED;
FileDescriptor targetFD = null;
@ -646,8 +646,9 @@ public class FileChannelImpl
}
// Size threshold above which to use a mapped buffer;
// transferToArbitraryChannel() is faster for smaller transfers
private static final long TRUSTED_TRANSFER_THRESHOLD = 16L*1024L;
// transferToArbitraryChannel() and transferFromArbitraryChannel()
// are faster for smaller transfers
private static final long MAPPED_TRANSFER_THRESHOLD = 16L*1024L;
// Maximum size to map when using a mapped buffer
private static final long MAPPED_TRANSFER_SIZE = 8L*1024L*1024L;
@ -656,7 +657,7 @@ public class FileChannelImpl
WritableByteChannel target)
throws IOException
{
if (count < TRUSTED_TRANSFER_THRESHOLD)
if (count < MAPPED_TRANSFER_THRESHOLD)
return IOStatus.UNSUPPORTED_CASE;
boolean isSelChImpl = (target instanceof SelChImpl);
@ -781,12 +782,66 @@ public class FileChannelImpl
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,
long position, long count)
throws IOException
{
if (!src.readable)
throw new NonReadableChannelException();
if (count < MAPPED_TRANSFER_THRESHOLD)
return IOStatus.UNSUPPORTED_CASE;
synchronized (src.positionLock) {
long pos = src.position();
long max = Math.min(count, src.size() - pos);
@ -876,8 +931,10 @@ public class FileChannelImpl
return 0;
if (src instanceof FileChannelImpl fci) {
long n = transferFromFileChannel(fci, position, count);
if (n >= 0)
long n;
if ((n = transferFromDirectly(fci, position, count)) >= 0)
return n;
if ((n = transferFromFileChannel(fci, position, count)) >= 0)
return n;
}
@ -1516,6 +1573,10 @@ public class FileChannelImpl
private static native long transferTo0(FileDescriptor src, long position,
long count, FileDescriptor dst);
private static native long transferFrom0(FileDescriptor src,
FileDescriptor dst,
long position, long count);
// Retrieves the maximum size of a transfer
private static native int maxDirectTransferSize0();

View File

@ -31,6 +31,7 @@
#if defined(__linux__)
#include <sys/sendfile.h>
#include <dlfcn.h>
#elif defined(_AIX)
#include <string.h>
#include <sys/socket.h>
@ -50,10 +51,20 @@
#include "java_lang_Integer.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
Java_sun_nio_ch_FileChannelImpl_allocationGranularity0(JNIEnv *env, jclass clazz)
{
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;
}
@ -248,6 +259,38 @@ Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this,
#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
Java_sun_nio_ch_FileChannelImpl_maxDirectTransferSize0(JNIEnv* env, jobject this)
{

View File

@ -188,6 +188,13 @@ Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this,
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
Java_sun_nio_ch_FileChannelImpl_maxDirectTransferSize0(JNIEnv* env, jobject this)

View File

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