Skip to content

Commit b504830

Browse files
kateinoigakukunMaxDesiatovMuhammedZakir
authored
Add concurrency section (#18)
* Add concurrency section * Apply suggestions from code review Co-authored-by: Max Desiatov <max@desiatov.com> Co-authored-by: Muhammed Zakir <8190126+MuhammedZakir@users.noreply.github.com> * Apply suggestions from code review take 2 * Update src/examples/concurrency.md Co-authored-by: Max Desiatov <max@desiatov.com> Co-authored-by: Max Desiatov <max@desiatov.com> Co-authored-by: Muhammed Zakir <8190126+MuhammedZakir@users.noreply.github.com>
1 parent cfd62cb commit b504830

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed

src/examples/concurrency.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Running `async` functions in WebAssembly
2+
3+
On macOS, iOS, and Linux, `libdispatch`-based executor is used by default, but `libdispatch` is not supported in single-threaded WebAssembly environment.
4+
However, there are still two global task executors available in SwiftWasm.
5+
6+
## Cooperative Task Executor
7+
8+
`Cooperative Task Executor` is the default task executor in SwiftWasm. It is a simple single-threaded cooperative task executor implemented in [Swift Concurrency library](https://github.com/apple/swift/blob/0c67ce64874d83b2d4f8d73b899ee58f2a75527f/stdlib/public/Concurrency/CooperativeGlobalExecutor.inc).
9+
If you are not familiar with "Cooperative" in concurrent programming term, see [its definition for more details](https://en.wikipedia.org/wiki/Cooperative_multitasking).
10+
11+
This executor has an *event loop* that dispatches tasks until no more tasks are enqueued, and exits immediately after all tasks are dispatched.
12+
Note that this executor won't yield control to the host environment during execution, so any host's async operation cannot call back to the Wasm execution.
13+
14+
This executor is suitable for WASI command line tools, or host-independent standalone applications.
15+
16+
```swift
17+
// USAGE
18+
// $ swiftc -target wasm32-unknown-wasi -parse-as-library main.swift -o main.wasm
19+
// $ wasmtime main.wasm
20+
@main
21+
struct Main {
22+
static func main() async throws {
23+
print("Sleeping for 1 second... 😴")
24+
try await Task.sleep(nanoseconds: 1_000_000_000)
25+
print("Wake up! 😁")
26+
}
27+
}
28+
```
29+
30+
## JavaScript Event Loop-based Task Executor
31+
32+
`JavaScript Event Loop-based Task Executor` is a task executor that cooperates with the JavaScript's event loop. It is provided by [`JavaScriptKit`](https://github.com/swiftwasm/JavaScriptKit), and you need to activate it explicitly by calling a predefined `JavaScriptEventLoop.installGlobalExecutor()` function (see below for more details).
33+
34+
This executor also has its own *event loop* that dispatches tasks until no more tasks are enqueued synchronously.
35+
It yields control to the JavaScript side after all pending tasks are dispatched, so JavaScript program can call back to the executed Wasm module.
36+
After a task is resumed by callbacks from JavaScript, the executor starts its event loop again in the next microtask tick.
37+
38+
To enable this executor, you need to use `JavaScriptEventLoop` module, which is provided as a part of `JavaScriptKit` package.
39+
40+
0. Ensure that you added `JavaScriptKit` dependency to your `Package.swift`
41+
1. Add `JavaScriptEventLoop` dependency to your targets that use this executor
42+
43+
```swift
44+
.product(name: "JavaScriptEventLoop", package: "JavaScriptKit"),
45+
```
46+
2. Import `JavaScriptEventLoop` and call `JavaScriptEventLoop.installGlobalExecutor()` before spawning any tasks to activate the executor instead of the default cooperative executor.
47+
48+
Note that this executor is only available on JavaScript host environment.
49+
50+
See also [`JavaScriptKit` package `README`](https://github.com/swiftwasm/JavaScriptKit/#asyncawait) for more details.
51+
52+
```swift
53+
import JavaScriptEventLoop
54+
import JavaScriptKit
55+
56+
JavaScriptEventLoop.installGlobalExecutor()
57+
58+
let document = JSObject.global.document
59+
var asyncButtonElement = document.createElement("button")
60+
_ = document.body.appendChild(asyncButtonElement)
61+
62+
asyncButtonElement.innerText = "Fetch Zen"
63+
func printZen() async throws {
64+
let fetch = JSObject.global.fetch.function!
65+
let response = try await JSPromise(fetch("https://api.github.com/zen").object!)!.value
66+
let text = try await JSPromise(response.text().object!)!.value
67+
print(text)
68+
}
69+
asyncButtonElement.onclick = .object(JSClosure { _ in
70+
Task {
71+
do {
72+
try await printZen()
73+
} catch {
74+
print(error)
75+
}
76+
}
77+
78+
return .undefined
79+
})
80+
```

0 commit comments

Comments
 (0)