Skip to content

Commit 0f7c991

Browse files
committed
[Educational Notes] Add an explanation for actor-isolated calls from
synchronous nonisolated contexts.
1 parent cf43771 commit 0f7c991

File tree

2 files changed

+50
-0
lines changed

2 files changed

+50
-0
lines changed

include/swift/AST/EducationalNotes.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ EDUCATIONAL_NOTES(concurrent_access_of_local_capture,
9797
"sendable-closure-captures.md")
9898
EDUCATIONAL_NOTES(concurrent_access_of_inout_param,
9999
"sendable-closure-captures.md")
100+
EDUCATIONAL_NOTES(actor_isolated_call,
101+
"actor-isolated-call.md")
102+
EDUCATIONAL_NOTES(actor_isolated_call_decl,
103+
"actor-isolated-call.md")
100104

101105
EDUCATIONAL_NOTES(error_in_swift_lang_mode,
102106
"error-in-future-swift-version.md")
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Calling an actor-isolated method from a synchronous nonisolated context
2+
3+
Calls to actor-isolated methods from outside the actor must be done asynchronously. Otherwise, access to actor state can happen concurrently and lead to data races. These rules also apply to global actors like the main actor.
4+
5+
For example:
6+
7+
```swift
8+
@MainActor
9+
class MyModel {
10+
func update() { ... }
11+
}
12+
13+
func runUpdate(model: MyModel) {
14+
model.update()
15+
}
16+
```
17+
18+
Building the above code produces an error about calling a main actor isolated method from outside the actor:
19+
20+
```
21+
| func runUpdate(model: MyModel) {
22+
| model.update()
23+
| `- error: call to main actor-isolated instance method 'update()' in a synchronous nonisolated context
24+
| }
25+
```
26+
27+
The `runUpdate` function doesn't specify any actor isolation, so it is `nonisolated` by default. `nonisolated` methods can be called from any concurrency domain. To prevent data races, `nonisolated` methods cannot access actor isolated state in their implementation. If `runUpdate` is called from off the main actor, calling `model.update()` could mutate main actor state at the same time as another task running on the main actor.
28+
29+
To resolve the error, `runUpdate` has to make sure the call to `model.update()` is on the main actor. One way to do that is to add main actor isolation to the `runUpdate` function:
30+
31+
```swift
32+
@MainActor
33+
func runUpdate(model: MyModel) {
34+
model.update()
35+
}
36+
```
37+
38+
Alternatively, if the `runUpdate` function is meant to be called from arbitrary concurrent contexts, create a task isolated to the main actor to call `model.update()`:
39+
40+
```swift
41+
func runUpdate(model: MyModel) {
42+
Task { @MainActor in
43+
model.update()
44+
}
45+
}
46+
```

0 commit comments

Comments
 (0)