From 935a2658ad11d09603b18f1232bcc8f0533acc59 Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 22 Oct 2025 10:57:41 +0100 Subject: [PATCH] fix: orphan Timer instances on release - Updated released logic, don't use expensive synchronized block, use `AtomicBoolean` instead - Do not create `Timer` instance if channel is released --- .../io/ably/lib/realtime/ChannelBase.java | 17 ++++++------- .../test/realtime/RealtimeChannelTest.java | 24 +++++++++++++++++++ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/lib/src/main/java/io/ably/lib/realtime/ChannelBase.java b/lib/src/main/java/io/ably/lib/realtime/ChannelBase.java index 283e9879d..d8332dedf 100644 --- a/lib/src/main/java/io/ably/lib/realtime/ChannelBase.java +++ b/lib/src/main/java/io/ably/lib/realtime/ChannelBase.java @@ -8,6 +8,7 @@ import java.util.Set; import java.util.Timer; import java.util.TimerTask; +import java.util.concurrent.atomic.AtomicBoolean; import io.ably.lib.http.BasePaginatedQuery; import io.ably.lib.http.Http; @@ -95,7 +96,7 @@ public abstract class ChannelBase extends EventEmitter threads = Thread.getAllStackTraces().keySet(); + List timers = threads.stream() + .filter(t -> t.getName().startsWith("Timer-")) + .toList(); + + long timeout = 1_000; + long start = System.currentTimeMillis(); + + while(timers.stream().anyMatch(Thread::isAlive) && System.currentTimeMillis() - start < timeout) { + Thread.sleep(100); + } + + assertFalse("Found orphan Timer threads", timers.stream().anyMatch(Thread::isAlive)); + } + /** * This test ensures that when the connection is manually triggered, the channel can successfully * transition to the attached state without interference or rewriting of its immediate attach action.