3048N/A/*
3048N/A * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
3048N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3048N/A *
3048N/A * This code is free software; you can redistribute it and/or modify it
3048N/A * under the terms of the GNU General Public License version 2 only, as
3048N/A * published by the Free Software Foundation.
3048N/A *
3048N/A * This code is distributed in the hope that it will be useful, but WITHOUT
3048N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
3048N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
3048N/A * version 2 for more details (a copy is included in the LICENSE file that
3048N/A * accompanied this code).
3048N/A *
3048N/A * You should have received a copy of the GNU General Public License version
3048N/A * 2 along with this work; if not, write to the Free Software Foundation,
3048N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
3048N/A *
3048N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
3048N/A * or visit www.oracle.com if you need additional information or have any
3048N/A * questions.
3048N/A */
3048N/A
3048N/A/* @test
3048N/A * @bug 6979009
3048N/A * @summary Ensure ClosedByInterruptException is thrown when I/O operation
3048N/A * interrupted by Thread.interrupt
3048N/A */
3048N/A
3048N/Aimport java.io.*;
3048N/Aimport java.util.Random;
3048N/Aimport java.nio.ByteBuffer;
3048N/Aimport java.nio.channels.*;
3048N/A
3048N/Apublic class ClosedByInterrupt {
3048N/A
3048N/A static final int K = 1024;
3048N/A static final Random rand = new Random();
3048N/A
3048N/A static volatile boolean failed;
3048N/A
3048N/A public static void main(String[] args) throws Exception {
3048N/A File f = File.createTempFile("blah", null);
3048N/A f.deleteOnExit();
3048N/A
3048N/A // create 1MB file.
3048N/A byte[] b = new byte[K*K];
3048N/A rand.nextBytes(b);
3048N/A ByteBuffer bb = ByteBuffer.wrap(b);
3048N/A try (FileChannel fc = new FileOutputStream(f).getChannel()) {
3048N/A while (bb.hasRemaining())
3048N/A fc.write(bb);
3048N/A }
3048N/A
4224N/A // test with 1-16 concurrent threads
4224N/A for (int i=1; i<=16; i++) {
3048N/A System.out.format("%d thread(s)%n", i);
3048N/A test(f, i);
3048N/A if (failed)
3048N/A break;
3048N/A }
4224N/A
4224N/A if (failed)
4224N/A throw new RuntimeException("Test failed");
3048N/A }
3048N/A
3048N/A /**
3048N/A * Starts "nThreads" that do I/O on the given file concurrently. Continuously
3048N/A * interrupts one of the threads to cause the file to be closed and
3048N/A * ClosedByInterruptException to be thrown. The other threads should "fail" with
3048N/A * ClosedChannelException (or the more specific AsynchronousCloseException).
3048N/A */
3048N/A static void test(File f, int nThreads) throws Exception {
3048N/A try (FileChannel fc = new RandomAccessFile(f, "rwd").getChannel()) {
3048N/A Thread[] threads = new Thread[nThreads];
3048N/A
3048N/A // start threads
3048N/A for (int i=0; i<nThreads; i++) {
3048N/A boolean interruptible = (i==0);
3048N/A ReaderWriter task = new ReaderWriter(fc, interruptible);
3048N/A Thread t = new Thread(task);
3048N/A t.start();
3048N/A threads[i] = t;
3048N/A }
3048N/A
3048N/A // give time for threads to start
3048N/A Thread.sleep(500 + rand.nextInt(1000));
3048N/A
3048N/A // interrupt thread until channel is closed
3048N/A while (fc.isOpen()) {
3048N/A threads[0].interrupt();
3048N/A Thread.sleep(rand.nextInt(50));
3048N/A }
3048N/A
3048N/A // wait for test to finish
3048N/A for (int i=0; i<nThreads; i++) {
3048N/A threads[i].join();
3048N/A }
3048N/A }
3048N/A }
3048N/A
3048N/A /**
3048N/A * A task that continuously reads or writes to random areas of a file
3048N/A * until the channel is closed. An "interruptible" task expects the
3048N/A * channel to be closed by an interupt, a "non-interruptible" thread
3048N/A * does not.
3048N/A */
3048N/A static class ReaderWriter implements Runnable {
3048N/A final FileChannel fc;
3048N/A final boolean interruptible;
3048N/A final boolean writer;
3048N/A
3048N/A ReaderWriter(FileChannel fc, boolean interruptible) {
3048N/A this.fc = fc;
3048N/A this.interruptible = interruptible;
3048N/A this.writer = rand.nextBoolean();
3048N/A }
3048N/A
3048N/A public void run() {
3048N/A ByteBuffer bb = ByteBuffer.allocate(K);
3048N/A if (writer)
3048N/A rand.nextBytes(bb.array());
3048N/A
3048N/A try {
3048N/A for (;;) {
3048N/A long position = rand.nextInt(K*K - bb.capacity());
3048N/A if (writer) {
3048N/A bb.position(0).limit(bb.capacity());
3048N/A fc.write(bb, position);
3048N/A } else {
3048N/A bb.clear();
3048N/A fc.read(bb, position);
3048N/A }
3048N/A if (!interruptible) {
3048N/A // give the interruptible thread a chance
3048N/A try {
3048N/A Thread.sleep(rand.nextInt(50));
4224N/A } catch (InterruptedException e) {
4224N/A unexpected(e);
4224N/A }
3048N/A }
3048N/A }
3048N/A } catch (ClosedByInterruptException e) {
3048N/A if (interruptible) {
4224N/A if (Thread.interrupted()) {
3048N/A expected(e + " thrown and interrupt status set");
3048N/A } else {
3048N/A unexpected(e + " thrown but interrupt status not set");
3048N/A }
3048N/A } else {
3048N/A unexpected(e);
3048N/A }
3048N/A } catch (ClosedChannelException e) {
3048N/A if (interruptible) {
3048N/A unexpected(e);
3048N/A } else {
3048N/A expected(e);
3048N/A }
3048N/A } catch (Exception e) {
3048N/A unexpected(e);
3048N/A }
3048N/A }
3048N/A }
3048N/A
3048N/A static void expected(Exception e) {
4224N/A System.out.format("%s (expected)%n", e);
3048N/A }
3048N/A
3048N/A static void expected(String msg) {
3048N/A System.out.format("%s (expected)%n", msg);
3048N/A }
3048N/A
3048N/A static void unexpected(Exception e) {
3048N/A System.err.format("%s (not expected)%n", e);
3048N/A failed = true;
3048N/A }
3048N/A
3048N/A static void unexpected(String msg) {
3048N/A System.err.println(msg);
3048N/A failed = true;
3048N/A }
3048N/A}