test/jdk/java/nio/channels/FileChannel/FileExtensionAndMap.java
author bpb
Mon, 30 Apr 2018 13:40:39 -0700
changeset 49930 3aaaa5370999
parent 47216 71c04702a3d5
permissions -rw-r--r--
8202284: FileChannel and FileOutpuStream variants of AtomicAppend should fail silently on macOS >= 10.13 Reviewed-by: chegar

/*
 * Copyright (c) 2016, 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
 * @ignore This test has huge disk space requirements
 * @bug 8168628
 * @summary Test extending files to very large sizes without hitting a SIGBUS
 * @requires (os.family == "linux")
 * @run main/othervm/timeout=600 -Xms4g -Xmx4g FileExtensionAndMap
 * @run main/othervm/timeout=600 -Xms4g -Xmx4g FileExtensionAndMap true
 */

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Semaphore;
import java.util.stream.IntStream;

public class FileExtensionAndMap {

    private static final ExecutorService CACHED_EXECUTORSERVICE =
        Executors.newCachedThreadPool();

    private static final String TMPDIR = System.getProperty("test.dir", ".");

    private static boolean useRaf = false;

    public static void main(String args[]) throws Exception {
        if (args.length > 2) {
            throw new IllegalArgumentException
                ("Arguments: [true|false [targetFolder]]");
        }

        String defaultFolder = TMPDIR + File.separator + "target";
        if (args.length > 0) {
            useRaf = Boolean.valueOf(args[0]);
            if (args.length > 1) {
                defaultFolder = args[1];
            }
        }
        final String targetFolder = defaultFolder;
        Path p = Paths.get(targetFolder);
        boolean targetExists = Files.exists(p);
        if (!targetExists) {
            Files.createDirectory(p);
        }

        System.out.printf("Using RandomAccessFile: %s; target folder: %s%n",
            useRaf, targetFolder);

        ForkJoinPool fjPool = new ForkJoinPool(3);
        fjPool.submit(() -> {
            IntStream.range(0, 20).parallel().forEach((index) -> {
                String fileName = "testBigFile_" + index + ".dat";
                Path source = null;
                Path target = null;
                try {
                    source = Paths.get(TMPDIR, fileName);
                    testCreateBigFile(source);
                    target = Paths.get(targetFolder, fileName);
                    testFileCopy(source, target);
                } catch (Throwable th) {
                    System.err.println("Error copying file with fileName: "
                        + fileName + " : " + th.getMessage());
                    th.printStackTrace(System.err);
                } finally {
                    try {
                        if (source != null) {
                            Files.deleteIfExists(source);
                        }
                    } catch (Throwable ignored) {
                    }
                    try {
                        if (target != null) {
                            Files.deleteIfExists(target);
                        }
                    } catch (Throwable ignored) {
                    }
                }
            });
        }).join();

        if (!targetExists) {
            Files.delete(p);
        }
    }

    private static void testFileCopy(Path source, Path target)
        throws IOException {
        Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
        System.out.println("Finished copying file with fileName: "
                + source.getFileName());
    }

    private static void testCreateBigFile(Path segmentFile)
        throws IOException {
        final Semaphore concurrencySemaphore = new Semaphore(5);
        long fileSize = 3L * 1024L * 1024L * 1024L;
        int blockSize = 10 * 1024 * 1024;
        int loopCount = (int) Math.floorDiv(fileSize, blockSize);

        String fileName = segmentFile.getFileName().toString();
        if (useRaf) {
            try (RandomAccessFile raf
                = new RandomAccessFile(segmentFile.toFile(), "rw")) {
                raf.setLength(fileSize);
                try (FileChannel fc = raf.getChannel()) {
                    for (int i = 0; i < loopCount; i++) {
                        final long startPosition = 1L * blockSize * i;
                        concurrencySemaphore.acquireUninterruptibly();
                        CACHED_EXECUTORSERVICE.submit(() -> {
                            writeTemplateData(fileName, fc, startPosition,
                                    blockSize, concurrencySemaphore);
                        });
                    }
                } finally {
                    concurrencySemaphore.acquireUninterruptibly(5);
                }
            }
        } else {
            Path file = Files.createFile(segmentFile);
            try (FileChannel fc = FileChannel.open(file,
                StandardOpenOption.READ, StandardOpenOption.WRITE)) {
                for (int i = 0; i < loopCount; i++) {
                    final long startPosition = 1L * blockSize * i;
                    concurrencySemaphore.acquireUninterruptibly();
                    CACHED_EXECUTORSERVICE.submit(() -> {
                        writeTemplateData(fileName, fc, startPosition,
                                blockSize, concurrencySemaphore);
                    });
                }
            }
        }
    }

    private static void writeTemplateData(String fileName,
        FileChannel fc, long startPosition, int blockSize,
        Semaphore concurrencySemaphore) {
        try {
            byte[] EMPTY_RECORD = new byte[blockSize / 256];

            MappedByteBuffer mappedByteBuffer = fc.map(MapMode.READ_WRITE,
                startPosition, blockSize);
            IntStream.range(0, 256).forEach((recordIndex) -> {
                try {
                    mappedByteBuffer.position((int) (recordIndex *
                        EMPTY_RECORD.length));
                    mappedByteBuffer.put(EMPTY_RECORD, 0, EMPTY_RECORD.length);
                } catch (Throwable th) {
                    System.err.println
                        ("Error in FileExtensionAndMap.writeTemplateData empty record for fileName: "
                        + fileName + ", startPosition: " + startPosition + ", recordIndex: "
                        + recordIndex + " : " + th.getMessage());
                    th.printStackTrace(System.err);
                }
            });

            mappedByteBuffer.force();
        } catch (Throwable th) {
            if (!(th instanceof ClosedChannelException)) {
                System.err.println
                    ("Error in FileExtensionAndMap.writeTemplateData empty record for fileName: "
                    + fileName + ", startPosition: " + startPosition + " : "
                    + th.getMessage());
                th.printStackTrace(System.err);
            }
        } finally {
            concurrencySemaphore.release();
        }
    }
}