fix: clear pending pings to avoid promise leaks on connection close/suspend
Some checks failed
ci / macos (push) Has been cancelled
ci / ios (push) Has been cancelled
ci / check-linter (push) Has been cancelled

- Add cancel() method to RttCommand to fail promise on connection close
- Add dequeueAll() method to ConcurrentQueue for batch cleanup
- Call clearPendingPings() in close(), suspend(), and disconnect() methods
- Prevents 'leaking promise' crash when connection is closed while pings are pending
This commit is contained in:
wenzuhuai
2026-01-12 19:16:56 +08:00
parent d7bdb4f378
commit 153e600bbc
3 changed files with 31 additions and 0 deletions

View File

@@ -712,6 +712,7 @@ class ConnectionHandler: ChannelInboundHandler {
guard let eventLoop = self.channel?.eventLoop else {
self.state.withLockedValue { $0 = .closed }
self.pingTask?.cancel()
clearPendingPings() // Clear pending pings to avoid promise leaks
self.fire(.closed)
return
}
@@ -720,6 +721,7 @@ class ConnectionHandler: ChannelInboundHandler {
eventLoop.execute {
self.state.withLockedValue { $0 = .closed }
self.pingTask?.cancel()
self.clearPendingPings() // Clear pending pings to avoid promise leaks
self.channel?.close(mode: .all, promise: promise)
}
@@ -735,8 +737,20 @@ class ConnectionHandler: ChannelInboundHandler {
private func disconnect() async throws {
self.pingTask?.cancel()
clearPendingPings() // Clear pending pings to avoid promise leaks
try await self.channel?.close().get()
}
/// Clear all pending ping requests to avoid promise leaks
private func clearPendingPings() {
let pendingPings = pingQueue.dequeueAll()
for ping in pendingPings {
ping.cancel()
}
if !pendingPings.isEmpty {
logger.debug("Cleared \(pendingPings.count) pending ping(s)")
}
}
func suspend() async throws {
self.reconnectTask?.cancel()
@@ -746,6 +760,7 @@ class ConnectionHandler: ChannelInboundHandler {
guard let eventLoop = self.channel?.eventLoop else {
// Set state to suspended even if channel is nil
self.state.withLockedValue { $0 = .suspended }
clearPendingPings() // Clear pending pings to avoid promise leaks
return
}
let promise = eventLoop.makePromise(of: Void.self)
@@ -759,8 +774,10 @@ class ConnectionHandler: ChannelInboundHandler {
if shouldClose {
self.pingTask?.cancel()
self.clearPendingPings() // Clear pending pings to avoid promise leaks
self.channel?.close(mode: .all, promise: promise)
} else {
self.clearPendingPings() // Clear pending pings even if not closing
promise.succeed()
}
}