Skip to content

Commit 10d304d

Browse files
committed
make HTTPClient Sendable
1 parent 2edac1d commit 10d304d

File tree

1 file changed

+35
-35
lines changed

1 file changed

+35
-35
lines changed

Sources/AsyncHTTPClient/HTTPClient.swift

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,9 @@ public class HTTPClient {
6666
let poolManager: HTTPConnectionPool.Manager
6767

6868
/// Shared thread pool used for file IO. It is lazily created on first access of ``Task/fileIOThreadPool``.
69-
private var fileIOThreadPool: NIOThreadPool?
70-
private let fileIOThreadPoolLock = NIOLock()
69+
private let fileIOThreadPool: NIOLockedValueBox<NIOThreadPool?>
7170

72-
private var state: State
73-
private let stateLock = NIOLock()
71+
private let state: NIOLockedValueBox<State>
7472
private let canBeShutDown: Bool
7573

7674
static let loggingDisabled = Logger(label: "AHC-do-not-log", factory: { _ in SwiftLogNoOpLogHandler() })
@@ -167,29 +165,32 @@ public class HTTPClient {
167165
configuration: self.configuration,
168166
backgroundActivityLogger: backgroundActivityLogger
169167
)
170-
self.state = .upAndRunning
168+
self.state = NIOLockedValueBox(.upAndRunning)
169+
self.fileIOThreadPool = NIOLockedValueBox(nil)
171170
}
172171

173172
deinit {
174173
debugOnly {
175174
// We want to crash only in debug mode.
176-
switch self.state {
177-
case .shutDown:
178-
break
179-
case .shuttingDown:
180-
preconditionFailure(
181-
"""
182-
This state should be totally unreachable. While the HTTPClient is shutting down a \
183-
reference cycle should exist, that prevents it from deinit.
184-
"""
185-
)
186-
case .upAndRunning:
187-
preconditionFailure(
188-
"""
189-
Client not shut down before the deinit. Please call client.shutdown() when no \
190-
longer needed. Otherwise memory will leak.
191-
"""
192-
)
175+
self.state.withLockedValue { state in
176+
switch state {
177+
case .shutDown:
178+
break
179+
case .shuttingDown:
180+
preconditionFailure(
181+
"""
182+
This state should be totally unreachable. While the HTTPClient is shutting down a \
183+
reference cycle should exist, that prevents it from deinit.
184+
"""
185+
)
186+
case .upAndRunning:
187+
preconditionFailure(
188+
"""
189+
Client not shut down before the deinit. Please call client.shutdown() when no \
190+
longer needed. Otherwise memory will leak.
191+
"""
192+
)
193+
}
193194
}
194195
}
195196
}
@@ -302,11 +303,11 @@ public class HTTPClient {
302303
return
303304
}
304305
do {
305-
try self.stateLock.withLock {
306-
guard case .upAndRunning = self.state else {
306+
try self.state.withLockedValue { state in
307+
guard case .upAndRunning = state else {
307308
throw HTTPClientError.alreadyShutdown
308309
}
309-
self.state = .shuttingDown(requiresCleanClose: requiresCleanClose, callback: callback)
310+
state = .shuttingDown(requiresCleanClose: requiresCleanClose, callback: callback)
310311
}
311312
} catch {
312313
callback(error)
@@ -320,17 +321,16 @@ public class HTTPClient {
320321
case .failure:
321322
preconditionFailure("Shutting down the connection pool must not fail, ever.")
322323
case .success(let unclean):
323-
let (callback, uncleanError) = self.stateLock.withLock { () -> (ShutdownCallback, Error?) in
324-
guard case .shuttingDown(let requiresClean, callback: let callback) = self.state else {
324+
let (callback, uncleanError) = self.state.withLockedValue {
325+
(state: inout HTTPClient.State) -> (ShutdownCallback, Error?) in
326+
guard case .shuttingDown(let requiresClean, callback: let callback) = state else {
325327
preconditionFailure("Why did the pool manager shut down, if it was not instructed to")
326328
}
327329

328330
let error: Error? = (requiresClean && unclean) ? HTTPClientError.uncleanShutdown : nil
331+
state = .shutDown
329332
return (callback, error)
330333
}
331-
self.stateLock.withLock {
332-
self.state = .shutDown
333-
}
334334
queue.async {
335335
callback(uncleanError)
336336
}
@@ -340,11 +340,11 @@ public class HTTPClient {
340340

341341
@Sendable
342342
private func makeOrGetFileIOThreadPool() -> NIOThreadPool {
343-
self.fileIOThreadPoolLock.withLock {
344-
guard let fileIOThreadPool = self.fileIOThreadPool else {
343+
self.fileIOThreadPool.withLockedValue { pool in
344+
guard let pool else {
345345
return NIOThreadPool.singleton
346346
}
347-
return fileIOThreadPool
347+
return pool
348348
}
349349
}
350350

@@ -734,8 +734,8 @@ public class HTTPClient {
734734
]
735735
)
736736

737-
let failedTask: Task<Delegate.Response>? = self.stateLock.withLock {
738-
switch self.state {
737+
let failedTask: Task<Delegate.Response>? = self.state.withLockedValue { state in
738+
switch state {
739739
case .upAndRunning:
740740
return nil
741741
case .shuttingDown, .shutDown:

0 commit comments

Comments
 (0)