mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 23:04:50 +02:00
6980847: (fs) Files.copy needs to be "tuned"
Reviewed-by: alanb
This commit is contained in:
parent
d579916a6b
commit
b8db0c383b
4 changed files with 176 additions and 32 deletions
|
@ -26,6 +26,7 @@
|
|||
package sun.nio.fs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.AtomicMoveNotSupportedException;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.DirectoryNotEmptyException;
|
||||
|
@ -36,6 +37,9 @@ import java.nio.file.StandardCopyOption;
|
|||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import jdk.internal.misc.Blocker;
|
||||
import sun.nio.ch.DirectBuffer;
|
||||
import sun.nio.ch.IOStatus;
|
||||
import sun.nio.ch.Util;
|
||||
import static sun.nio.fs.UnixNativeDispatcher.*;
|
||||
import static sun.nio.fs.UnixConstants.*;
|
||||
|
||||
|
@ -44,6 +48,9 @@ import static sun.nio.fs.UnixConstants.*;
|
|||
*/
|
||||
|
||||
class UnixCopyFile {
|
||||
// minimum size of a temporary direct buffer
|
||||
private static final int MIN_BUFFER_SIZE = 16384;
|
||||
|
||||
private UnixCopyFile() { }
|
||||
|
||||
// The flags that control how a file is copied or moved
|
||||
|
@ -217,6 +224,47 @@ class UnixCopyFile {
|
|||
}
|
||||
}
|
||||
|
||||
// calculate the least common multiple of two values;
|
||||
// the parameters in general will be powers of two likely in the
|
||||
// range [4096, 65536] so this algorithm is expected to converge
|
||||
// when it is rarely called
|
||||
private static long lcm(long x, long y) {
|
||||
assert x > 0 && y > 0 : "Non-positive parameter";
|
||||
|
||||
long u = x;
|
||||
long v = y;
|
||||
|
||||
while (u != v) {
|
||||
if (u < v)
|
||||
u += x;
|
||||
else // u > v
|
||||
v += y;
|
||||
}
|
||||
|
||||
return u;
|
||||
}
|
||||
|
||||
// calculate temporary direct buffer size
|
||||
private static int temporaryBufferSize(UnixPath source, UnixPath target) {
|
||||
int bufferSize = MIN_BUFFER_SIZE;
|
||||
try {
|
||||
long bss = UnixFileStoreAttributes.get(source).blockSize();
|
||||
long bst = UnixFileStoreAttributes.get(target).blockSize();
|
||||
if (bss > 0 && bst > 0) {
|
||||
bufferSize = (int)(bss == bst ? bss : lcm(bss, bst));
|
||||
}
|
||||
if (bufferSize < MIN_BUFFER_SIZE) {
|
||||
int factor = (MIN_BUFFER_SIZE + bufferSize - 1)/bufferSize;
|
||||
bufferSize *= factor;
|
||||
}
|
||||
} catch (UnixException ignored) {
|
||||
}
|
||||
return bufferSize;
|
||||
}
|
||||
|
||||
// whether direct copying is supported on this platform
|
||||
private static volatile boolean directCopyNotSupported;
|
||||
|
||||
// copy regular file from source to target
|
||||
private static void copyFile(UnixPath source,
|
||||
UnixFileAttributes attrs,
|
||||
|
@ -248,17 +296,43 @@ class UnixCopyFile {
|
|||
// set to true when file and attributes copied
|
||||
boolean complete = false;
|
||||
try {
|
||||
// transfer bytes to target file
|
||||
try {
|
||||
boolean copied = false;
|
||||
if (!directCopyNotSupported) {
|
||||
// copy bytes to target using platform function
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
transfer(fo, fi, addressToPollForCancel);
|
||||
int res = directCopy0(fo, fi, addressToPollForCancel);
|
||||
if (res == 0) {
|
||||
copied = true;
|
||||
} else if (res == IOStatus.UNSUPPORTED) {
|
||||
directCopyNotSupported = true;
|
||||
}
|
||||
} catch (UnixException x) {
|
||||
x.rethrowAsIOException(source, target);
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
} catch (UnixException x) {
|
||||
x.rethrowAsIOException(source, target);
|
||||
}
|
||||
|
||||
if (!copied) {
|
||||
// copy bytes to target via a temporary direct buffer
|
||||
int bufferSize = temporaryBufferSize(source, target);
|
||||
ByteBuffer buf = Util.getTemporaryDirectBuffer(bufferSize);
|
||||
try {
|
||||
long comp = Blocker.begin();
|
||||
try {
|
||||
bufferedCopy0(fo, fi, ((DirectBuffer)buf).address(),
|
||||
bufferSize, addressToPollForCancel);
|
||||
} catch (UnixException x) {
|
||||
x.rethrowAsIOException(source, target);
|
||||
} finally {
|
||||
Blocker.end(comp);
|
||||
}
|
||||
} finally {
|
||||
Util.releaseTemporaryDirectBuffer(buf);
|
||||
}
|
||||
}
|
||||
|
||||
// copy owner/permissions
|
||||
if (flags.copyPosixAttributes) {
|
||||
try {
|
||||
|
@ -628,7 +702,38 @@ class UnixCopyFile {
|
|||
|
||||
// -- native methods --
|
||||
|
||||
static native void transfer(int dst, int src, long addressToPollForCancel)
|
||||
/**
|
||||
* Copies data between file descriptors {@code src} and {@code dst} using
|
||||
* a platform-specific function or system call possibly having kernel
|
||||
* support.
|
||||
*
|
||||
* @param dst destination file descriptor
|
||||
* @param src source file descriptor
|
||||
* @param addressToPollForCancel address to check for cancellation
|
||||
* (a non-zero value written to this address indicates cancel)
|
||||
*
|
||||
* @return 0 on success, UNAVAILABLE if the platform function would block,
|
||||
* UNSUPPORTED_CASE if the call does not work with the given
|
||||
* parameters, or UNSUPPORTED if direct copying is not supported
|
||||
* on this platform
|
||||
*/
|
||||
private static native int directCopy0(int dst, int src,
|
||||
long addressToPollForCancel)
|
||||
throws UnixException;
|
||||
|
||||
/**
|
||||
* Copies data between file descriptors {@code src} and {@code dst} using
|
||||
* an intermediate temporary direct buffer.
|
||||
*
|
||||
* @param dst destination file descriptor
|
||||
* @param src source file descriptor
|
||||
* @param address the address of the temporary direct buffer's array
|
||||
* @param size the size of the temporary direct buffer's array
|
||||
* @param addressToPollForCancel address to check for cancellation
|
||||
* (a non-zero value written to this address indicates cancel)
|
||||
*/
|
||||
private static native void bufferedCopy0(int dst, int src, long address,
|
||||
int size, long addressToPollForCancel)
|
||||
throws UnixException;
|
||||
|
||||
static {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2008, 2022, 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
|
||||
|
@ -27,11 +27,15 @@
|
|||
#include "jni_util.h"
|
||||
#include "jlong.h"
|
||||
|
||||
#include "nio.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/sendfile.h>
|
||||
#include <fcntl.h>
|
||||
#elif defined(_ALLBSD_SOURCE)
|
||||
#include <copyfile.h>
|
||||
#endif
|
||||
|
@ -68,14 +72,28 @@ int fcopyfile_callback(int what, int stage, copyfile_state_t state,
|
|||
}
|
||||
#endif
|
||||
|
||||
// Transfer via user-space buffers
|
||||
void transfer(JNIEnv* env, jint dst, jint src, volatile jint* cancel)
|
||||
// Copy via an intermediate temporary direct buffer
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_nio_fs_UnixCopyFile_bufferCopy0
|
||||
(JNIEnv* env, jclass this, jint dst, jint src, jlong address,
|
||||
jint transferSize, jlong cancelAddress)
|
||||
{
|
||||
char buf[8192];
|
||||
volatile jint* cancel = (jint*)jlong_to_ptr(cancelAddress);
|
||||
|
||||
char* buf = (char*)address;
|
||||
|
||||
#if defined(__linux__)
|
||||
int advice = POSIX_FADV_SEQUENTIAL | // sequential data access
|
||||
POSIX_FADV_NOREUSE | // will access only once
|
||||
POSIX_FADV_WILLNEED; // will access in near future
|
||||
|
||||
// ignore the return value hence any failure
|
||||
posix_fadvise(src, 0, 0, advice);
|
||||
#endif
|
||||
|
||||
for (;;) {
|
||||
ssize_t n, pos, len;
|
||||
RESTARTABLE(read((int)src, &buf, sizeof(buf)), n);
|
||||
RESTARTABLE(read((int)src, buf, transferSize), n);
|
||||
if (n <= 0) {
|
||||
if (n < 0)
|
||||
throwUnixException(env, errno);
|
||||
|
@ -101,12 +119,18 @@ void transfer(JNIEnv* env, jint dst, jint src, volatile jint* cancel)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer all bytes from src to dst within the kernel if possible (Linux),
|
||||
* otherwise via user-space buffers
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_nio_fs_UnixCopyFile_transfer
|
||||
// Copy all bytes from src to dst, within the kernel if possible (Linux),
|
||||
// and return zero, otherwise return the appropriate status code.
|
||||
//
|
||||
// Return value
|
||||
// 0 on success
|
||||
// IOS_UNAVAILABLE if the platform function would block
|
||||
// IOS_UNSUPPORTED_CASE if the call does not work with the given parameters
|
||||
// IOS_UNSUPPORTED if direct copying is not supported on this platform
|
||||
// IOS_THROWN if a Java exception is thrown
|
||||
//
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_nio_fs_UnixCopyFile_directCopy0
|
||||
(JNIEnv* env, jclass this, jint dst, jint src, jlong cancelAddress)
|
||||
{
|
||||
volatile jint* cancel = (jint*)jlong_to_ptr(cancelAddress);
|
||||
|
@ -119,20 +143,21 @@ Java_sun_nio_fs_UnixCopyFile_transfer
|
|||
ssize_t bytes_sent;
|
||||
do {
|
||||
RESTARTABLE(sendfile64(dst, src, NULL, count), bytes_sent);
|
||||
if (bytes_sent == -1) {
|
||||
if (errno == EINVAL || errno == ENOSYS) {
|
||||
// Fall back to copying via user-space buffers
|
||||
transfer(env, dst, src, cancel);
|
||||
} else {
|
||||
throwUnixException(env, errno);
|
||||
}
|
||||
return;
|
||||
if (bytes_sent < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return IOS_UNAVAILABLE;
|
||||
if (errno == EINVAL || errno == ENOSYS)
|
||||
return IOS_UNSUPPORTED_CASE;
|
||||
throwUnixException(env, errno);
|
||||
return IOS_THROWN;
|
||||
}
|
||||
if (cancel != NULL && *cancel != 0) {
|
||||
throwUnixException(env, ECANCELED);
|
||||
return;
|
||||
return IOS_THROWN;
|
||||
}
|
||||
} while (bytes_sent > 0);
|
||||
|
||||
return 0;
|
||||
#elif defined(_ALLBSD_SOURCE)
|
||||
copyfile_state_t state;
|
||||
if (cancel != NULL) {
|
||||
|
@ -147,11 +172,13 @@ Java_sun_nio_fs_UnixCopyFile_transfer
|
|||
if (state != NULL)
|
||||
copyfile_state_free(state);
|
||||
throwUnixException(env, errno_fcopyfile);
|
||||
return;
|
||||
return IOS_THROWN;
|
||||
}
|
||||
if (state != NULL)
|
||||
copyfile_state_free(state);
|
||||
|
||||
return 0;
|
||||
#else
|
||||
transfer(env, dst, src, cancel);
|
||||
return IOS_UNSUPPORTED;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2008, 2022, 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
|
||||
|
@ -132,6 +132,7 @@ class WindowsConstants {
|
|||
// copy flags
|
||||
public static final int COPY_FILE_FAIL_IF_EXISTS = 0x00000001;
|
||||
public static final int COPY_FILE_COPY_SYMLINK = 0x00000800;
|
||||
public static final int COPY_FILE_NO_BUFFERING = 0x00001000;
|
||||
|
||||
// move flags
|
||||
public static final int MOVEFILE_REPLACE_EXISTING = 0x00000001;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2008, 2022, 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
|
||||
|
@ -25,8 +25,14 @@
|
|||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.AtomicMoveNotSupportedException;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.DirectoryNotEmptyException;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.LinkPermission;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import static sun.nio.fs.WindowsNativeDispatcher.*;
|
||||
|
@ -37,6 +43,9 @@ import static sun.nio.fs.WindowsConstants.*;
|
|||
*/
|
||||
|
||||
class WindowsFileCopy {
|
||||
// file size above which copying uses unbuffered I/O
|
||||
private static final long UNBUFFERED_IO_THRESHOLD = 314572800; // 300 MiB
|
||||
|
||||
private WindowsFileCopy() {
|
||||
}
|
||||
|
||||
|
@ -174,7 +183,9 @@ class WindowsFileCopy {
|
|||
|
||||
// Use CopyFileEx if the file is not a directory or junction
|
||||
if (!sourceAttrs.isDirectory() && !sourceAttrs.isDirectoryLink()) {
|
||||
final int flags = (!followLinks) ? COPY_FILE_COPY_SYMLINK : 0;
|
||||
boolean isBuffering = sourceAttrs.size() <= UNBUFFERED_IO_THRESHOLD;
|
||||
final int flags = (followLinks ? 0 : COPY_FILE_COPY_SYMLINK) |
|
||||
(isBuffering ? 0 : COPY_FILE_NO_BUFFERING);
|
||||
|
||||
if (interruptible) {
|
||||
// interruptible copy
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue