From 17f2e11fe400ade68ee3b0ac4706209aae4e14dd Mon Sep 17 00:00:00 2001 From: Daniel Fuchs Date: Mon, 29 Jun 2026 14:26:50 +0000 Subject: [PATCH] 8386989: QuicEndpoint.ClosedConnection should not use QuicTimerQueue::offer Reviewed-by: djelinski --- .../internal/net/http/quic/QuicEndpoint.java | 11 ++--- .../net/http/quic/QuicTimerQueue.java | 45 +++++++------------ .../H3MultipleConnectionsToSameHost.java | 2 +- 3 files changed, 23 insertions(+), 35 deletions(-) diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java index 3dee814e1f1..18fd7717d6d 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicEndpoint.java @@ -1788,9 +1788,10 @@ public abstract sealed class QuicEndpoint implements AutoCloseable if (more > 16) { // the server doesn't seem to take into account our // connection close frame. Just stop responding - updatedDeadline = Deadline.MIN; + updated = updatedDeadline = Deadline.MIN; } else { - updatedDeadline = updated.plusMillis(maxIdleTimeMs); + updated = updatedDeadline = timeSource().instant() + .plusMillis(maxIdleTimeMs); } handleIncoming(source, destConnId, headersType, buffer); } else { @@ -1798,7 +1799,7 @@ public abstract sealed class QuicEndpoint implements AutoCloseable dropIncoming(source, destConnId, headersType, buffer); } - timer().reschedule(this, updatedDeadline); + timer().reschedule(this, updated); } protected void handleIncoming(SocketAddress source, ByteBuffer idbytes, @@ -1821,8 +1822,8 @@ public abstract sealed class QuicEndpoint implements AutoCloseable } public final void startTimer() { - deadline = updatedDeadline = timeSource().instant().plusMillis(maxIdleTimeMs); - timer().offer(this); + Deadline deadline = updatedDeadline = timeSource().instant().plusMillis(maxIdleTimeMs); + timer().reschedule(this, deadline); } @Override diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicTimerQueue.java b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicTimerQueue.java index 830415593cb..bbb88cf1c45 100644 --- a/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicTimerQueue.java +++ b/src/java.net.http/share/classes/jdk/internal/net/http/quic/QuicTimerQueue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -74,6 +74,9 @@ public final class QuicTimerQueue { private volatile Deadline scheduledDeadline = Deadline.MAX; private volatile Deadline returnedDeadline = Deadline.MAX; + // Not volatile: never accessed without holding monitor + private Deadline notifiedDeadline = Deadline.MAX; + /** * Creates a new timer queue with the given notifier. * A notifier is used to notify the timer thread that @@ -113,33 +116,8 @@ public final class QuicTimerQueue { * @param event an event to be scheduled */ public void offer(QuicTimedEvent event) { - if (event instanceof Marker marker) - throw new IllegalArgumentException(marker.name()); - assert QuicTimedEvent.COMPARATOR.compare(event, FLOOR) > 0; - assert QuicTimedEvent.COMPARATOR.compare(event, CEILING) < 0; - Deadline deadline = event.deadline(); - scheduled.add(event); - scheduled(deadline); if (debug.on()) debug.log("QuicTimerQueue: event %s offered", event); - if (notify(deadline)) { - if (debug.on()) debug.log("QuicTimerQueue: event %s will be rescheduled", event); - if (Log.quicTimer()) { - var now = debugNow(); - Log.logQuic(String.format("%s: QuicTimerQueue: event %s will be scheduled" + - " at %s (returned deadline: %s, nextDeadline: %s)", - Thread.currentThread().getName(), event, d(now, deadline), - d(now, returnedDeadline), d(now, nextDeadline()))); - } - notifier.run(); - } else { - if (Log.quicTimer()) { - var now = debugNow(); - Log.logQuic(String.format("%s: QuicTimerQueue: event %s will not be scheduled" + - " at %s (returned deadline: %s, nextDeadline: %s)", - Thread.currentThread().getName(), event, d(now, deadline), - d(now, returnedDeadline), d(now, nextDeadline()))); - } - } + reschedule(event, event.deadline()); } /** @@ -181,7 +159,7 @@ public final class QuicTimerQueue { int drained = 0; int dues; synchronized (this) { - scheduledDeadline = Deadline.MAX; + scheduledDeadline = returnedDeadline = notifiedDeadline = Deadline.MAX; } // moved scheduled / rescheduled tasks to due, until // nothing else is due. Then process dues. @@ -347,7 +325,16 @@ public final class QuicTimerQueue { synchronized (this) { if (deadline.isBefore(nextDeadline()) || deadline.isBefore(returnedDeadline)) { - return true; + // notifiedDeadline will be reset to MAX first thing in + // processEventAndReturnNextDeadline; We do not want + // to call the notifier (wake the selector) again if it's + // been already called for a notifiedDeadline <= to deadline; + // On the other hand, if deadline < notifiedDeadline, we + // need to call the notifier to force an additional wakeup + if (deadline.isBefore(notifiedDeadline)) { + notifiedDeadline = deadline; + return true; + } } } return false; diff --git a/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java b/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java index c38671e65b8..ff8e3804996 100644 --- a/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java +++ b/test/jdk/java/net/httpclient/http3/H3MultipleConnectionsToSameHost.java @@ -77,7 +77,7 @@ */ /* * @test id=useNioSelector - * @bug 8087112 8372409 + * @bug 8087112 8372409 8386989 * @library /test/lib /test/jdk/java/net/httpclient/lib * @build jdk.test.lib.net.SimpleSSLContext * jdk.httpclient.test.lib.http2.Http2TestServer