1 /*
2  * Copyright (c) 2013, 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 8012019
26  * @summary Tests interruption of threads doing position-based read methods in
27  *   an attempt to provoke a deadlock between position sensitive and position
28  *   insensitive methods
29  */
30 import java.nio.ByteBuffer;
31 import java.nio.channels.*;
32 import java.nio.file.*;
33 import static java.nio.file.StandardOpenOption.*;
34 
35 public class InterruptDeadlock {
36 
37     /**
38      * A thread that continuously reads from a FileChannel with
39      * read(ByteBuffer,long). The thread terminates when interrupted and/or
40      * the FileChannel is closed.
41      */
42     static class Reader extends Thread {
43         final FileChannel fc;
44         volatile Exception exception;
45 
Reader(FileChannel fc)46         Reader(FileChannel fc) {
47             this.fc = fc;
48         }
49 
50         @Override
run()51         public void run() {
52             ByteBuffer bb = ByteBuffer.allocate(1024);
53             try {
54                 long pos = 0L;
55                 for (;;) {
56                     bb.clear();
57                     int n = fc.read(bb, pos);
58                     if (n > 0)
59                         pos += n;
60                     // fc.size is important here as it is position sensitive
61                     if (pos > fc.size())
62                         pos = 0L;
63                 }
64             } catch (ClosedChannelException x) {
65                 System.out.println(x.getClass() + " (expected)");
66             } catch (Exception unexpected) {
67                 this.exception = unexpected;
68             }
69         }
70 
exception()71         Exception exception() {
72             return exception;
73         }
74 
startReader(FileChannel fc)75         static Reader startReader(FileChannel fc) {
76             Reader r = new Reader(fc);
77             r.start();
78             return r;
79         }
80     }
81 
82     // the number of reader threads to start
83     private static final int READER_COUNT = 4;
84 
main(String[] args)85     public static void main(String[] args) throws Exception {
86         Path file = Paths.get("data.txt");
87         try (FileChannel fc = FileChannel.open(file, CREATE, TRUNCATE_EXISTING, WRITE)) {
88             fc.position(1024L * 1024L);
89             fc.write(ByteBuffer.wrap(new byte[1]));
90         }
91 
92         Reader[] readers = new Reader[READER_COUNT];
93 
94         for (int i=1; i<=20; i++) {
95             System.out.format("Iteration: %s%n", i);
96 
97             try (FileChannel fc = FileChannel.open(file)) {
98                 boolean failed = false;
99 
100                 // start reader threads
101                 for (int j=0; j<READER_COUNT; j++) {
102                     readers[j] = Reader.startReader(fc);
103                 }
104 
105                 // give readers a bit of time to get started (not strictly required)
106                 Thread.sleep(100);
107 
108                 // interrupt and wait for the readers to terminate
109                 for (Reader r: readers) {
110                     r.interrupt();
111                 }
112                 for (Reader r: readers) {
113                     try {
114                         r.join(10000);
115                         Exception e = r.exception();
116                         if (e != null) {
117                             System.err.println("Reader thread failed with: " + e);
118                             failed = true;
119                         }
120                     } catch (InterruptedException x) {
121                         System.err.println("Reader thread did not terminte");
122                         failed = true;
123                     }
124                 }
125 
126                 // the channel should not be open at this point
127                 if (fc.isOpen()) {
128                     System.err.println("FileChannel was not closed");
129                     failed = true;
130                 }
131 
132                 if (failed)
133                     throw new RuntimeException("Test failed - see log for details");
134             }
135         }
136     }
137 }
138