8373409: java/net/httpclient/http3/H3ErrorHandlingTest.java failed due to deadlock

Reviewed-by: dfuchs
This commit is contained in:
Daniel Jeliński 2025-12-17 07:49:58 +00:00
parent 94c51ce314
commit 386ad61458
2 changed files with 31 additions and 36 deletions

View File

@ -1581,34 +1581,30 @@ public abstract sealed class QuicEndpoint implements AutoCloseable
peerIssuedResetTokens.replaceAll((tok, c) -> c == from ? to : c);
}
public void draining(final QuicPacketReceiver connection) {
public void draining(final QuicConnectionImpl connection) {
// remap the connection to a DrainingConnection
if (closed) return;
final long idleTimeout = connection.peerPtoMs() * 3; // 3 PTO
connection.localConnectionIdManager().close();
DrainingConnection draining = new DrainingConnection(connection.connectionIds(), idleTimeout);
// we can ignore stateless reset in the draining state.
remapPeerIssuedResetToken(connection, draining);
connection.connectionIds().forEach((id) ->
connections.compute(id, this::remapDraining));
connections.compute(id, (i, r) -> remapDraining(i, r, draining)));
draining.startTimer();
assert !connections.containsValue(connection) : connection;
}
private DrainingConnection remapDraining(QuicConnectionId id, QuicPacketReceiver conn) {
private DrainingConnection remapDraining(QuicConnectionId id, QuicPacketReceiver conn, DrainingConnection draining) {
if (closed) return null;
var debugOn = debug.on() && !Thread.currentThread().isVirtual();
if (conn instanceof ClosingConnection closing) {
if (conn instanceof QuicConnectionImpl || conn instanceof ClosingConnection) {
if (debugOn) debug.log("remapping %s to DrainingConnection", id);
final var draining = closing.toDraining();
remapPeerIssuedResetToken(closing, draining);
draining.startTimer();
return draining;
} else if (conn instanceof DrainingConnection draining) {
return draining;
} else if (conn instanceof QuicConnectionImpl impl) {
final long idleTimeout = impl.peerPtoMs() * 3; // 3 PTO
impl.localConnectionIdManager().close();
if (debugOn) debug.log("remapping %s to DrainingConnection", id);
var draining = new DrainingConnection(conn.connectionIds(), idleTimeout);
// we can ignore stateless reset in the draining state.
remapPeerIssuedResetToken(impl, draining);
draining.startTimer();
return draining;
} else if (conn instanceof DrainingConnection d) {
return d;
} else if (conn == null) {
// connection absent (was probably removed), don't remap to draining
if (debugOn) {
@ -1623,30 +1619,32 @@ public abstract sealed class QuicEndpoint implements AutoCloseable
protected void closing(QuicConnectionImpl connection, ByteBuffer datagram) {
if (closed) return;
ByteBuffer closing = ByteBuffer.allocate(datagram.limit());
closing.put(datagram.slice());
closing.flip();
ByteBuffer closingDatagram = ByteBuffer.allocate(datagram.limit());
closingDatagram.put(datagram.slice());
closingDatagram.flip();
final long idleTimeout = connection.peerPtoMs() * 3; // 3 PTO
connection.localConnectionIdManager().close();
var closingConnection = new ClosingConnection(connection.connectionIds(), idleTimeout, datagram);
remapPeerIssuedResetToken(connection, closingConnection);
connection.connectionIds().forEach((id) ->
connections.compute(id, (i, r) -> remapClosing(i, r, closing)));
connections.compute(id, (i, r) -> remapClosing(i, r, closingConnection)));
closingConnection.startTimer();
assert !connections.containsValue(connection) : connection;
}
private ClosedConnection remapClosing(QuicConnectionId id, QuicPacketReceiver conn, ByteBuffer datagram) {
private ClosedConnection remapClosing(QuicConnectionId id, QuicPacketReceiver conn, ClosingConnection closingConnection) {
if (closed) return null;
var debugOn = debug.on() && !Thread.currentThread().isVirtual();
if (conn instanceof ClosingConnection closing) {
if (conn instanceof QuicConnectionImpl) {
if (debugOn) debug.log("remapping %s to ClosingConnection", id);
return closingConnection;
} else if (conn instanceof ClosingConnection closing) {
// we already have a closing datagram, drop the new one
return closing;
} else if (conn instanceof DrainingConnection draining) {
return draining;
} else if (conn instanceof QuicConnectionImpl impl) {
final long idleTimeout = impl.peerPtoMs() * 3; // 3 PTO
impl.localConnectionIdManager().close();
if (debugOn) debug.log("remapping %s to ClosingConnection", id);
var closing = new ClosingConnection(conn.connectionIds(), idleTimeout, datagram);
remapPeerIssuedResetToken(impl, closing);
closing.startTimer();
return closing;
} else if (conn == null) {
// connection absent (was probably removed), don't remap to closing
if (debugOn) {
@ -1896,10 +1894,6 @@ public abstract sealed class QuicEndpoint implements AutoCloseable
debug.log("ClosingConnection(%s): dropping %s packet", localConnectionIds, headersType);
}
}
private DrainingConnection toDraining() {
return new DrainingConnection(localConnectionIds, maxIdleTimeMs);
}
}
/**

View File

@ -71,6 +71,7 @@ import static org.testng.Assert.*;
/*
* @test
* @bug 8373409
* @key intermittent
* @comment testResetControlStream may fail if the client doesn't read the stream type
* before the stream is reset,