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
Copy file name to clipboardExpand all lines: src/vision/roadmap/polish/sync_and_async.md
+96Lines changed: 96 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -17,3 +17,99 @@ unexpected should happen.
17
17
| Create testing to ensure same behavior | 💤 |[yoshuawuyts]|
18
18
19
19
[yoshuawuyts]: https://github.com/yoshuawuyts
20
+
21
+
## Details
22
+
23
+
Ideally, there should not be a lot of work needed specifically to achieve this goal.
24
+
Instead, the primary aim is to define principles that can inform the design in other places.
25
+
That said, automated testing to verify we have achieved these principles may take significant effort.
26
+
27
+
There are several ways we can look at what it means to behave the same.
28
+
One way is from a language and semantics standpoint, while another is from a library and ecosystem standpoint.
29
+
We will look at each of these in more detail, and then lay out some ideas for automated testing.
30
+
31
+
### Language and Semantics
32
+
33
+
Roughly what we want here is that code that differs only in its syncness should do the same thing.
34
+
Of course, this is not strictly possible because a sync and async program are fundamentally different programs.
35
+
We still want something approximating this.
36
+
Below are several principles that try to make this more precise.
37
+
For each one, we are talking about a synchronous and asynchronous version of a piece of code where the synchronous version is basically the async version with all the `async` and `.await` keywords removed.
38
+
Or conversely, we can view the async version as the sync version where all `fn`s have been replaced with `async fn` and all calls have `.await` added.
39
+
Note that this assumes there are no manually implemented futures.
40
+
This is an intentionally restrictive subset to focus on the core language semantics.
41
+
In the Library and Ecosystem section, we will discuss replacing standard library functionality with async equivalents to make this comparison more interesting.
42
+
43
+
1.**Equality**: if the sync version and the async version both produce a value, then the values are the same.
44
+
2.**Effects**: the same set of observable effects happen in both the sync and async version, and the effects happen in the same order, at least where order is specified. Effects here includes things such as writing to a file (although realistically the async version should use the async version of the File I/O API), observable memory reads or writes.
45
+
3.**Termination**: either both the sync and async version terminate (or can be polled to completion in the async case), or both do not terminate. Note that this is a special case of **Effects**.
46
+
4.**Panic**: the sync version panics if and only if the async version panics. Note that this is a special case of **Effects**.
47
+
5.**Types***: if the sync version of a function returns type `T` then the async version returns type `Future<Output = T>` and vice-versa. Functions or closures passed as parameters would undergo a similar transformation.
48
+
6.**Compilation***: either both the sync and async version compile successfully, or they both produce equivalent compiler errors on the same line.
49
+
50
+
The first four principles are probably not terrible hard to achieve.
51
+
The last two, marked with an asterisk, may not be completely possible or even desirable in all cases.
52
+
53
+
For types, there is a fundamental difference in the async code because `.await` points expose types that would be purely internal in the sync version.
54
+
One impact of this is that the auto traits may not be the same between the two.
55
+
We might be able to get this property in one direction though.
56
+
For example, adding a `.await` might make the future not `Send`, but removing a `.await` will probably not remove any auto traits.
57
+
See the following code for more detail:
58
+
59
+
```rust
60
+
fnsync_foo() {
61
+
lett=NonSend { ... };
62
+
bar(); // `sync_foo` is `Send` with or without this line.
63
+
}
64
+
65
+
asyncfnasync_foo() {
66
+
lett=NonSend { ... };
67
+
bar().await; // With this line, the future returned by `async_foo` is `!Send`
68
+
// because NonSend is `!Send` and is alive across the `.await`
69
+
// point. Without this line, the future returned by `async_foo`
70
+
// is `Send`.
71
+
}
72
+
```
73
+
74
+
The key difference between the sync version and the async version here is that the suspension introduced by the `.await` point reveals internal details of `async_foo` that are not observable in the `sync_foo` case.
75
+
76
+
Compilation is closely related to the types goal because if async causes the types to change then this could introduce or remove compilation errors.
77
+
Additionally, we will probably have some async-only diagnostics, such as the [`must_not_suspend` lint][must_not_suspend].
78
+
79
+
### Library and Ecosystem
80
+
81
+
At a high level, the library and ecosystem goals are about having comparable capabilities available in libraries for both sync and async code.
82
+
For example, mutexes in an async context need integration with the runtime, so the standard synchronous mutex is not generally suitable for async code, although there are cases where a sync mutex makes sense [[1]], [[2]].
83
+
For this reason, most async runtimes provide some form of `AsyncMutex`.
Note that one way to achieve a comparable sync and async library ecosystem may be through [Async Overloading].
89
+
90
+
One way to approach this is to generalize the mostly mechanical transformation we described above to also include translating library calls, and then define what properties we would want to be preserved during the translation.
91
+
We would assume for synchronous blocking APIs, such as File I/O, the `Read` and `Write` traits, etc., we have corresponding async File I/O APIs, `AsyncRead` and `AsyncWrite` traits, etc.
92
+
The [async-std] project showed that most of the Rust standard library can be pretty directly translated into async code, other than cases where there were missing language features such as [async drop], [async traits], and [async closures].
0 commit comments