blob: 35cae88f2cd5bb444a50a977468829241337850f [file]
// Copyright (C) 2026 Apple Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
// FIXME: (rdar://164119356) Move this file into WTF.
#if compiler(>=6.4) && !SWIFT_WEBKIT_TOOLCHAIN
import wtf.Core.CompletionHandler
/// The machinery shared by ``CxxCompletionHandler``, ``CxxConsumingCompletionHandler``, and
/// ``CxxVoidCompletionHandler``.
///
/// - Important: Do not conform to this directly. Conform to one of those three instead.
public protocol CxxCompletionHandlerBase: ~Copyable {
/// The type of the completion handler's only parameter, or `Void` if it has none.
///
/// State this explicitly, matching the C++ parameter type:
///
/// ```swift
/// typealias Argument = CInt
/// ```
associatedtype Argument: ~Copyable
/// Creates a completion handler from the C ABI a Swift closure reduces to: a function pointer, an opaque context, and
/// a destructor for that context.
///
/// Every specialization of `WTF::CompletionHandler` already has this constructor, so conformances never implement it
/// themselves.
///
/// - Parameters:
/// - invoke: The function pointer.
/// - destroy: The function describing how to destroy the closure.
/// - context: An opaque context for the closure.
/// - Important: Do not implement or call this yourself.
init(invoke: WTF.SwiftClosureInvoke, destroy: WTF.SwiftClosureDestroy, context: UnsafeMutableRawPointer)
}
/// A protocol for concrete specializations of `WTF::CompletionHandler` that take one argument by value to conform to.
///
/// Conforming a specialization to this protocol allows Swift to easily call functions that accept `WTF::CompletionHandler` parameters.
/// For example, this makes it possible to call a function like:
///
/// ```cpp
/// using DoSomethingCompletionHandler = WTF::CompletionHandler<void(int)>;
/// void doSomething(DoSomethingCompletionHandler&&);
/// ```
///
/// by creating this conformance:
///
/// ```swift
/// extension DoSomethingCompletionHandler: @unsafe CxxCompletionHandler {
/// typealias Argument = CInt
/// }
/// ```
///
/// Swift can then call this via the `init(_ body: @escaping (Argument) -> Void)` initializer:
///
/// ```swift
/// doSomething(consuming: .init { number in ... })
/// ```
///
/// or by using the convenience continuation initializer:
///
/// ```swift
/// await withCheckedContinuation { continuation in
/// doSomething(consuming: .init(continuation))
/// }
/// ```
///
/// Common specializations like `WTF::VoidCompletionHandler` and `WTF::BoolCompletionHandler` already conform.
///
/// - Note: If the C++ parameter is an rvalue reference (`T&&`) rather than a value, conform to
/// ``CxxConsumingCompletionHandler`` instead.
/// - Important: Do not implement the protocol requirements yourself. It is unsafe if you do so.
public protocol CxxCompletionHandler: CxxCompletionHandlerBase, ~Copyable where Argument: Copyable {
/// Invokes the completion handler.
///
/// This is implemented by the imported C++ `operator()`, which is what makes the compiler verify that `Argument` matches
/// the type C++ actually passes.
///
/// - Important: Do not implement this yourself.
mutating func callAsFunction(_ argument: Argument)
}
/// A protocol for concrete specializations of `WTF::CompletionHandler` that take one argument by rvalue reference to
/// conform to.
///
/// This is the same as ``CxxCompletionHandler`` except that it matches a C++ signature like:
///
/// ```cpp
/// using DoSomethingCompletionHandler = WTF::CompletionHandler<void(MoveOnlyThing&&)>;
/// ```
///
/// - Important: Do not implement the protocol requirements yourself. It is unsafe if you do so.
public protocol CxxConsumingCompletionHandler: CxxCompletionHandlerBase, ~Copyable {
/// Invokes the completion handler.
///
/// This is implemented by the imported C++ `operator()`, which is what makes the compiler verify that `Argument` matches
/// the type C++ actually passes.
///
/// - Important: Do not implement this yourself.
mutating func callAsFunction(consuming: consuming Argument)
}
/// A protocol for concrete specializations of `WTF::CompletionHandler` that take no argument to conform to.
public protocol CxxVoidCompletionHandler: CxxCompletionHandlerBase, ~Copyable where Argument == Void {
/// Invokes the completion handler.
///
/// - Important: Do not implement this yourself.
mutating func callAsFunction()
}
extension CxxCompletionHandlerBase where Self: ~Copyable {
/// Creates a completion handler that owns `box` and dispatches to it.
///
/// - Parameter box: The box holding the Swift closure to call.
fileprivate init(box: SwiftClosureBoxBase) {
// This is all safe because all uses of the raw pointer are encapsulated in the C++ implementation and its lifetime
// is not modified elsewhere.
unsafe self.init(
invoke: { context, argument in
// This uses `SwiftClosureBoxBase` and not `SwiftClosureBox<Argument>` because a C function pointer
// cannot be formed from a closure that captures generic parameters.
unsafe Unmanaged<SwiftClosureBoxBase>.fromOpaque(context).takeUnretainedValue().call(with: argument)
},
destroy: { context in
unsafe Unmanaged<AnyObject>.fromOpaque(context).release()
},
context: unsafe Unmanaged.passRetained(box).toOpaque()
)
}
}
extension CxxCompletionHandler where Self: ~Copyable {
// These functions are safe because its implementation is safe.
/// Creates a `WTF::CompletionHandler` type from a Swift closure.
///
/// - Parameters:
/// - isolation: The current isolation.
/// - body: The Swift closure to use.
@safe
public init(isolation: isolated (any Actor)? = #isolation, _ body: @escaping (Argument) -> Void) {
self.init(box: SwiftCopyingClosureBox(isolation: isolation, body))
}
}
extension CxxConsumingCompletionHandler where Self: ~Copyable {
// These functions are safe because its implementation is safe.
/// Creates a `WTF::CompletionHandler` type from a Swift closure.
///
/// - Parameters:
/// - isolation: The current isolation.
/// - body: The Swift closure to use.
@safe
public init(isolation: isolated (any Actor)? = #isolation, _ body: @escaping (consuming Argument) -> Void) {
self.init(box: SwiftTakingClosureBox(isolation: isolation, body))
}
}
extension CxxCompletionHandler where Self: ~Copyable, Argument: Sendable {
/// A convenience initializer to create a completion handler from a `CheckedContinuation` directly to make bridging to a Swift `async`
/// context trivial.
///
/// This should be preferred for most cases.
///
/// - Parameter continuation: A continuation vended by `withCheckedContinuation`.
@safe
public init(_ continuation: CheckedContinuation<Argument, some Error>) {
self.init { continuation.resume(returning: $0) }
}
}
extension CxxVoidCompletionHandler where Self: ~Copyable {
// These functions are safe because its implementation is safe.
/// Creates a `WTF::CompletionHandler` type from a Swift closure.
///
/// - Parameters:
/// - isolation: The current isolation.
/// - body: The Swift closure to use.
@safe
public init(isolation: isolated (any Actor)? = #isolation, _ body: @escaping () -> Void) {
self.init(box: SwiftVoidClosureBox(isolation: isolation, body))
}
/// A convenience initializer to create a completion handler from a `CheckedContinuation` directly to make bridging to a Swift `async`
/// context trivial.
///
/// This should be preferred for most cases.
///
/// - Parameter continuation: A continuation vended by `withCheckedContinuation`.
@safe
public init(_ continuation: CheckedContinuation<Void, some Error>) {
self.init { continuation.resume() }
}
}
private class SwiftClosureBoxBase {
private let isolation: (any Actor)?
init(isolation: (any Actor)?) {
self.isolation = isolation
}
func call(with argument: UnsafeMutableRawPointer?) {
preconditionFailure("Subclasses must override call(with:)")
}
final func checkIsolation() {
isolation?.preconditionIsolated("A completion handler must be invoked on the isolation that created it")
}
}
private final class SwiftVoidClosureBox: SwiftClosureBoxBase {
private let body: () -> Void
init(isolation: (any Actor)?, _ body: @escaping () -> Void) {
self.body = body
super.init(isolation: isolation)
}
override func call(with pointer: UnsafeMutableRawPointer?) {
unsafe precondition(pointer == nil)
checkIsolation()
body()
}
}
/// Copies the argument out of the pointer C++ supplied, leaving C++ to destroy its own object.
private final class SwiftCopyingClosureBox<Argument>: SwiftClosureBoxBase {
private let body: (Argument) -> Void
init(isolation: (any Actor)?, _ body: @escaping (Argument) -> Void) {
self.body = body
super.init(isolation: isolation)
}
override func call(with pointer: UnsafeMutableRawPointer?) {
guard let pointer = unsafe pointer else {
preconditionFailure("A completion handler taking an argument must be passed a pointer to one")
}
checkIsolation()
// Safety properties:
//
// Non-null, initialized, aligned: guarded above, and C++ passes `std::addressof` of the live
// parameter object it was invoked with, so it is the address of a fully constructed object.
// Really an `Argument`: the `callAsFunction` requirement is witnessed by the imported C++
// `operator()`, so a conformance whose `Argument` disagrees with the C++ parameter type does
// not compile. The typealias is checked, not merely promised.
// Lifetime: `invoke` is synchronous, so C++ is blocked inside the call for as long as this runs
// and cannot have released the object. This method never stores `pointer`; only the copied
// value may outlive the call.
// Aliasing: C++ still owns that same object, so this may only read it. `.pointee` copies, and
// C++ is blocked and so cannot be writing concurrently. Taking the value instead would
// leave C++'s object deinitialized behind its back.
// Isolation: `checkIsolation()` above.
body(unsafe pointer.assumingMemoryBound(to: Argument.self).pointee)
}
}
/// Takes the argument out of the pointer C++ supplied.
private final class SwiftTakingClosureBox<Argument: ~Copyable>: SwiftClosureBoxBase {
private let body: (consuming Argument) -> Void
init(isolation: (any Actor)?, _ body: @escaping (consuming Argument) -> Void) {
self.body = body
super.init(isolation: isolation)
}
override func call(with pointer: UnsafeMutableRawPointer?) {
guard let pointer = unsafe pointer else {
preconditionFailure("A completion handler taking an argument must be passed a pointer to one")
}
checkIsolation()
// Safety properties:
//
// As in `SwiftCopyingClosureBox`, with three differences that all follow from C++ having built
// this object solely to hand over:
//
// Aliasing: C++ placement-new'd the value into an `AlignedStorage` local and does not touch it
// again -- `AlignedStorage`'s destructor does not run `~ArgumentType` -- so taking it here
// races with nothing. Alignment comes from `alignas(alignment_of_v<T>)` on that storage.
// Consumed exactly once: `.move()` deinitializes the storage, and nothing else destroys it. C++
// will not, and `CompletionHandler::operator()` clears its function before calling, so the
// handler cannot run twice.
// Relocation: Swift moves a noncopyable imported C++ value byte-wise rather than running its
// move constructor. `WTF::CompletionHandler` static-asserts that such an argument has a
// non-trivial destructor, which is what makes Swift use the move constructor instead.
body(unsafe pointer.assumingMemoryBound(to: Argument.self).move())
}
}
extension WTF.VoidCompletionHandler: @unsafe CxxVoidCompletionHandler {}
extension WTF.BoolCompletionHandler: @unsafe CxxCompletionHandler {
// swift-format-ignore: AllPublicDeclarationsHaveDocumentation
public typealias Argument = Bool
}
#endif // compiler(>=6.4) && !SWIFT_WEBKIT_TOOLCHAIN