Skip to content

Commit c74b6d4

Browse files
author
Brian Burkhalter
committedAug 26, 2022
8292562: (fc) Use copy_file_range in FileChannel::transferTo on Linux
Reviewed-by: alanb
1 parent 3844685 commit c74b6d4

File tree

4 files changed

+151
-10
lines changed

4 files changed

+151
-10
lines changed
 

‎src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java

+8-4
Original file line numberDiff line numberDiff line change
@@ -570,10 +570,11 @@ private long transferToDirectlyInternal(long position, int icount,
570570
ti = threads.add();
571571
if (!isOpen())
572572
return -1;
573+
boolean append = fdAccess.getAppend(targetFD);
573574
do {
574575
long comp = Blocker.begin();
575576
try {
576-
n = transferTo0(fd, position, icount, targetFD);
577+
n = transferTo0(fd, position, icount, targetFD, append);
577578
} finally {
578579
Blocker.end(comp);
579580
}
@@ -801,7 +802,8 @@ private long transferFromDirectlyInternal(FileDescriptor srcFD,
801802
do {
802803
long comp = Blocker.begin();
803804
try {
804-
n = transferFrom0(srcFD, fd, position, count);
805+
boolean append = fdAccess.getAppend(fd);
806+
n = transferFrom0(srcFD, fd, position, count, append);
805807
} finally {
806808
Blocker.end(comp);
807809
}
@@ -1573,11 +1575,13 @@ private native long map0(FileDescriptor fd, int prot, long position,
15731575
// Transfers from src to dst, or returns IOStatus.UNSUPPORTED (-4) or
15741576
// IOStatus.UNSUPPORTED_CASE (-6) if the kernel does not support it
15751577
private static native long transferTo0(FileDescriptor src, long position,
1576-
long count, FileDescriptor dst);
1578+
long count, FileDescriptor dst,
1579+
boolean append);
15771580

15781581
private static native long transferFrom0(FileDescriptor src,
15791582
FileDescriptor dst,
1580-
long position, long count);
1583+
long position, long count,
1584+
boolean append);
15811585

15821586
// Retrieves the maximum size of a transfer
15831587
private static native int maxDirectTransferSize0();

‎src/java.base/unix/native/libnio/ch/FileChannelImpl.c

+35-4
Original file line numberDiff line numberDiff line change
@@ -169,14 +169,40 @@ JNIEXPORT jlong JNICALL
169169
Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this,
170170
jobject srcFDO,
171171
jlong position, jlong count,
172-
jobject dstFDO)
172+
jobject dstFDO, jboolean append)
173173
{
174174
jint srcFD = fdval(env, srcFDO);
175175
jint dstFD = fdval(env, dstFDO);
176176

177177
#if defined(__linux__)
178+
// copy_file_range fails with EBADF when appending, and sendfile
179+
// fails with EINVAL
180+
if (append == JNI_TRUE)
181+
return IOS_UNSUPPORTED_CASE;
182+
178183
off64_t offset = (off64_t)position;
179-
jlong n = sendfile64(dstFD, srcFD, &offset, (size_t)count);
184+
jlong n;
185+
if (my_copy_file_range_func != NULL) {
186+
size_t len = (size_t)count;
187+
n = my_copy_file_range_func(srcFD, &offset, dstFD, NULL, len, 0);
188+
if (n < 0) {
189+
switch (errno) {
190+
case EINTR:
191+
return IOS_INTERRUPTED;
192+
case EINVAL:
193+
case EXDEV:
194+
// ignore and try sendfile()
195+
break;
196+
default:
197+
JNU_ThrowIOExceptionWithLastError(env, "Copy failed");
198+
return IOS_THROWN;
199+
}
200+
}
201+
if (n >= 0)
202+
return n;
203+
}
204+
205+
n = sendfile64(dstFD, srcFD, &offset, (size_t)count);
180206
if (n < 0) {
181207
if (errno == EAGAIN)
182208
return IOS_UNAVAILABLE;
@@ -262,17 +288,22 @@ Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this,
262288
JNIEXPORT jlong JNICALL
263289
Java_sun_nio_ch_FileChannelImpl_transferFrom0(JNIEnv *env, jobject this,
264290
jobject srcFDO, jobject dstFDO,
265-
jlong position, jlong count)
291+
jlong position, jlong count,
292+
jboolean append)
266293
{
267294
#if defined(__linux__)
268295
if (my_copy_file_range_func == NULL)
269296
return IOS_UNSUPPORTED;
297+
// copy_file_range fails with EBADF when appending
298+
if (append == JNI_TRUE)
299+
return IOS_UNSUPPORTED_CASE;
270300

271301
jint srcFD = fdval(env, srcFDO);
272302
jint dstFD = fdval(env, dstFDO);
273303

274304
off64_t offset = (off64_t)position;
275-
jlong n = my_copy_file_range_func(srcFD, NULL, dstFD, &offset, count, 0);
305+
size_t len = (size_t)count;
306+
jlong n = my_copy_file_range_func(srcFD, NULL, dstFD, &offset, len, 0);
276307
if (n < 0) {
277308
if (errno == EAGAIN)
278309
return IOS_UNAVAILABLE;

‎src/java.base/windows/native/libnio/ch/FileChannelImpl.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ JNIEXPORT jlong JNICALL
147147
Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this,
148148
jobject srcFD,
149149
jlong position, jlong count,
150-
jobject dstFD)
150+
jobject dstFD, jboolean append)
151151
{
152152
const int PACKET_SIZE = 524288;
153153

@@ -191,7 +191,8 @@ Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this,
191191
JNIEXPORT jlong JNICALL
192192
Java_sun_nio_ch_FileChannelImpl_transferFrom0(JNIEnv *env, jobject this,
193193
jobject srcFDO, jobject dstFDO,
194-
jlong position, jlong count)
194+
jlong position, jlong count,
195+
jboolean append)
195196
{
196197
return IOS_UNSUPPORTED;
197198
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/* @test
25+
* @bug 8292562
26+
* @summary Test transferTo and transferFrom when target is appending
27+
* @library /test/lib
28+
* @build jdk.test.lib.RandomFactory
29+
* @run main TransferToAppending
30+
* @key randomness
31+
*/
32+
33+
import java.io.IOException;
34+
import java.nio.ByteBuffer;
35+
import java.nio.file.Files;
36+
import java.nio.file.Path;
37+
import java.nio.channels.FileChannel;
38+
import java.util.Random;
39+
import jdk.test.lib.RandomFactory;
40+
41+
import static java.nio.file.StandardOpenOption.*;
42+
43+
public class TransferToAppending {
44+
private static final int MIN_SIZE = 128;
45+
private static final int MAX_SIZE = 32768;
46+
private static final Random RND = RandomFactory.getRandom();
47+
48+
public static void main(String... args) throws IOException {
49+
// Create files in size range [MIN_SIZE,MAX_SIZE)
50+
// filled with random bytes
51+
Path source = createFile("src");
52+
Path target = createFile("tgt");
53+
54+
try (FileChannel src = FileChannel.open(source, READ, WRITE);
55+
FileChannel tgt = FileChannel.open(target, WRITE, APPEND);) {
56+
// Set source range to a subset of the source
57+
long size = Files.size(source);
58+
long position = RND.nextInt((int)size);
59+
long count = RND.nextInt((int)(size - position));
60+
long tgtSize = Files.size(target);
61+
62+
// Transfer subrange to target
63+
long nbytes = src.transferTo(position, count, tgt);
64+
65+
long expectedSize = tgtSize + nbytes;
66+
67+
if (Files.size(target) != expectedSize) {
68+
String msg = String.format("Bad size: expected %d, actual %d%n",
69+
expectedSize, Files.size(target));
70+
throw new RuntimeException(msg);
71+
}
72+
73+
tgt.close();
74+
75+
// Load subrange of source
76+
ByteBuffer bufSrc = ByteBuffer.allocate((int)nbytes);
77+
src.read(bufSrc, position);
78+
79+
try (FileChannel res = FileChannel.open(target, READ, WRITE)) {
80+
// Load appended range of target
81+
ByteBuffer bufTgt = ByteBuffer.allocate((int)nbytes);
82+
res.read(bufTgt, tgtSize);
83+
84+
// Subranges of values should be equal
85+
if (bufSrc.mismatch(bufTgt) != -1) {
86+
throw new RuntimeException("Range of values unequal");
87+
}
88+
}
89+
} finally {
90+
Files.delete(source);
91+
Files.delete(target);
92+
}
93+
}
94+
95+
private static Path createFile(String name) throws IOException {
96+
Path path = Files.createTempFile(name, ".dat");
97+
try (FileChannel fc = FileChannel.open(path, CREATE, READ, WRITE)) {
98+
int size = Math.max(RND.nextInt(MAX_SIZE), 128);
99+
byte[] b = new byte[size];
100+
RND.nextBytes(b);
101+
fc.write(ByteBuffer.wrap(b));
102+
}
103+
return path;
104+
}
105+
}

0 commit comments

Comments
 (0)
Please sign in to comment.