Skip to content

Instantly share code, notes, and snippets.

@rnapier
Last active April 23, 2024 13:29
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rnapier/f513a58ec982ff4738b25afa465f6dda to your computer and use it in GitHub Desktop.
Save rnapier/f513a58ec982ff4738b25afa465f6dda to your computer and use it in GitHub Desktop.
Regarding MainActor.run
// In regards to https://mastodon.social/@mattiem/112285978801305971
// MainActor class with synchronous methods
@MainActor final class M {
func methodA() {}
func methodB() {}
}
// Actor that relies on M.
actor A {
func f() async {
//
// This is atomic
//
await MainActor.run {
m.methodA()
m.methodB()
}
print("M stuff is done.")
//
// This is not atomic
//
await m.methodA()
await m.methodB() // There is a suspension point between A and B
print("M stuff is done.")
//
// The equivalent to .run would be:
//
await Task { @MainActor in
m.methodA()
m.methodB()
}.value
print("M stuff is done.")
// I don't believe this approach is better than .run
// What **probably** is a better approach than either is making `M.methodAandB`
// to keep the operation atomic, and express it clearly. But if folks replace
// .run with Task, I think they're just shuffling syntax, and .run is clearer.
// It's easy to forget to add the `await` and `.value`, in which case the `print`
// happens too soon.
//
// I agree with Matt that it's possible A should be MainActor, but just because
// it happens to call M doesn't mean that's certain.
}
let m: M
init(m: M) { self.m = m }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment