Formatter (#51)

Enforce consistent formatting use `dprint`
This commit is contained in:
Luca Palmieri
2024-05-24 17:00:03 +02:00
committed by GitHub
parent 537118574b
commit 99591a715e
157 changed files with 1057 additions and 1044 deletions

View File

@@ -32,9 +32,9 @@ async fn http_call(v: &[u64]) {
### `std::sync::MutexGuard` and yield points
This code will compile, but it's dangerous.
This code will compile, but it's dangerous.
We try to acquire a lock over a `Mutex` from `std` in an asynchronous context.
We try to acquire a lock over a `Mutex` from `std` in an asynchronous context.
We then hold on to the resulting `MutexGuard` across a yield point (the `.await` on
`http_call`).
@@ -42,18 +42,18 @@ Let's imagine that there are two tasks executing `run`, concurrently, on a singl
runtime. We observe the following sequence of scheduling events:
```text
Task A Task B
|
Acquire lock
Yields to runtime
|
+--------------+
|
Tries to acquire lock
Task A Task B
|
Acquire lock
Yields to runtime
|
+--------------+
|
Tries to acquire lock
```
We have a deadlock. Task B we'll never manage to acquire the lock, because the lock
is currently held by task A, which has yielded to the runtime before releasing the
is currently held by task A, which has yielded to the runtime before releasing the
lock and won't be scheduled again because the runtime cannot preempt task B.
### `tokio::sync::Mutex`
@@ -73,32 +73,32 @@ async fn run(m: Arc<Mutex<Vec<u64>>>) {
```
Acquiring the lock is now an asynchronous operation, which yields back to the runtime
if it can't make progress.
if it can't make progress.\
Going back to the previous scenario, the following would happen:
```text
Task A Task B
|
Acquires the lock
Starts `http_call`
Yields to runtime
|
+--------------+
|
Tries to acquire the lock
Cannot acquire the lock
Yields to runtime
|
+--------------+
|
`http_call` completes
Releases the lock
Yield to runtime
|
+--------------+
|
Acquires the lock
[...]
Task A Task B
|
Acquires the lock
Starts `http_call`
Yields to runtime
|
+--------------+
|
Tries to acquire the lock
Cannot acquire the lock
Yields to runtime
|
+--------------+
|
`http_call` completes
Releases the lock
Yield to runtime
|
+--------------+
|
Acquires the lock
[...]
```
All good!
@@ -107,14 +107,14 @@ All good!
We've used a single-threaded runtime as the execution context in our
previous example, but the same risk persists even when using a multithreaded
runtime.
runtime.\
The only difference is in the number of concurrent tasks required to create the deadlock:
in a single-threaded runtime, 2 are enough; in a multithreaded runtime, we
would need `N+1` tasks, where `N` is the number of runtime threads.
would need `N+1` tasks, where `N` is the number of runtime threads.
### Downsides
Having an async-aware `Mutex` comes with a performance penalty.
Having an async-aware `Mutex` comes with a performance penalty.\
If you're confident that the lock isn't under significant contention
_and_ you're careful to never hold it across a yield point, you can
still use `std::sync::Mutex` in an asynchronous context.
@@ -124,6 +124,6 @@ will incur.
## Other primitives
We used `Mutex` as an example, but the same applies to `RwLock`, semaphores, etc.
We used `Mutex` as an example, but the same applies to `RwLock`, semaphores, etc.\
Prefer async-aware versions when working in an asynchronous context to minimise
the risk of issues.
the risk of issues.