Skip to content

Commit 8da40bb

Browse files
committed
explain better the reasoning behind the redundant isolation
1 parent f94017b commit 8da40bb

File tree

1 file changed

+18
-1
lines changed

1 file changed

+18
-1
lines changed

proposals/0327-actor-initializers.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -755,7 +755,24 @@ var y = x + 2
755755
#### Removing Redundant Isolation
756756

757757
Global-actor isolation on a stored property provides safe concurrent access to the storage occupied by that stored property in the type's instances.
758-
For example, if `pid` is an actor-isolated stored property (i.e., one without an observer or property wrapper), then the access `p.pid.reset()` only protects the memory read of `pid` from `p`, and not the call to `reset` afterwards. Thus, for value types (enums and structs), global-actor isolation on those stored properties fundamentally serves no use: mutations of the storage occupied by the stored property in a value type are concurrency-safe by default, thanks to copy-on-write semantics. So, we propose to remove the requirement that access to those properties are protected by isolation. That is, reading or writing those stored properties do not require an `await`.
758+
For example, if `pid` is an actor-isolated stored property (i.e., one without an observer or property wrapper), then the access `p.pid.reset()` only protects the memory read of `pid` from `p`, and not the call to `reset` afterwards. Thus, for value types (enums and structs), global-actor isolation on those stored properties fundamentally serves no use: mutations of the storage occupied by the stored property in a value type are concurrency-safe by default, because mutable variables cannot be shared between tasks. For example, it is error when trying to capture a mutable var in a Sendable closure:
759+
760+
```swift
761+
@MainActor
762+
struct StatTracker {
763+
var count = 0
764+
765+
mutating func update() {
766+
count += 1
767+
}
768+
}
769+
770+
var st = StatTracker()
771+
Task { await st.update() } // error: mutation of captured var 'st' in concurrently-executing code
772+
```
773+
774+
As a result, there is no way to concurrently mutate the memory of a struct, regardless of whether the stored properties of the struct are isolated to a global actor. Whether the instance can be shared only depends on whether it's var-bound or not, and the only kind of sharing permitted is via copying. Any mutations of reference types stored _within_ the struct require the usual actor-isolation applied to that reference type itself. In other words, applying global-actor isolation to a stored property containing a class type does _not_ protect the members of that class instance from concurrent access.
775+
So, we propose to remove the requirement that access to those properties are protected by isolation. That is, accessing those stored properties do not require an `await`.
759776

760777
The [global actors](0316-global-actors.md) proposal explicitly excludes actor types from having stored properties that are global-actor isolated. But in Swift 5.5, that is not enforced by the compiler. We feel that the rule should be enforced, i.e., the storage of an actor should uniformly be isolated to the actor instance. One benefit of this rule is that it reduces the possibility of [false sharing](https://en.wikipedia.org/wiki/False_sharing) among threads. Specifically, only one thread will have write access the memory occupied by an actor instance at any given time.
761778

0 commit comments

Comments
 (0)