Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions Sources/NIOCore/EventLoopFuture+AssumeIsolated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,62 @@ public struct NIOIsolatedEventLoop {
return .init(promise: promise, cancellationTask: { scheduled.cancel() })
}

/// Creates and returns a new `EventLoopFuture` that is already marked as success. Notifications
/// will be done using this `EventLoop` as execution `NIOThread`.
///
/// - Parameters:
/// - value: the value that is used by the `EventLoopFuture`.
/// - Returns: a succeeded `EventLoopFuture`.
@inlinable
@available(*, noasync)
public func makeSucceededFuture<Success>(_ value: Success) -> EventLoopFuture<Success> {
let promise = self._wrapped.makePromise(of: Success.self)
promise.assumeIsolatedUnsafeUnchecked().succeed(value)
return promise.futureResult
}

/// Creates and returns a new `EventLoopFuture` that is already marked as failed. Notifications
/// will be done using this `EventLoop`.
///
/// - Parameters:
/// - error: the `Error` that is used by the `EventLoopFuture`.
/// - Returns: a failed `EventLoopFuture`.
@inlinable
@available(*, noasync)
public func makeFailedFuture<Success>(_ error: Error) -> EventLoopFuture<Success> {
let promise = self._wrapped.makePromise(of: Success.self)
promise.fail(error)
return promise.futureResult
}

/// Creates and returns a new `EventLoopFuture` that is marked as succeeded or failed with the
/// value held by `result`.
///
/// - Parameters:
/// - result: The value that is used by the `EventLoopFuture`
/// - Returns: A completed `EventLoopFuture`.
@inlinable
@available(*, noasync)
public func makeCompletedFuture<Success>(_ result: Result<Success, Error>) -> EventLoopFuture<Success> {
let promise = self._wrapped.makePromise(of: Success.self)
promise.assumeIsolatedUnsafeUnchecked().completeWith(result)
return promise.futureResult
}

/// Creates and returns a new `EventLoopFuture` that is marked as succeeded or failed with the
/// value returned by `body`.
///
/// - Parameters:
/// - body: The function that is used to complete the `EventLoopFuture`
/// - Returns: A completed `EventLoopFuture`.
@inlinable
@available(*, noasync)
public func makeCompletedFuture<Success>(
withResultOf body: () throws -> Success
) -> EventLoopFuture<Success> {
self.makeCompletedFuture(Result(catching: body))
}

/// Returns the wrapped event loop.
@inlinable
public func nonisolated() -> any EventLoop {
Expand Down
61 changes: 61 additions & 0 deletions Tests/NIOCoreTests/NIOIsolatedEventLoopTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2025 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import NIOCore
import NIOEmbedded
import XCTest

final class NIOIsolatedEventLoopTests: XCTestCase {
func withEmbeddedEventLoop(_ body: (EmbeddedEventLoop) throws -> Void) rethrows {
let loop = EmbeddedEventLoop()
defer { try! loop.syncShutdownGracefully() }
try body(loop)
}

func testMakeSucceededFuture() throws {
try self.withEmbeddedEventLoop { loop in
let future = loop.assumeIsolated().makeSucceededFuture(NotSendable())
XCTAssertNoThrow(try future.map { _ in }.wait())
}
}

func testMakeFailedFuture() throws {
try self.withEmbeddedEventLoop { loop in
let future: EventLoopFuture<NotSendable> = loop.assumeIsolated().makeFailedFuture(
ChannelError.alreadyClosed
)
XCTAssertThrowsError(try future.map { _ in }.wait())
}
}

func testMakeCompletedFuture() throws {
try self.withEmbeddedEventLoop { loop in
let result = Result<NotSendable, any Error>.success(NotSendable())
let future: EventLoopFuture<NotSendable> = loop.assumeIsolated().makeCompletedFuture(result)
XCTAssertNoThrow(try future.map { _ in }.wait())
}
}

func testMakeCompletedFutureWithResultOfClosure() throws {
try self.withEmbeddedEventLoop { loop in
let future = loop.assumeIsolated().makeCompletedFuture { NotSendable() }
XCTAssertNoThrow(try future.map { _ in }.wait())
}
}
}

private struct NotSendable {}

@available(*, unavailable)
extension NotSendable: Sendable {}
Loading