Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8140241: (fc) Data transfer from FileChannel to itself causes hang in case of overlap #5061

Closed
Closed
@@ -585,6 +585,17 @@ private long transferToTrustedChannel(long position, long count,
if (!((target instanceof FileChannelImpl) || isSelChImpl))
return IOStatus.UNSUPPORTED;

if (target == this) {
synchronized (positionLock) {
long posThis = position();
if (posThis - count + 1 <= position &&
This conversation was marked as resolved by bplb

This comment has been minimized.

@AlanBateman

AlanBateman Aug 11, 2021
Contributor Outdated

I realise I suggested we need the positionLock here but I don't think it is needed now because there is only one call to position().

This comment has been minimized.

@bplb

bplb Aug 11, 2021
Author Member Outdated

Will remove.

position - count + 1 <= posThis &&
!nd.canTransferToFromOverlappedMap()) {
return IOStatus.UNSUPPORTED_CASE;
}
}
}

// Trusted target: Use a mapped buffer
long remaining = count;
while (remaining > 0L) {
@@ -677,7 +688,7 @@ public long transferTo(long position, long count,
return 0;

if ((sz - position) < count)
count = (int)(sz - position);
count = sz - position;

// Attempt a direct transfer, if the kernel supports it, limiting
// the number of bytes according to which platform
@@ -704,6 +715,14 @@ private long transferFromFileChannel(FileChannelImpl src,
long pos = src.position();
long max = Math.min(count, src.size() - pos);

if (src == this) {
if (position() - max + 1 <= pos &&
pos - max + 1 <= position() &&
!nd.canTransferToFromOverlappedMap()) {
return IOStatus.UNSUPPORTED_CASE;
}
}

long remaining = max;
long p = pos;
while (remaining > 0L) {
@@ -779,9 +798,12 @@ public long transferFrom(ReadableByteChannel src,
throw new IllegalArgumentException();
if (position > size())
return 0;
if (src instanceof FileChannelImpl)
return transferFromFileChannel((FileChannelImpl)src,
position, count);

if (src instanceof FileChannelImpl) {
long n = transferFromFileChannel((FileChannelImpl)src, position, count);
This conversation was marked as resolved by bplb

This comment has been minimized.

@AlanBateman

AlanBateman Aug 11, 2021
Contributor Outdated

It might be a bit cleaner to use the pattern matching with the instanceof to avoid the cast to FileChannelImpl.

This comment has been minimized.

@bplb

bplb Aug 11, 2021
Author Member Outdated

Right, I should have caught that myself.

if (n >= 0)
return n;
}

return transferFromArbitraryChannel(src, position, count);
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 2021, 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
@@ -67,5 +67,7 @@ abstract FileDescriptor duplicateForMapping(FileDescriptor fd)

abstract boolean transferToDirectlyNeedsPositionLock();

abstract boolean canTransferToFromOverlappedMap();

abstract int setDirectIO(FileDescriptor fd, String path);
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2021, 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
@@ -126,6 +126,10 @@ boolean transferToDirectlyNeedsPositionLock() {
return false;
}

boolean canTransferToFromOverlappedMap() {
return canTransferToFromOverlappedMap0();
}

int setDirectIO(FileDescriptor fd, String path) {
int result = -1;
try {
@@ -184,6 +188,8 @@ static native void release0(FileDescriptor fd, long pos, long size)

static native void closeIntFD(int fd) throws IOException;

static native boolean canTransferToFromOverlappedMap0();

static native int setDirect0(FileDescriptor fd) throws IOException;

static native void init();
@@ -338,6 +338,16 @@ Java_sun_nio_ch_FileDispatcherImpl_closeIntFD(JNIEnv *env, jclass clazz, jint fd
closeFileDescriptor(env, fd);
}

JNIEXPORT jboolean JNICALL
Java_sun_nio_ch_FileDispatcherImpl_canTransferToFromOverlappedMap0(JNIEnv *env, jclass clazz)
{
#ifdef MACOSX
return JNI_FALSE;
#else
return JNI_TRUE;
#endif
}

JNIEXPORT jint JNICALL
Java_sun_nio_ch_FileDispatcherImpl_setDirect0(JNIEnv *env, jclass clazz,
jobject fdo)
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2021, 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
@@ -125,6 +125,10 @@ boolean transferToDirectlyNeedsPositionLock() {
return true;
}

boolean canTransferToFromOverlappedMap() {
return true;
}

int setDirectIO(FileDescriptor fd, String path) {
int result = -1;
String filePath = path.substring(0, path.lastIndexOf(File.separator));
@@ -0,0 +1,116 @@
/*
* Copyright (c) 2021, 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.
*
* 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.
*/

/* @test
* @bug 8140241
* @summary Test transferring to and from same file channel
*/
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.Random;

public class TransferOverlappedFileChannel {

private static File file;
private static FileChannel channel;

public static void main(String[] args) throws Exception {
file = File.createTempFile("readingin", null);
file.deleteOnExit();
generateBigFile(file);
RandomAccessFile raf = new RandomAccessFile(file, "rw");
channel = raf.getChannel();
transferToNoOverlap();
transferToOverlap();
transferFromNoOverlap();
transferFromOverlap();
channel.close();
file.delete();
}

private static void transferToNoOverlap() throws IOException {
final long length = file.length();

// position at three quarters
channel.position(length*3/4);
// copy last quarter to third quarter
// (copied and overwritten regions do NOT overlap)
// So: 1 2 3 4 -> 1 2 4 4
channel.transferTo(length / 2, length / 4, channel);
System.out.println("transferToNoOverlap: OK");
}

private static void transferToOverlap() throws IOException {
final long length = file.length();

// position at half
channel.position(length/2);
// copy last half to second quarter
// (copied and overwritten regions DO overlap)
// So: 1 2 3 4 -> 1 3 4 4
channel.transferTo(length / 4, length / 2, channel);
System.out.println("transferToOverlap: OK");
}

private static void transferFromNoOverlap() throws IOException {
final long length = file.length();

// position at three quarters
channel.position(length*3/4);
// copy last quarter to third quarter
// (copied and overwritten regions do NOT overlap)
// So: 1 2 3 4 -> 1 2 4 4
channel.transferFrom(channel, length / 2, length / 4);
System.out.println("transferFromNoOverlap: OK");
}

private static void transferFromOverlap() throws IOException {
final long length = file.length();

// position at half
channel.position(length/2);
// copy last half to second quarter
// (copied and overwritten regions DO overlap)
// So: 1 2 3 4 -> 1 3 4 4
channel.transferFrom(channel, length / 4, length / 2);
System.out.println("transferFromOverlap: OK");
}

static void generateBigFile(File file) throws Exception {
OutputStream out = new BufferedOutputStream(
new FileOutputStream(file));
byte[] randomBytes = new byte[1024];
Random rand = new Random(0);
rand.nextBytes(randomBytes);
for (int i = 0; i < 1024; i++) {
out.write(randomBytes);
}
out.flush();
out.close();
}
}