fix IMAPIdleHandler thread safety: pass idleTag via init, clean up event loop group on reconnect

This commit is contained in:
2026-03-14 14:20:53 +01:00
parent 55ec465677
commit 3b82e6cd95
2 changed files with 11 additions and 10 deletions
@@ -67,6 +67,12 @@ public actor IMAPIdleClient {
// MARK: - Connection
private func connectAndLogin() async throws {
// Clean up previous connection if reconnecting
try? await channel?.close()
channel = nil
try? await group?.shutdownGracefully()
group = nil
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let sslContext = try NIOSSLContext(configuration: TLSConfiguration.makeClientConfiguration())
let hostname = host
@@ -138,16 +144,14 @@ public actor IMAPIdleClient {
// Iterative IDLE loop avoids unbounded stack growth from recursion
while !Task.isCancelled {
// Swap response handler for IDLE handler
let idleTag = "IDLE1"
let (stream, streamContinuation) = AsyncStream<IMAPIdleEvent>.makeStream()
let idleHandler = IMAPIdleHandler(continuation: streamContinuation)
let idleHandler = IMAPIdleHandler(continuation: streamContinuation, idleTag: idleTag)
let oldHandler = try await getResponseHandler()
try await pipeline.removeHandler(oldHandler).get()
try await pipeline.addHandler(idleHandler).get()
let idleTag = "IDLE1"
idleHandler.setIdleTag(idleTag)
// Send IDLE command
let idleCommand = TaggedCommand(tag: idleTag, command: .idleStart)
channel.writeAndFlush(IMAPClientHandler.Message.part(.tagged(idleCommand)), promise: nil)