Skip to content

Commit e304a1a

Browse files
chore: For WASI builds only, use fatalError in all .wait() calls. Recommend using .get() instead. (#3421)
Use fatalError in all .wait() calls for WASI builds only, and point developers towards .get() instead. ### Motivation: While working to adopt NIO in some wasm code, I've commonly ran into issues any time code calls `.wait()` during wasm runtime. Typically the executable either traps or crashes any time `.wait()` is called with an ambiguous error: `Uncaught (in promise) RuntimeError: Atomics.wait cannot be called in this context`. The error occurs because it is forbidden to block the main thread for a wasm executable, and the current implementation of `.wait()` blocks the calling thread. The fix is straight forward, all calls to `.wait()` need refactor to `.get()`, which sometimes involves some Swift Concurrency adoption (eg. `async`) in the process. That change avoids blocking the main thread. ### Modifications: Added `fatalError` with a descriptive error message to help developers identify the issue easier. ### Result: For WASI builds only, changes the trap error message `Uncaught (in promise) RuntimeError: Atomics.wait cannot be called in this context` into the following error message instead: ``` NIO's wait() function should not be called on WASI platforms. It will freeze or crash. Use get() instead. ``` ### Alternatives considered - We could instead conditionalize `.wait()` completely out of nio for WASI builds, to turn runtime errors into compiler errors. That makes detection of this issue easier, but forces a refactor and breaking change, and somewhat precludes the possibility that WASI might support this down the road with a future change. - We could add a deprecation for WASI only. But long term WASI may be able to support this blocking call, so deprecation and removal is communicating the wrong message if the future ends up being reality. ### Context This PR is [part of a larger effort by PassiveLogic](PassiveLogic/swift-web-examples#1) to move Swift for WebAssembly support forward in a large number of dependencies.
1 parent fa91d79 commit e304a1a

File tree

1 file changed

+20
-0
lines changed

1 file changed

+20
-0
lines changed

Sources/NIOCore/EventLoopFuture.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,7 +1091,27 @@ extension EventLoopFuture {
10911091
@preconcurrency
10921092
@inlinable
10931093
public func wait(file: StaticString = #file, line: UInt = #line) throws -> Value where Value: Sendable {
1094+
#if os(WASI)
1095+
// NOTE: As of July 22, 2025 `wait()` calling wait() is not supported on WASI platforms.
1096+
//
1097+
// This may change down the road if and when true multi-threading evolves. But right now
1098+
// calling wait here results in the following runtime crash:
1099+
//
1100+
// ```
1101+
// SomeExecutable.wasm:0x123456 Uncaught (in promise) RuntimeError: Atomics.wait cannot be called in this context
1102+
// ```
1103+
//
1104+
// Using the following fatal error here gives wasm runtime users a much more clear error message
1105+
// to identify the issue.
1106+
//
1107+
// If you're running into this error on WASI, refactoring to `get()` instead of `wait()` will
1108+
// likely solve the issue.
1109+
fatalError(
1110+
"NIO's wait() function should not be called on WASI platforms. It will freeze or crash. Use get() instead."
1111+
)
1112+
#else
10941113
try self._blockingWaitForFutureCompletion(file: file, line: line)
1114+
#endif
10951115
}
10961116

10971117
@inlinable

0 commit comments

Comments
 (0)