Skip to content

Commit ace010b

Browse files
author
Alan Bateman
committed
8319757: java/nio/channels/DatagramChannel/InterruptibleOrNot.java failed: wrong exception thrown
Reviewed-by: jpai, bpb
1 parent be4614e commit ace010b

File tree

1 file changed

+132
-106
lines changed

1 file changed

+132
-106
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -25,7 +25,7 @@
2525
* @test
2626
* @bug 8236246
2727
* @modules java.base/sun.nio.ch
28-
* @run testng InterruptibleOrNot
28+
* @run junit InterruptibleOrNot
2929
* @summary Test SelectorProviderImpl.openDatagramChannel(boolean) to create
3030
* DatagramChannel objects that optionally support interrupt
3131
*/
@@ -40,152 +40,178 @@
4040
import java.nio.channels.ClosedByInterruptException;
4141
import java.nio.channels.DatagramChannel;
4242
import java.time.Duration;
43-
import java.util.concurrent.Executors;
44-
import java.util.concurrent.Future;
45-
import java.util.concurrent.ScheduledExecutorService;
46-
import java.util.concurrent.TimeUnit;
43+
import java.util.Arrays;
4744
import sun.nio.ch.DefaultSelectorProvider;
4845

49-
import org.testng.annotations.Test;
50-
import static org.testng.Assert.*;
46+
import org.junit.jupiter.api.Test;
47+
import org.junit.jupiter.api.BeforeAll;
48+
import org.junit.jupiter.api.function.Executable;
49+
import static org.junit.jupiter.api.Assertions.*;
5150

