On Thu, Sep 3, 2026 at 10:44 AM Mark Thomas <[email protected]> wrote: > - Iterate through the entire list of endpoints in the background thread.
The async timeouts (which have no IO) do that, the background thread calls timeoutAsync on all processors to see if something should happen. So it couldn't be more basic, but nobody complained. > The first option, as you point out, gets complicated, fast. The second > option would ensure that entries are timed out in the correct order. The > third option is the simplest. > > I am trying to determine how important it is that endpoints are timed > out in the correct order. In the static resource cache, that has similar > code, it was more obvious that correct order had a benefit. The benefit > is not as clear for WebSocket writes but I think it is worth doing as it > simplifies reasoning about timeout behaviour. +1 > > It would use "public void register(WsRemoteEndpointImplServer > > endpoint, long timeoutExpiry)" instead, and I have a patch ready. > > Hang on to that patch. I may still end up tying myself in knots. It's the "timeout.patch" file I attached to a previous reply in the thread, so it won't get lost since it's in the archive. There's a test case too for the structure (written from before the inclusion of the change to avoid re registering without removing when useAsyncIO=false, so it focused on that). Rémy > Mark > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: [email protected] > For additional commands, e-mail: [email protected] >
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.tomcat.websocket.server; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.apache.tomcat.unittest.TesterServletContext; public class TestWsWriteTimeout { private WsServerContainer serverContainer; private WsWriteTimeout wsWriteTimeout; @Before public void init() { serverContainer = new WsServerContainer(new TesterServletContext()); wsWriteTimeout = serverContainer.getTimeout(); } @Test public void testTimeout() { TestEndpoint endpoint = new TestEndpoint(serverContainer); long now = System.currentTimeMillis(); wsWriteTimeout.register(endpoint, now - 1000); wsWriteTimeout.backgroundProcess(); Assert.assertEquals(1, endpoint.getTimeoutCount()); // The write is no longer registered so it must not be timed out again wsWriteTimeout.backgroundProcess(); Assert.assertEquals(1, endpoint.getTimeoutCount()); wsWriteTimeout.unregister(endpoint); } @Test public void testNoTimeoutWhenExpiryInFuture() { TestEndpoint endpoint = new TestEndpoint(serverContainer); long now = System.currentTimeMillis(); wsWriteTimeout.register(endpoint, now + 60_000); wsWriteTimeout.backgroundProcess(); Assert.assertEquals(0, endpoint.getTimeoutCount()); wsWriteTimeout.unregister(endpoint); } @Test public void testReRegistrationUpdatesExpiry() { TestEndpoint endpoint = new TestEndpoint(serverContainer); long now = System.currentTimeMillis(); // Register with an expiry in the past then re-register (as done when a // write spans multiple write-possible events) with an expiry in the // future wsWriteTimeout.register(endpoint, now - 1000); wsWriteTimeout.register(endpoint, now + 60_000); wsWriteTimeout.backgroundProcess(); Assert.assertEquals(0, endpoint.getTimeoutCount()); // Re-register with an expiry in the past wsWriteTimeout.register(endpoint, now - 1000); wsWriteTimeout.backgroundProcess(); Assert.assertEquals(1, endpoint.getTimeoutCount()); wsWriteTimeout.unregister(endpoint); } @Test public void testNoTimeoutAfterUnregisterFollowingReRegistration() { TestEndpoint endpoint = new TestEndpoint(serverContainer); long now = System.currentTimeMillis(); // A write is registered, re-registered and then completes wsWriteTimeout.register(endpoint, now - 4000); wsWriteTimeout.register(endpoint, now + 60_000); wsWriteTimeout.unregister(endpoint); // A later write starts and completes within its timeout wsWriteTimeout.register(endpoint, now - 1000); wsWriteTimeout.unregister(endpoint); wsWriteTimeout.backgroundProcess(); Assert.assertEquals(0, endpoint.getTimeoutCount()); } @Test public void testTimeoutNotDelayedByReRegisteredEndpoint() { TestEndpoint a = new TestEndpoint(serverContainer); TestEndpoint b = new TestEndpoint(serverContainer); long now = System.currentTimeMillis(); // b's write is registered with an expiry in the past wsWriteTimeout.register(b, now - 2000); // a's write is registered, then re-registered with a different expiry wsWriteTimeout.register(a, now - 4000); wsWriteTimeout.register(a, now + 60_000); // a's write completes wsWriteTimeout.unregister(a); wsWriteTimeout.backgroundProcess(); Assert.assertEquals(0, a.getTimeoutCount()); // b's write has expired and must be timed out even though a was // re-registered after b Assert.assertEquals(1, b.getTimeoutCount()); wsWriteTimeout.unregister(b); } @Test public void testSameExpiry() { TestEndpoint a = new TestEndpoint(serverContainer); TestEndpoint b = new TestEndpoint(serverContainer); long now = System.currentTimeMillis(); wsWriteTimeout.register(a, now - 1000); wsWriteTimeout.register(b, now - 1000); wsWriteTimeout.backgroundProcess(); Assert.assertEquals(1, a.getTimeoutCount()); Assert.assertEquals(1, b.getTimeoutCount()); wsWriteTimeout.unregister(a); wsWriteTimeout.unregister(b); } private static class TestEndpoint extends WsRemoteEndpointImplServer { private final AtomicInteger timeoutCount = new AtomicInteger(0); TestEndpoint(WsServerContainer serverContainer) { super(null, null, serverContainer, null); } @Override protected void onTimeout(boolean useDispatch) { timeoutCount.incrementAndGet(); } int getTimeoutCount() { return timeoutCount.get(); } } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
