Skip to content

Commit fef8c0d

Browse files
sebstoSebastien StormacqCopilot
authored
Fix for swift test : _Concurrency/Executor.swift:357: Fatal error (#639)
Fix the issue described at #640 Here is the proposed fix: I added a new function `assumeIsolatedOnEventLoop` — a nonisolated method that: 1. Calls `self.eventLoop.preconditionInEventLoop()` to verify we're on the correct event loop (NIO's own thread-identity check, which always works) 2. Uses `unsafeBitCast` to strip the isolated annotation, the same pattern NIO uses internally and that I found on the Swift Forums. See: https://github.com/swiftlang/swift/blob/main/stdlib/public/Concurrency/ExecutorAssertions.swift#L348 See: https://forums.swift.org/t/actor-assumeisolated-erroneously-crashes-when-using-a-dispatch-queue-as-the-underlying-executor/72434/3 --------- Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent a8a54d9 commit fef8c0d

File tree

1 file changed

+29
-3
lines changed

1 file changed

+29
-3
lines changed

Sources/AWSLambdaRuntime/HTTPClient/LambdaRuntimeClient.swift

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,32 @@ final actor LambdaRuntimeClient: LambdaRuntimeClientProtocol {
133133
self.logger = logger
134134
}
135135

136+
/// Assume that the current context is isolated to this actor's event loop and execute the closure.
137+
///
138+
/// This is a workaround for `Actor.assumeIsolated` which can crash on open-source Swift toolchains
139+
/// built with runtime assertions enabled. In those toolchains, `assumeIsolated` performs a strict
140+
/// runtime check via `_taskIsCurrentExecutor` that fails when called from NIO callbacks
141+
/// (e.g. `whenComplete`, channel handler methods) because there is no Swift Concurrency task
142+
/// tracking the current executor in thread-local storage.
143+
///
144+
/// We use `eventLoop.preconditionInEventLoop()` as our safety check instead, then perform
145+
/// the same unsafe cast that `assumeIsolated` does internally after its check passes.
146+
/// See: https://github.com/swiftlang/swift/blob/main/stdlib/public/Concurrency/ExecutorAssertions.swift#L348
147+
/// See: https://forums.swift.org/t/actor-assumeisolated-erroneously-crashes-when-using-a-dispatch-queue-as-the-underlying-executor/72434/3
148+
private nonisolated func assumeIsolatedOnEventLoop(
149+
_ operation: (isolated LambdaRuntimeClient) -> Void
150+
) {
151+
self.eventLoop.preconditionInEventLoop()
152+
// This is safe: we verified we're on the event loop, which is this actor's executor.
153+
withoutActuallyEscaping(operation) { escapingOperation in
154+
let strippedOperation = unsafeBitCast(
155+
escapingOperation,
156+
to: ((LambdaRuntimeClient) -> Void).self
157+
)
158+
strippedOperation(self)
159+
}
160+
}
161+
136162
@usableFromInline
137163
func close() async {
138164
self.logger.trace("Close lambda runtime client")
@@ -435,8 +461,8 @@ final actor LambdaRuntimeClient: LambdaRuntimeClientProtocol {
435461
"lambda_ip": "\(self.configuration.ip)",
436462
]
437463
)
438-
channel.closeFuture.whenComplete { result in
439-
self.assumeIsolated { runtimeClient in
464+
channel.closeFuture.whenComplete { _ in
465+
self.assumeIsolatedOnEventLoop { runtimeClient in
440466
// close the channel
441467
runtimeClient.channelClosed(channel)
442468
// Note: Do NOT set connectionState = .disconnected here!
@@ -483,7 +509,7 @@ extension LambdaRuntimeClient: LambdaChannelHandlerDelegate {
483509
nonisolated func connectionErrorHappened(_ error: any Error, channel: any Channel) {}
484510

485511
nonisolated func connectionWillClose(channel: any Channel) {
486-
self.assumeIsolated { isolated in
512+
self.assumeIsolatedOnEventLoop { isolated in
487513
switch isolated.connectionState {
488514
case .disconnected:
489515
// this case should never happen. But whatever

0 commit comments

Comments
 (0)