52-
@Test
5351
public class InterruptibleOrNot {
52+
// DatagramChannel implementation class
53+
private static String dcImplClassName;
5454

55-
public void testInterruptBeforeInterruptibleReceive() throws Exception {
56-
testInterruptBeforeReceive(true);
57-
}
58-
59-
public void testInterruptDuringInterruptibleReceive() throws Exception {
60-
testInterruptDuringReceive(true);
61-
}
62-
63-
public void testInterruptBeforeUninterruptibleReceive() throws Exception {
64-
testInterruptBeforeReceive(false);
65-
}
66-
67-
public void testInterruptDuringUninterruptibleReceive() throws Exception {
68-
testInterruptDuringReceive(false);
69-
}
70-
71-
public void testInterruptBeforeInterruptibleSend() throws Exception {
72-
testInterruptBeforeSend(true);
55+
@BeforeAll
56+
static void setup() throws Exception {
57+
try (DatagramChannel dc = boundDatagramChannel(true)) {
58+
dcImplClassName = dc.getClass().getName();
59+
}
7360
}
7461

75-
public void testInterruptBeforeUninterruptibleSend() throws Exception {
76-
testInterruptBeforeSend(false);
62+
/**
63+
* Call DatagramChannel.receive with the interrupt status set, the DatagramChannel
64+
* is interruptible.
65+
*/
66+
@Test
67+
public void testInterruptBeforeInterruptibleReceive() throws Exception {
68+
try (DatagramChannel dc = boundDatagramChannel(true)) {
69+
ByteBuffer buf = ByteBuffer.allocate(100);
70+
Thread.currentThread().interrupt();
71+
assertThrows(ClosedByInterruptException.class, () -> dc.receive(buf));
72+
assertFalse(dc.isOpen());
73+
} finally {
74+
Thread.interrupted(); // clear interrupt status
75+
}
7776
}
7877

7978
/**
80-
* Test invoking DatagramChannel receive with interrupt status set
79+
* Test interrupting a thread blocked in DatagramChannel.receive, the DatagramChannel
80+
* is interruptible.
8181
*/
82-
static void testInterruptBeforeReceive(boolean interruptible)
83-
throws Exception
84-
{
85-
try (DatagramChannel dc = openDatagramChannel(interruptible)) {
86-
dc.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
87-
Future<?> timeout = scheduleClose(dc, Duration.ofSeconds(2));
88-
try {
89-
ByteBuffer buf = ByteBuffer.allocate(100);
90-
Thread.currentThread().interrupt();
91-
assertThrows(expectedException(interruptible), () -> dc.receive(buf));
92-
} finally {
93-
timeout.cancel(false);
94-
}
82+
@Test
83+
public void testInterruptDuringInterruptibleReceive() throws Exception {
84+
try (DatagramChannel dc = boundDatagramChannel(true)) {
85+
ByteBuffer buf = ByteBuffer.allocate(100);
86+
Thread thread = Thread.currentThread();
87+
onReceive(thread::interrupt);
88+
assertThrows(ClosedByInterruptException.class, () -> dc.receive(buf));
89+
assertFalse(dc.isOpen());
9590
} finally {
96-
Thread.interrupted(); // clear interrupt
91+
Thread.interrupted(); // clear interrupt status
9792
}
9893
}
9994

10095
/**
101-
* Test Thread.interrupt when target thread is blocked in DatagramChannel receive
96+
* Call DatagramChannel.receive with the interrupt status set, the DatagramChannel
97+
* is not interruptible.
10298
*/
103-
static void testInterruptDuringReceive(boolean interruptible)
104-
throws Exception
105-
{
106-
try (DatagramChannel dc = openDatagramChannel(interruptible)) {
107-
dc.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
108-
Future<?> timerTask = scheduleClose(dc, Duration.ofSeconds(5));
109-
Future<?> interruptTask = scheduleInterrupt(Thread.currentThread(), Duration.ofSeconds(1));
110-
try {
111-
ByteBuffer buf = ByteBuffer.allocate(100);
112-
assertThrows(expectedException(interruptible), () -> dc.receive(buf));
113-
} finally {
114-
timerTask.cancel(false);
115-
interruptTask.cancel(false);
116-
}
99+
@Test
100+
public void testInterruptBeforeUninterruptibleReceive() throws Exception {
101+
try (DatagramChannel dc = boundDatagramChannel(false)) {
102+
ByteBuffer buf = ByteBuffer.allocate(100);
103+
onReceive(() -> {
104+
// close the channel after a delay to ensure receive wakes up
105+
Thread.sleep(1000);
106+
dc.close();
107+
});
108+
Thread.currentThread().interrupt();
109+
assertThrows(AsynchronousCloseException.class, () -> dc.receive(buf));
110+
assertFalse(dc.isOpen());
117111
} finally {
118-
Thread.interrupted(); // clear interrupt
112+
Thread.interrupted(); // clear interrupt status
119113
}
120114
}
121115

122116
/**
123-
* Test invoking DatagramChannel send with interrupt status set
117+
* Test interrupting a thread blocked in DatagramChannel.receive, the DatagramChannel
118+
* is not interruptible.
124119
*/
125-
static void testInterruptBeforeSend(boolean interruptible)
126-
throws Exception
127-
{
128-
try (DatagramChannel dc = openDatagramChannel(interruptible)) {
129-
dc.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
130-
Future<?> timeout = scheduleClose(dc, Duration.ofSeconds(2));
131-
try {
132-
ByteBuffer buf = ByteBuffer.allocate(100);
133-
SocketAddress target = dc.getLocalAddress();
134-
Thread.currentThread().interrupt();
135-
if (interruptible) {
136-
assertThrows(ClosedByInterruptException.class, () -> dc.send(buf, target));
137-
} else {
138-
int n = dc.send(buf, target);
139-
assertTrue(n == 100);
140-
}
141-
} finally {
142-
timeout.cancel(false);
143-
}
120+
@Test
121+
public void testInterruptDuringUninterruptibleReceive() throws Exception {
122+
try (DatagramChannel dc = boundDatagramChannel(true)) {
123+
ByteBuffer buf = ByteBuffer.allocate(100);
124+
125+
Thread thread = Thread.currentThread();
126+
onReceive(() -> {
127+
// interrupt should not cause the receive to wakeup
128+
thread.interrupt();
129+
130+
// close the channel after a delay to ensure receive wakes up
131+
Thread.sleep(1000);
132+
dc.close();
133+
});
134+
assertThrows(AsynchronousCloseException.class, () -> dc.receive(buf));
135+
assertFalse(dc.isOpen());
144136
} finally {
145-
Thread.interrupted(); // clear interrupt
137+
Thread.interrupted(); // clear interrupt status
146138
}
147139
}
148140

149141
/**
150-
* Creates a DatagramChannel that is interruptible or not.
142+
* Call DatagramChannel.send with the interrupt status set, the DatagramChannel
143+
* is interruptible.
151144
*/
152-
static DatagramChannel openDatagramChannel(boolean interruptible) throws IOException {
153-
if (interruptible) {
154-
return DatagramChannel.open();
155-
} else {
156-
return DefaultSelectorProvider.get().openUninterruptibleDatagramChannel();
145+
@Test
146+
public void testInterruptBeforeInterruptibleSend() throws Exception {
147+
try (DatagramChannel dc = boundDatagramChannel(true)) {
148+
ByteBuffer buf = ByteBuffer.allocate(100);
149+
SocketAddress target = dc.getLocalAddress();
150+
Thread.currentThread().interrupt();
151+
assertThrows(ClosedByInterruptException.class, () -> dc.send(buf, target));
152+
assertFalse(dc.isOpen());
153+
} finally {
154+
Thread.interrupted(); // clear interrupt
157155
}
158156
}
159157

160158
/**
161-
* Expect ClosedByInterruptException if interruptible.
159+
* Call DatagramChannel.send with the interrupt status set, the DatagramChannel
160+
* is not interruptible.
162161
*/
163-
static Class<? extends Exception> expectedException(boolean expectInterrupt) {
164-
if (expectInterrupt) {
165-
return ClosedByInterruptException.class;
166-
} else {
167-
return AsynchronousCloseException.class;
162+
@Test
163+
public void testInterruptBeforeUninterruptibleSend() throws Exception {
164+
try (DatagramChannel dc = boundDatagramChannel(false)) {
165+
ByteBuffer buf = ByteBuffer.allocate(100);
166+
SocketAddress target = dc.getLocalAddress();
167+
Thread.currentThread().interrupt();
168+
int n = dc.send(buf, target);
169+
assertEquals(100, n);
170+
assertTrue(dc.isOpen());
171+
} finally {
172+
Thread.interrupted(); // clear interrupt status
168173
}
169174
}
170175

171176
/**
172-
* Schedule the given object to be closed.
177+
* Creates a DatagramChannel that is interruptible or not, and bound to the loopback
178+
* address.
173179
*/
174-
static Future<?> scheduleClose(Closeable c, Duration timeout) {
175-
long nanos = TimeUnit.NANOSECONDS.convert(timeout);
176-
return STPE.schedule(() -> {
177-
c.close();
178-
return null;
179-
}, nanos, TimeUnit.NANOSECONDS);
180+
static DatagramChannel boundDatagramChannel(boolean interruptible) throws IOException {
181+
DatagramChannel dc;
182+
if (interruptible) {
183+
dc = DatagramChannel.open();
184+
} else {
185+
dc = DefaultSelectorProvider.get().openUninterruptibleDatagramChannel();
186+
}
187+
try {
188+
dc.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
189+
} catch (IOException ioe) {
190+
dc.close();
191+
throw ioe;
192+
}
193+
return dc;
180194
}
181195

182196
/**
183-
* Schedule the given thread to be interrupted.
197+
* Runs the given action when the current thread is sampled in DatagramChannel.receive.
184198
*/
185-
static Future<?> scheduleInterrupt(Thread t, Duration timeout) {
186-
long nanos = TimeUnit.NANOSECONDS.convert(timeout);
187-
return STPE.schedule(t::interrupt, nanos, TimeUnit.NANOSECONDS);
199+
static void onReceive(Executable action) {
200+
Thread target = Thread.currentThread();
201+
Thread.ofPlatform().daemon().start(() -> {
202+
try {
203+
boolean found = false;
204+
while (!found) {
205+
Thread.sleep(20);
206+
StackTraceElement[] stack = target.getStackTrace();
207+
found = Arrays.stream(stack)
208+
.anyMatch(e -> dcImplClassName.equals(e.getClassName())
209+
&& "receive".equals(e.getMethodName()));
210+
}
211+
action.execute();
212+
} catch (Throwable ex) {
213+
ex.printStackTrace();
214+
}
215+
});
188216
}
189-
190-
static final ScheduledExecutorService STPE = Executors.newScheduledThreadPool(0);
191217
}

0 commit comments

Comments
 (0)