8064407: (fc) FileChannel transferTo should use TransmitFile on Windows
Reviewed-by: alanb
Contributed-by: kirk.shoop@microsoft.com, v-valkop@microsoft.com
--- a/jdk/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java Fri Dec 05 17:36:18 2014 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java Sun Dec 07 07:10:29 2014 +0000
@@ -38,6 +38,7 @@
import java.nio.channels.NonWritableChannelException;
import java.nio.channels.OverlappingFileLockException;
import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.SelectableChannel;
import java.nio.channels.WritableByteChannel;
import java.security.AccessController;
import java.util.ArrayList;
@@ -404,30 +405,13 @@
//
private static volatile boolean fileSupported = true;
- private long transferToDirectly(long position, int icount,
- WritableByteChannel target)
+ private long transferToDirectlyInternal(long position, int icount,
+ WritableByteChannel target,
+ FileDescriptor targetFD)
throws IOException
{
- if (!transferSupported)
- return IOStatus.UNSUPPORTED;
-
- FileDescriptor targetFD = null;
- if (target instanceof FileChannelImpl) {
- if (!fileSupported)
- return IOStatus.UNSUPPORTED_CASE;
- targetFD = ((FileChannelImpl)target).fd;
- } else if (target instanceof SelChImpl) {
- // Direct transfer to pipe causes EINVAL on some configurations
- if ((target instanceof SinkChannelImpl) && !pipeSupported)
- return IOStatus.UNSUPPORTED_CASE;
- targetFD = ((SelChImpl)target).getFD();
- }
- if (targetFD == null)
- return IOStatus.UNSUPPORTED;
- int thisFDVal = IOUtil.fdVal(fd);
- int targetFDVal = IOUtil.fdVal(targetFD);
- if (thisFDVal == targetFDVal) // Not supported on some configurations
- return IOStatus.UNSUPPORTED;
+ assert !nd.transferToDirectlyNeedsPositionLock() ||
+ Thread.holdsLock(positionLock);
long n = -1;
int ti = -1;
@@ -437,7 +421,7 @@
if (!isOpen())
return -1;
do {
- n = transferTo0(thisFDVal, position, icount, targetFDVal);
+ n = transferTo0(fd, position, icount, targetFD);
} while ((n == IOStatus.INTERRUPTED) && isOpen());
if (n == IOStatus.UNSUPPORTED_CASE) {
if (target instanceof SinkChannelImpl)
@@ -458,6 +442,54 @@
}
}
+ private long transferToDirectly(long position, int icount,
+ WritableByteChannel target)
+ throws IOException
+ {
+ if (!transferSupported)
+ return IOStatus.UNSUPPORTED;
+
+ FileDescriptor targetFD = null;
+ if (target instanceof FileChannelImpl) {
+ if (!fileSupported)
+ return IOStatus.UNSUPPORTED_CASE;
+ targetFD = ((FileChannelImpl)target).fd;
+ } else if (target instanceof SelChImpl) {
+ // Direct transfer to pipe causes EINVAL on some configurations
+ if ((target instanceof SinkChannelImpl) && !pipeSupported)
+ return IOStatus.UNSUPPORTED_CASE;
+
+ // Platform-specific restrictions. Now there is only one:
+ // Direct transfer to non-blocking channel could be forbidden
+ SelectableChannel sc = (SelectableChannel)target;
+ if (!nd.canTransferToDirectly(sc))
+ return IOStatus.UNSUPPORTED_CASE;
+
+ targetFD = ((SelChImpl)target).getFD();
+ }
+
+ if (targetFD == null)
+ return IOStatus.UNSUPPORTED;
+ int thisFDVal = IOUtil.fdVal(fd);
+ int targetFDVal = IOUtil.fdVal(targetFD);
+ if (thisFDVal == targetFDVal) // Not supported on some configurations
+ return IOStatus.UNSUPPORTED;
+
+ if (nd.transferToDirectlyNeedsPositionLock()) {
+ synchronized (positionLock) {
+ long pos = position();
+ try {
+ return transferToDirectlyInternal(position, icount,
+ target, targetFD);
+ } finally {
+ position(pos);
+ }
+ }
+ } else {
+ return transferToDirectlyInternal(position, icount, target, targetFD);
+ }
+ }
+
// Maximum size to map when using a mapped buffer
private static final long MAPPED_TRANSFER_SIZE = 8L*1024L*1024L;
@@ -1173,7 +1205,8 @@
private static native int unmap0(long address, long length);
// Transfers from src to dst, or returns -2 if kernel can't do that
- private native long transferTo0(int src, long position, long count, int dst);
+ private native long transferTo0(FileDescriptor src, long position,
+ long count, FileDescriptor dst);
// Sets or reports this file's position
// If offset is -1, the current position is returned
--- a/jdk/src/java.base/share/classes/sun/nio/ch/FileDispatcher.java Fri Dec 05 17:36:18 2014 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/FileDispatcher.java Sun Dec 07 07:10:29 2014 +0000
@@ -25,7 +25,9 @@
package sun.nio.ch;
-import java.io.*;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.nio.channels.SelectableChannel;
abstract class FileDispatcher extends NativeDispatcher {
@@ -53,4 +55,8 @@
*/
abstract FileDescriptor duplicateForMapping(FileDescriptor fd)
throws IOException;
+
+ abstract boolean canTransferToDirectly(SelectableChannel sc);
+
+ abstract boolean transferToDirectlyNeedsPositionLock();
}
--- a/jdk/src/java.base/unix/classes/sun/nio/ch/FileDispatcherImpl.java Fri Dec 05 17:36:18 2014 -0800
+++ b/jdk/src/java.base/unix/classes/sun/nio/ch/FileDispatcherImpl.java Sun Dec 07 07:10:29 2014 +0000
@@ -25,10 +25,10 @@
package sun.nio.ch;
-import java.io.*;
+import java.io.FileDescriptor;
+import java.io.IOException;
-class FileDispatcherImpl extends FileDispatcher
-{
+class FileDispatcherImpl extends FileDispatcher {
static {
IOUtil.load();
@@ -104,6 +104,14 @@
return new FileDescriptor();
}
+ boolean canTransferToDirectly(java.nio.channels.SelectableChannel sc) {
+ return true;
+ }
+
+ boolean transferToDirectlyNeedsPositionLock() {
+ return false;
+ }
+
// -- Native methods --
static native int read0(FileDescriptor fd, long address, int len)
--- a/jdk/src/java.base/unix/native/libnio/ch/FileChannelImpl.c Fri Dec 05 17:36:18 2014 -0800
+++ b/jdk/src/java.base/unix/native/libnio/ch/FileChannelImpl.c Sun Dec 07 07:10:29 2014 +0000
@@ -154,10 +154,13 @@
JNIEXPORT jlong JNICALL
Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this,
- jint srcFD,
+ jobject srcFDO,
jlong position, jlong count,
- jint dstFD)
+ jobject dstFDO)
{
+ jint srcFD = fdval(env, srcFDO);
+ jint dstFD = fdval(env, dstFDO);
+
#if defined(__linux__)
off64_t offset = (off64_t)position;
jlong n = sendfile64(dstFD, srcFD, &offset, (size_t)count);
--- a/jdk/src/java.base/windows/classes/sun/nio/ch/FileDispatcherImpl.java Fri Dec 05 17:36:18 2014 -0800
+++ b/jdk/src/java.base/windows/classes/sun/nio/ch/FileDispatcherImpl.java Sun Dec 07 07:10:29 2014 +0000
@@ -25,21 +25,21 @@
package sun.nio.ch;
-import java.io.*;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.security.PrivilegedAction;
import sun.misc.SharedSecrets;
import sun.misc.JavaIOFileDescriptorAccess;
-class FileDispatcherImpl extends FileDispatcher
-{
+class FileDispatcherImpl extends FileDispatcher {
+
private static final JavaIOFileDescriptorAccess fdAccess =
SharedSecrets.getJavaIOFileDescriptorAccess();
- static {
- IOUtil.load();
- }
+ // set to true if fast file transmission (TransmitFile) is enabled
+ private static final boolean fastFileTransfer;
- FileDispatcherImpl() {
- }
+ FileDispatcherImpl() { }
@Override
boolean needsPositionLock() {
@@ -110,6 +110,36 @@
return result;
}
+ boolean canTransferToDirectly(java.nio.channels.SelectableChannel sc) {
+ return fastFileTransfer && sc.isBlocking();
+ }
+
+ boolean transferToDirectlyNeedsPositionLock() {
+ return true;
+ }
+
+ static boolean isFastFileTransferRequested() {
+ String fileTransferProp = java.security.AccessController.doPrivileged(
+ new PrivilegedAction<String>() {
+ @Override
+ public String run() {
+ return System.getProperty("jdk.net.enableFastFileTransfer");
+ }
+ });
+ boolean enable;
+ if ("".equals(fileTransferProp)) {
+ enable = true;
+ } else {
+ enable = Boolean.parseBoolean(fileTransferProp);
+ }
+ return enable;
+ }
+
+ static {
+ IOUtil.load();
+ fastFileTransfer = isFastFileTransferRequested();
+ }
+
//-- Native methods
static native int read0(FileDescriptor fd, long address, int len)
--- a/jdk/src/java.base/windows/native/libnio/ch/FileChannelImpl.c Fri Dec 05 17:36:18 2014 -0800
+++ b/jdk/src/java.base/windows/native/libnio/ch/FileChannelImpl.c Sun Dec 07 07:10:29 2014 +0000
@@ -31,6 +31,10 @@
#include "nio.h"
#include "nio_util.h"
#include "sun_nio_ch_FileChannelImpl.h"
+#include "java_lang_Integer.h"
+
+#include <Mswsock.h>
+#pragma comment(lib, "Mswsock.lib")
static jfieldID chan_fd; /* id for jobject 'fd' in java.io.FileChannel */
@@ -175,9 +179,42 @@
JNIEXPORT jlong JNICALL
Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this,
- jint srcFD,
+ jobject srcFD,
jlong position, jlong count,
- jint dstFD)
+ jobject dstFD)
{
- return IOS_UNSUPPORTED;
+ const int PACKET_SIZE = 524288;
+
+ HANDLE src = (HANDLE)(handleval(env, srcFD));
+ SOCKET dst = (SOCKET)(fdval(env, dstFD));
+ DWORD chunkSize = (count > java_lang_Integer_MAX_VALUE) ?
+ java_lang_Integer_MAX_VALUE : (DWORD)count;
+ BOOL result = 0;
+
+ jlong pos = Java_sun_nio_ch_FileChannelImpl_position0(env, this, srcFD, position);
+ if (pos == IOS_THROWN) {
+ return IOS_THROWN;
+ }
+
+ result = TransmitFile(
+ dst,
+ src,
+ chunkSize,
+ PACKET_SIZE,
+ NULL,
+ NULL,
+ TF_USE_KERNEL_APC
+ );
+ if (!result) {
+ int error = WSAGetLastError();
+ if (WSAEINVAL == error && count >= 0) {
+ return IOS_UNSUPPORTED_CASE;
+ }
+ if (WSAENOTSOCK == error) {
+ return IOS_UNSUPPORTED_CASE;
+ }
+ JNU_ThrowIOExceptionWithLastError(env, "transfer failed");
+ return IOS_THROWN;
+ }
+ return chunkSize;
}
--- a/jdk/test/java/nio/channels/FileChannel/TransferToChannel.java Fri Dec 05 17:36:18 2014 -0800
+++ b/jdk/test/java/nio/channels/FileChannel/TransferToChannel.java Sun Dec 07 07:10:29 2014 +0000
@@ -24,6 +24,8 @@
/* @test
* @bug 4652496
* @summary Test transferTo with different target channels
+ * @run main TransferToChannel
+ * @run main/othervm -Djdk.net.enableFastFileTransfer TransferToChannel
*/
import java.nio.channels.FileChannel;