mirror of
https://github.com/swift-server/async-http-client.git
synced 2026-06-02 07:37:34 +00:00
At the moment, `HTTPClient`'s entire API surface violates Structured Concurrency. Both the creation & shutdown of a HTTP client as well as making requests (#807) doesn't follow Structured Concurrency. Some of the problems are: 1. Upon return of methods, resources are still in active use in other threads/tasks 2. Cancellation doesn't always work This PR is baby steps towards a Structured Concurrency API, starting with start/shutdown of the HTTP client. Co-authored-by: Johannes Weiss <johannes@jweiss.io>
81 lines
2.8 KiB
Swift
81 lines
2.8 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the AsyncHTTPClient open source project
|
|
//
|
|
// Copyright (c) 2025 Apple Inc. and the AsyncHTTPClient project authors
|
|
// Licensed under Apache License v2.0
|
|
//
|
|
// See LICENSE.txt for license information
|
|
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#if compiler(>=6.0)
|
|
@inlinable
|
|
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
|
|
internal func asyncDo<R>(
|
|
isolation: isolated (any Actor)? = #isolation,
|
|
_ body: () async throws -> sending R,
|
|
finally: sending @escaping ((any Error)?) async throws -> Void
|
|
) async throws -> sending R {
|
|
let result: R
|
|
do {
|
|
result = try await body()
|
|
} catch {
|
|
// `body` failed, we need to invoke `finally` with the `error`.
|
|
|
|
// This _looks_ unstructured but isn't really because we unconditionally always await the return.
|
|
// We need to have an uncancelled task here to assure this is actually running in case we hit a
|
|
// cancellation error.
|
|
try await Task {
|
|
try await finally(error)
|
|
}.value
|
|
throw error
|
|
}
|
|
|
|
// `body` succeeded, we need to invoke `finally` with `nil` (no error).
|
|
|
|
// This _looks_ unstructured but isn't really because we unconditionally always await the return.
|
|
// We need to have an uncancelled task here to assure this is actually running in case we hit a
|
|
// cancellation error.
|
|
try await Task {
|
|
try await finally(nil)
|
|
}.value
|
|
return result
|
|
}
|
|
#else
|
|
@inlinable
|
|
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
|
|
internal func asyncDo<R: Sendable>(
|
|
_ body: () async throws -> R,
|
|
finally: @escaping @Sendable ((any Error)?) async throws -> Void
|
|
) async throws -> R {
|
|
let result: R
|
|
do {
|
|
result = try await body()
|
|
} catch {
|
|
// `body` failed, we need to invoke `finally` with the `error`.
|
|
|
|
// This _looks_ unstructured but isn't really because we unconditionally always await the return.
|
|
// We need to have an uncancelled task here to assure this is actually running in case we hit a
|
|
// cancellation error.
|
|
try await Task {
|
|
try await finally(error)
|
|
}.value
|
|
throw error
|
|
}
|
|
|
|
// `body` succeeded, we need to invoke `finally` with `nil` (no error).
|
|
|
|
// This _looks_ unstructured but isn't really because we unconditionally always await the return.
|
|
// We need to have an uncancelled task here to assure this is actually running in case we hit a
|
|
// cancellation error.
|
|
try await Task {
|
|
try await finally(nil)
|
|
}.value
|
|
return result
|
|
}
|
|
#endif
|