@@ -30,7 +30,7 @@ without any help from the compiler.
30
30
### Sendable Types
31
31
32
32
``` swift
33
- var islandsInTheSea = 42
33
+ var supportedStyleCount = 42
34
34
```
35
35
36
36
Here, we have defined a global variable.
@@ -39,61 +39,61 @@ isolation domain. Compiling the above code in Swift 6 mode
39
39
produces an error message:
40
40
41
41
```
42
- 1 | var islandsInTheSea = 42
43
- | |- error: global variable 'islandsInTheSea ' is not concurrency-safe because it is non-isolated global shared mutable state
44
- | |- note: convert 'islandsInTheSea ' to a 'let' constant to make the shared state immutable
45
- | |- note: restrict 'islandsInTheSea ' to the main actor if it will only be accessed from the main thread
46
- | |- note: unsafely mark 'islandsInTheSea ' as concurrency-safe if all accesses are protected by an external synchronization mechanism
42
+ 1 | var supportedStyleCount = 42
43
+ | |- error: global variable 'supportedStyleCount ' is not concurrency-safe because it is non-isolated global shared mutable state
44
+ | |- note: convert 'supportedStyleCount ' to a 'let' constant to make the shared state immutable
45
+ | |- note: restrict 'supportedStyleCount ' to the main actor if it will only be accessed from the main thread
46
+ | |- note: unsafely mark 'supportedStyleCount ' as concurrency-safe if all accesses are protected by an external synchronization mechanism
47
47
2 |
48
48
```
49
49
50
50
Two functions with different isolation domains accessing this
51
- variable risks a data race. In the following code, ` printIslands ()`
51
+ variable risks a data race. In the following code, ` printSupportedStyles ()`
52
52
could be running on the main actor concurrently with a call to
53
- ` addIsland ()` from another isolation domain:
53
+ ` addNewStyle ()` from another isolation domain:
54
54
55
55
``` swift
56
56
@MainActor
57
- func printIslands () {
58
- print (" Islands in the sea of concurrency : " , islandsInTheSea )
57
+ func printSupportedStyles () {
58
+ print (" Supported styles : " , supportedStyleCount )
59
59
}
60
60
61
- func addIsland () {
62
- let island = Island ()
61
+ func addNewStyle () {
62
+ let style = Style ()
63
63
64
- islandsInTheSea += 1
64
+ supportedStyleCount += 1
65
65
66
- addToMap (island )
66
+ storeStyle (style )
67
67
}
68
68
```
69
69
70
70
One way to address the problem is by changing variable's isolation.
71
71
72
72
``` swift
73
73
@MainActor
74
- var islandsInTheSea = 42
74
+ var supportedStyleCount = 42
75
75
```
76
76
77
77
The variable remains mutable, but has been isolated to a global actor.
78
78
All accesses can now only happen in one isolation domain, and the synchronous
79
- access within ` addIsland ` would be invalid at compile time.
79
+ access within ` addNewStyle ` would be invalid at compile time.
80
80
81
81
If the variable is meant to be constant and is never mutated,
82
82
a straight-forward solution is to express this to the compiler.
83
83
By changing the ` var ` to a ` let ` , the compiler can statically
84
84
disallow mutation, guaranteeing safe read-only access.
85
85
86
86
``` swift
87
- let islandsInTheSea = 42
87
+ let supportedStyleCount = 42
88
88
```
89
89
90
90
If there is synchronization in place that protects this variable in a way that
91
91
is invisible to the compiler, you can disable all isolation checking for
92
- ` islandsInTheSea ` using the ` nonisolated(unsafe) ` keyword:
92
+ ` supportedStyleCount ` using the ` nonisolated(unsafe) ` keyword:
93
93
94
94
``` swift
95
- /// This value is only ever accessed while holding `islandLock `.
96
- nonisolated (unsafe) var islandsInTheSea = 42
95
+ /// This value is only ever accessed while holding `styleLock `.
96
+ nonisolated (unsafe) var supportedStyleCount = 42
97
97
```
98
98
99
99
Only use ` nonisolated(unsafe) ` when you are carefully guarding all access to
@@ -108,36 +108,35 @@ Global _reference_ types present an additional challenge, because they
108
108
are typically not ` Sendable ` .
109
109
110
110
``` swift
111
- class Chicken {
112
- let name: String
113
- var currentHunger: HungerLevel
111
+ class WindowStyler {
112
+ var background: ColorComponents
114
113
115
- static let prizedHen = Chicken ()
114
+ static let defaultStyler = WindowStyler ()
116
115
}
117
116
```
118
117
119
118
The problem with this ` static let ` declaration is not related to the
120
119
mutability of the variable.
121
- The issue is ` Chicken ` is a non-Sendable type, making its internal state
120
+ The issue is ` WindowStyler ` is a non-` Sendable ` type, making its internal state
122
121
unsafe to share across isolation domains.
123
122
124
123
``` swift
125
- func feedPrizedHen () {
126
- Chicken. prizedHen . currentHunger = . wellFed
124
+ func resetDefaultStyle () {
125
+ WindowStyler. defaultStyler . background = ColorComponents ( red : 1.0 , green : 1.0 , blue : 1.0 )
127
126
}
128
127
129
128
@MainActor
130
- class ChickenValley {
131
- var flock : [Chicken ]
129
+ class StyleStore {
130
+ var stylers : [WindowStyler ]
132
131
133
- func compareHunger () -> Bool {
134
- flock .contains { $0 .currentHunger > Chicken. prizedHen . currentHunger }
132
+ func hasDefaultBackground () -> Bool {
133
+ stylers .contains { $0 .background == WindowStyler. defaultStyler . background }
135
134
}
136
135
}
137
136
```
138
137
139
138
Here, we see two functions that could access the internal state of the
140
- ` Chicken.prizedHen ` concurrently.
139
+ ` WindowStyler.defaultStyler ` concurrently.
141
140
The compiler only permits these kinds of cross-isolation accesses with
142
141
` Sendable ` types.
143
142
One option is to isolate the variable to a single domain using a global actor.
0 commit comments