diff --git a/Sources/NIOCore/EventLoop+SerialExecutor.swift b/Sources/NIOCore/EventLoop+SerialExecutor.swift index b3d0434133..2fc92f1f1a 100644 --- a/Sources/NIOCore/EventLoop+SerialExecutor.swift +++ b/Sources/NIOCore/EventLoop+SerialExecutor.swift @@ -36,13 +36,25 @@ extension NIOSerialEventLoopExecutor { @inlinable public func asUnownedSerialExecutor() -> UnownedSerialExecutor { - UnownedSerialExecutor(ordinary: self) + UnownedSerialExecutor(complexEquality: self) } @inlinable public var executor: any SerialExecutor { self } + + @available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) + @inlinable + public func isSameExclusiveExecutionContext(other: Self) -> Bool { + other === self + } + + @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, *) + @inlinable + public func checkIsolated() { + self.preconditionInEventLoop() + } } /// A type that wraps a NIO ``EventLoop`` into a `SerialExecutor` diff --git a/Tests/NIOPosixTests/SerialExecutorTests.swift b/Tests/NIOPosixTests/SerialExecutorTests.swift index 5e02989606..8df2ed6be0 100644 --- a/Tests/NIOPosixTests/SerialExecutorTests.swift +++ b/Tests/NIOPosixTests/SerialExecutorTests.swift @@ -21,6 +21,8 @@ import XCTest actor EventLoopBoundActor { nonisolated let unownedExecutor: UnownedSerialExecutor + var counter: Int = 0 + init(loop: EventLoop) { self.unownedExecutor = loop.executor.asUnownedSerialExecutor() } @@ -34,6 +36,14 @@ actor EventLoopBoundActor { loop.assertNotInEventLoop() XCTAssertFalse(loop.inEventLoop) } + + #if compiler(>=6.0) + nonisolated func assumeInLoop() -> Int { + self.assumeIsolated { actor in + actor.counter + } + } + #endif } #endif @@ -69,4 +79,20 @@ final class SerialExecutorTests: XCTestCase { try await self._testBasicExecutorFitsOnEventLoop(loop1: loop1, loop2: loop2) } + + func testAssumeIsolation() async throws { + #if compiler(<6.0) + throw XCTSkip("Custom executors are only supported in 5.9") + #else + + let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) + let el = group.next() + + let testActor = EventLoopBoundActor(loop: el) + let result = try await el.submit { + testActor.assumeInLoop() + }.get() + XCTAssertEqual(result, 0) + #endif + } }