You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Aug 5, 2022. It is now read-only.
Copy file name to clipboardExpand all lines: docs/concurrency-adoption-guidelines.md
+43-35Lines changed: 43 additions & 35 deletions
Original file line number
Diff line number
Diff line change
@@ -9,37 +9,16 @@ In 2021 we saw structured concurrency and actors arrive with Swift 5.5. Now is a
9
9
10
10
## What you can do right now
11
11
12
-
### `#if` guarding code using Concurrency
13
-
14
-
In order to have code using concurrency along with code not using concurrency, you may have to `#if` guard certain pieces of code. The correct way to do so is the following:
15
-
16
-
```swift
17
-
#ifcompiler(>=5.5) &&canImport(_Concurrency)
18
-
...
19
-
#endif
20
-
```
21
-
22
-
Please note that you do _not_ need to _import_ the `_Concurrency` at all, if it is present it is imported automatically.
23
-
24
-
```swift
25
-
#ifcompiler(>=5.5) &&canImport(_Concurrency)
26
-
// DO NOT DO THIS.
27
-
// Instead don't do any import and it'll import automatically when possible.
28
-
import_Concurrency
29
-
#endif
30
-
```
31
-
32
-
33
-
34
12
### API Design
35
13
36
14
Firstly, existing libraries should strive to add `async` functions where possible to their user-facing “surface” APIs in addition to existing `*Future` based APIs wherever possible. These additive APIs can be gated on the Swift version and can be added without breaking existing users' code, for example like this:
@@ -51,12 +30,13 @@ Such adoption can begin immediately, and should not cause any issues to existing
51
30
52
31
### SwiftNIO helper functions
53
32
54
-
To allow an easy transition to async code, SwiftNIO offers a number of helper methods on `EventLoopFuture` and `-Promise`. Those live in the `_NIOConcurrency` module and will move to `NIOCore` once Swift concurrency is released.
33
+
To allow an easy transition to async code, SwiftNIO offers a number of helper methods on `EventLoopFuture` and `-Promise`.
55
34
56
35
On every `EventLoopFuture` you can call `.get()` to transition the future into an `await`-able invocation. If you want to translate async/await calls to an `EventLoopFuture` we recommend the following pattern:
Further helpers exist for `EventLoopGroup`, `Channel`, `ChannelOutboundInvoker` and `ChannelPipeline`.
72
52
53
+
54
+
### `#if` guarding code using Concurrency
55
+
56
+
In order to have code using concurrency along with code not using concurrency, you may have to `#if` guard certain pieces of code. The correct way to do so is the following:
57
+
58
+
```swift
59
+
#ifcompiler(>=5.5) &&canImport(_Concurrency)
60
+
...
61
+
#endif
62
+
```
63
+
64
+
Please note that you do _not_ need to _import_ the `_Concurrency` at all, if it is present it is imported automatically.
65
+
66
+
```swift
67
+
#ifcompiler(>=5.5) &&canImport(_Concurrency)
68
+
// DO NOT DO THIS.
69
+
// Instead don't do any import and it'll import automatically when possible.
70
+
import_Concurrency
71
+
#endif
72
+
```
73
+
74
+
73
75
### Sendable Checking
74
76
75
77
> [SE-0302][SE-0302] introduced the `Sendable` protocol, which is used to indicate which types have values that can safely be copied across actors or, more generally, into any context where a copy of the value might be used concurrently with the original. Applied uniformly to all Swift code, `Sendable` checking eliminates a large class of data races caused by shared mutable state.
@@ -110,18 +112,20 @@ Here, the `Value` type must be marked `Sendable` for Swift 6’s concurrency che
110
112
In such situations, it may be helpful to utilize the following trick to be able to share the same `Container` declaration between both Swift versions of the library:
111
113
112
114
```swift
113
-
#ifcompiler(>=5.5)
115
+
#ifswift(>=5.5) &&canImport(_Concurrency)
114
116
publictypealiasMYPREFIX_Sendable= Swift.Sendable
115
117
#else
116
118
publictypealiasMYPREFIX_Sendable=Any
117
119
#endif
118
120
```
119
121
122
+
> **NOTE:** Yes, we're using `swift(>=5.5)` here, while we're using `compiler(>=5.5)` to guard specific APIs using conrrency features.
123
+
120
124
The `Any` alias is effectively a no-op when applied as generic constraint, and thus this way it is possible to keep the same `Container<Value>` declaration working across Swift versions.
121
125
122
126
### Task Local Values and Logging
123
127
124
-
The newly introduced Task Local Values API ([SE-0311][SE-0311]) allows for implicit carrying of metadata along with `Task` execution. It is a natural fit for for tracing and carrying metadata around with task execution, and e.g. including it in log messages.
128
+
The newly introduced Task Local Values API ([SE-0311][SE-0311]) allows for implicit carrying of metadata along with `Task` execution. It is a natural fit for tracing and carrying metadata around with task execution, and e.g. including it in log messages.
125
129
126
130
We are working on adjusting [SwiftLog](https://github.com/apple/swift-log) to become powerful enough to automatically pick up and log specific task local values. This change will be introduced in a source compatible way.
127
131
@@ -170,31 +174,35 @@ While we expect potential performance gains from using custom executors “on th
170
174
The guidance here will evolve as Swift Evolution proposals for Custom Executors are proposed, but don’t hold off adopting Swift Concurrency until custom executors “land” - it is important to start adoption early. For most code we believe that the gains from adopting Swift Concurrency vastly outweigh the slight performance cost actor-hops might induce.
171
175
172
176
173
-
### Reduce use of Swift NIO Futures as “Concurrency Library“
177
+
### Reduce use of SwiftNIO Futures as “Concurrency Library“
174
178
175
-
Swift NIO currently provides a number of concurrency types for the Swift on Server ecosystem. Most notably `EventLoopFuture`s and `EventLoopPromise`s, that are used widely for asynchronous results. While the SSWG recommended using those at the API level in the past for easier interplay of server libraries, we advise to deprecate or remove such APIs once Swift 6 lands. The swift-server ecosystem should go all in on the structured concurrency features the languages provides. For this reason, it is crucial to provide async/await APIs today, to give your library users time to adopt the new APIs.
179
+
SwiftNIO currently provides a number of concurrency types for the Swift on Server ecosystem. Most notably `EventLoopFuture`s and `EventLoopPromise`s, that are used widely for asynchronous results. While the SSWG recommended using those at the API level in the past for easier interplay of server libraries, we advise to deprecate or remove such APIs once Swift 6 lands. The swift-server ecosystem should go all in on the structured concurrency features the languages provides. For this reason, it is crucial to provide async/await APIs today, to give your library users time to adopt the new APIs.
176
180
177
181
Some NIO types will remain however in the public interfaces of Swift on server libraries. We expect that networking clients and servers continue to be initialized with `EventLoopGroup`s. The underlying transport mechanism (`NIOPosix` and `NIOTransportServices`) should become implementation details however and should not be exposed to library adopters.
178
182
179
183
### SwiftNIO 3
180
184
181
-
While subject to change, it is likely that Swift NIO will cut a 3.0 release in the months after Swift 6.0, at which point in time Swift will have enabled “full” `Sendable` checking.
185
+
While subject to change, it is likely that SwiftNIO will cut a 3.0 release in the months after Swift 6.0, at which point in time Swift will have enabled “full” `Sendable` checking.
186
+
187
+
You should not expect NIO to suddenly become “more async”, NIO’s inherent design principles are about performing small tasks on the event loop and using Futures for any async operations. The design of NIO is not expected to change. Channel pipelines are not expected to become "async" in the Swift Concurrency meaning of the word. This is because SwiftNIO is, at its heard, an IO system, and that poses a challenge to the co-operative, shared, thread-pool used by Swift Concurrency. This thread pool must not be blocked by any operation, because doing so will starve the pool and prevent further progress of other async tasks.
188
+
189
+
I/O systems however must, at some point, block a thread waiting for more I/O events, either in an I/O syscall or in something like epoll_wait. This is how NIO works: each of the event loop threads ultimately blocks on epoll_wait. We can’t do that inside the cooperative thread pool, as to do so would starve it for other async tasks, so we’d have to do so on a different thread. As such, SwiftNIO should not be used _on_ the cooperative threadpool, but should take ownership and full control of its threads–because it is an I/O system.
182
190
183
-
Do not expect NIO to suddenly become “more async”, NIO’s inherent design principles are about performing small tasks on the event loop and using Futures for any async operations. The design of NIO is not expected to change. It is crucial to its high performance networking design. Channel pipelines are not expected to become “async”.
191
+
It would be possible to make all NIO work happen on the co-operative pool, and thread-hop between each I/O operation and dispatching it onto the async/await pool, however this is not acceptable for high performance I/O: the context switch for _each I/O operation_ is too expensive. As a result, SwiftNIO is not planning to just adopt Swift Concurrency for the ease of use it brings, because in its specific context, the context switches are not an acceptable tradeoff. SwiftNIO could however cooperate with Swift Concurrency with the arrival of "custom executors" in the language runtime, however this has not been fully proposed yet, so we are not going to speculate about this too much.
184
192
185
193
The NIO team will however use the chance to remove deprecated APIs and improve some APIs. The scope of changes should be comparable to the NIO1 → NIO2 version bump. If your SwiftNIO code compiles today without warnings, chances are high that it will continue to work without modifications in NIO3.
186
194
187
195
After the release of NIO3, NIO2 will see bug fixes only.
188
196
189
197
### End-user code breakage
190
198
191
-
It is expected that Swift 6 will break some code. As mentioned Swift NIO 3 is also going to be released sometime around Swift 6 dropping. Keeping this in mind, it might be a good idea to align major version releases around the same time, along with updating version requirements to Swift 6 and NIO 3 in your libraries.
199
+
It is expected that Swift 6 will break some code. As mentioned SwiftNIO 3 is also going to be released sometime around Swift 6 dropping. Keeping this in mind, it might be a good idea to align major version releases around the same time, along with updating version requirements to Swift 6 and NIO 3 in your libraries.
192
200
193
-
Both Swift and Swift NIO are not planning to do “vast amounts of change”, so adoption should be possible without major pains.
201
+
Both Swift and SwiftNIO are not planning to do “vast amounts of change”, so adoption should be possible without major pains.
194
202
195
203
### Guidance for library users
196
204
197
-
As soon as Swift 6 comes out, we recommend using the latest Swift 6 toolchains, even if using the Swift 5.5.n language mode (which may yield only warnings rather than hard failures on failed Sendability checks).
205
+
As soon as Swift 6 comes out, we recommend using the latest Swift 6 toolchains, even if using the Swift 5.5.n language mode (which may yield only warnings rather than hard failures on failed Sendability checks). This will result in better warnings and compiler hints, than just using a 5.5 toolchain.
0 commit comments