diff --git a/Sources/AsyncHTTPClient/HTTPClient.swift b/Sources/AsyncHTTPClient/HTTPClient.swift index a1f699546..088329513 100644 --- a/Sources/AsyncHTTPClient/HTTPClient.swift +++ b/Sources/AsyncHTTPClient/HTTPClient.swift @@ -68,7 +68,7 @@ public class HTTPClient { let eventLoopGroupProvider: EventLoopGroupProvider let configuration: Configuration let poolManager: HTTPConnectionPool.Manager - var state: State + private var state: State private let stateLock = Lock() internal static let loggingDisabled = Logger(label: "AHC-do-not-log", factory: { _ in SwiftLogNoOpLogHandler() }) @@ -118,8 +118,22 @@ public class HTTPClient { } deinit { - guard case .shutDown = self.state else { - preconditionFailure("Client not shut down before the deinit. Please call client.syncShutdown() when no longer needed.") + debugOnly { + // We want to crash only in debug mode. + switch self.state { + case .shutDown: + break + case .shuttingDown: + preconditionFailure(""" + This state should be totally unreachable. While the HTTPClient is shutting down a \ + reference cycle should exist, that prevents it from deinit. + """) + case .upAndRunning: + preconditionFailure(""" + Client not shut down before the deinit. Please call client.syncShutdown() when no \ + longer needed. Otherwise memory will leak. + """) + } } } diff --git a/Sources/AsyncHTTPClient/Utils.swift b/Sources/AsyncHTTPClient/Utils.swift index 240d39a01..79eeb86e5 100644 --- a/Sources/AsyncHTTPClient/Utils.swift +++ b/Sources/AsyncHTTPClient/Utils.swift @@ -32,3 +32,13 @@ public final class HTTPClientCopyingDelegate: HTTPClientResponseDelegate { return () } } + +/// A utility function that runs the body code only in debug builds, without +/// emitting compiler warnings. +/// +/// This is currently the only way to do this in Swift: see +/// https://forums.swift.org/t/support-debug-only-code/11037 for a discussion. +@inlinable +internal func debugOnly(_ body: () -> Void) { + assert({ body(); return true }()) +}