Skip to content

Commit f1c0b4e

Browse files
author
Brian Burkhalter
committed
8361495: (fc) Async close of streams connected to uninterruptible FileChannel doesn't throw AsynchronousCloseException in all cases
Reviewed-by: alanb
1 parent b43c2c6 commit f1c0b4e

File tree

2 files changed

+163
-1
lines changed

2 files changed

+163
-1
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,11 @@ private void beginBlocking() {
178178
}
179179

180180
private void endBlocking(boolean completed) throws AsynchronousCloseException {
181-
if (!uninterruptible) end(completed);
181+
if (!uninterruptible) {
182+
end(completed);
183+
} else if (!completed && !isOpen()) {
184+
throw new AsynchronousCloseException();
185+
}
182186
}
183187

184188
// -- Standard channel operations --
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
* Copyright (c) 2025, 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 8361495
26+
* @summary Test for AsynchronousCloseException from uninterruptible FileChannel
27+
* @run junit AsyncCloseStreams
28+
*/
29+
30+
import java.io.Closeable;
31+
import java.io.InputStream;
32+
import java.io.IOException;
33+
import java.io.OutputStream;
34+
import java.nio.channels.AsynchronousCloseException;
35+
import java.nio.channels.ClosedChannelException;
36+
import java.nio.file.Files;
37+
import java.nio.file.Path;
38+
import java.util.Arrays;
39+
import java.util.concurrent.LinkedTransferQueue;
40+
41+
import org.junit.jupiter.api.Test;
42+
import static org.junit.jupiter.api.Assertions.fail;
43+
44+
public class AsyncCloseStreams {
45+
private static final Closeable STOP = () -> { };
46+
47+
private static Thread startCloseThread(LinkedTransferQueue<Closeable> q) {
48+
return Thread.ofPlatform().start(() -> {
49+
try {
50+
Closeable c;
51+
while((c = q.take()) != STOP) {
52+
try {
53+
c.close();
54+
} catch (IOException ignored) {
55+
}
56+
}
57+
} catch (InterruptedException ignored) {
58+
}
59+
});
60+
}
61+
62+
@Test
63+
public void available() throws InterruptedException, IOException {
64+
var close = new LinkedTransferQueue<Closeable>();
65+
Thread closeThread = startCloseThread(close);
66+
67+
try {
68+
Path path = Files.createTempFile(Path.of("."), "foo", "bar");
69+
path.toFile().deleteOnExit();
70+
71+
do {
72+
InputStream in = Files.newInputStream(path);
73+
close.offer(in);
74+
int available = 0;
75+
try {
76+
available = in.available();
77+
} catch (AsynchronousCloseException ace) {
78+
System.err.println("AsynchronousCloseException caught");
79+
break;
80+
} catch (ClosedChannelException ignored) {
81+
continue;
82+
} catch (Throwable t) {
83+
fail("Unexpected error", t);
84+
}
85+
if (available < 0) {
86+
fail("FAILED: available < 0");
87+
}
88+
} while (true);
89+
} finally {
90+
close.offer(STOP);
91+
closeThread.join();
92+
}
93+
}
94+
95+
@Test
96+
public void read() throws InterruptedException, IOException {
97+
var close = new LinkedTransferQueue<Closeable>();
98+
Thread closeThread = startCloseThread(close);
99+
100+
try {
101+
Path path = Files.createTempFile(Path.of("."), "foo", "bar");
102+
path.toFile().deleteOnExit();
103+
byte[] bytes = new byte[100_000];
104+
Arrays.fill(bytes, (byte)27);
105+
Files.write(path, bytes);
106+
107+
do {
108+
InputStream in = Files.newInputStream(path);
109+
close.offer(in);
110+
int value = 0;
111+
try {
112+
value = in.read();
113+
} catch (AsynchronousCloseException ace) {
114+
System.err.println("AsynchronousCloseException caught");
115+
break;
116+
} catch (ClosedChannelException ignored) {
117+
continue;
118+
} catch (Throwable t) {
119+
fail("Unexpected error", t);
120+
}
121+
if (value < 0) {
122+
fail("FAILED: value < 0");
123+
}
124+
} while (true);
125+
} finally {
126+
close.offer(STOP);
127+
closeThread.join();
128+
}
129+
}
130+
131+
@Test
132+
public void write() throws InterruptedException, IOException {
133+
var close = new LinkedTransferQueue<Closeable>();
134+
Thread closeThread = startCloseThread(close);
135+
136+
try {
137+
Path path = Files.createTempFile(Path.of("."), "foo", "bar");
138+
path.toFile().deleteOnExit();
139+
140+
do {
141+
OutputStream out = Files.newOutputStream(path);
142+
close.offer(out);
143+
try {
144+
out.write(27);
145+
} catch (AsynchronousCloseException ace) {
146+
System.err.println("AsynchronousCloseException caught");
147+
break;
148+
} catch (ClosedChannelException ignored) {
149+
} catch (Throwable t) {
150+
fail("Write error", t);
151+
}
152+
} while (true);
153+
} finally {
154+
close.offer(STOP);
155+
closeThread.join();
156+
}
157+
}
158+
}

0 commit comments

Comments
 (0)