From fd4619a1be314b89a0c1efb7396e61564c86b945 Mon Sep 17 00:00:00 2001 From: Pedro de Matos Fedricci Date: Tue, 1 Feb 2022 23:20:00 -0300 Subject: [PATCH] Fix some links, names and linter warnings --- src/0_background_information.md | 3 ++- src/1_futures_in_rust.md | 13 ++++++------- src/5_pin.md | 1 - src/6_future_example.md | 23 +++++++++++------------ src/7_finished_example.md | 6 ++---- src/conclusion.md | 4 ++-- src/introduction.md | 10 +++++----- 7 files changed, 28 insertions(+), 32 deletions(-) diff --git a/src/0_background_information.md b/src/0_background_information.md index 0563b2f..f978627 100644 --- a/src/0_background_information.md +++ b/src/0_background_information.md @@ -92,6 +92,7 @@ that they implement a way to do multitasking by having a "userland" runtime. ## Green threads/stackful coroutines + In this book I'll use the term "green threads" to mean stackful coroutines to differentiate them from the other continuation mechanisms described in this chapter. You can, however, see the term "green threads" be used to describe a broader set of continuation mechanisms in different @@ -108,7 +109,7 @@ Rust had green threads once, but they were removed before it hit 1.0. The state of execution is stored in each stack so in such a solution there would be no need for `async`, `await`, `Future` or `Pin`. In many ways, green threads mimics how an operating system facilitates concurrency, and implementing them is a great -learning experience. +learning experience. **The typical flow looks like this:** diff --git a/src/1_futures_in_rust.md b/src/1_futures_in_rust.md index e0ffef2..11c9216 100644 --- a/src/1_futures_in_rust.md +++ b/src/1_futures_in_rust.md @@ -107,7 +107,7 @@ you'll just use the one provided for you. I find it easier to reason about how Futures work by creating a high level mental model we can use. To do that I have to introduce the concept of a runtime which will drive our Futures to completion. ->Please note that the mental model I create here is not the *only* way to drive Futures to +>Please note that the mental model I create here is not the **only** way to drive Futures to completion and that Rust’s Futures does not impose any restrictions on how you actually accomplish this task. @@ -217,7 +217,7 @@ The problem with #2 is that if you switch runtime you need to make sure that it supports this kind of supervision as well or else you will end up blocking the executor. -#3 is more of theoretical importance, normally you'd be happy by sending the task +And #3 is more of theoretical importance, normally you'd be happy by sending the task to the thread-pool most runtimes provide. Most executors have a way to accomplish #1 using methods like `spawn_blocking`. @@ -238,10 +238,10 @@ general, I know where you're coming from and I have written some resources to try to give a high-level overview that will make it easier to learn Rust's Futures afterwards: -* [Async Basics - The difference between concurrency and parallelism](https://cfsamson.github.io/book-exploring-async-basics/1_concurrent_vs_parallel.html) -* [Async Basics - Async history](https://cfsamson.github.io/book-exploring-async-basics/2_async_history.html) -* [Async Basics - Strategies for handling I/O](https://cfsamson.github.io/book-exploring-async-basics/5_strategies_for_handling_io.html) -* [Async Basics - Epoll, Kqueue and IOCP](https://cfsamson.github.io/book-exploring-async-basics/6_epoll_kqueue_iocp.html) +- [Async Basics - The difference between concurrency and parallelism](https://cfsamson.github.io/book-exploring-async-basics/1_concurrent_vs_parallel.html) +- [Async Basics - Async history](https://cfsamson.github.io/book-exploring-async-basics/2_async_history.html) +- [Async Basics - Strategies for handling I/O](https://cfsamson.github.io/book-exploring-async-basics/5_strategies_for_handling_io.html) +- [Async Basics - Epoll, Kqueue and IOCP](https://cfsamson.github.io/book-exploring-async-basics/6_epoll_kqueue_iocp.html) Learning these concepts by studying futures is making it much harder than it needs to be, so go on and read these chapters if you feel a bit unsure. @@ -307,4 +307,3 @@ understanding of what the concerns are. [tokio]: https://github.com/tokio-rs/tokio [compat_info]: https://rust-lang.github.io/futures-rs/blog/2019/04/18/compatibility-layer.html [futures_rs]: https://github.com/rust-lang/futures-rs - diff --git a/src/5_pin.md b/src/5_pin.md index 54e6b44..7b5741b 100644 --- a/src/5_pin.md +++ b/src/5_pin.md @@ -581,7 +581,6 @@ by adding `std::marker::PhantomPinned` to your type on stable. > guarantees you normally get from the compiler. An `unsafe` implementation can > be perfectly safe to do, but you have no safety net. - ### Projection/structural pinning In short, projection is a programming language term. `mystruct.field1` is a diff --git a/src/6_future_example.md b/src/6_future_example.md index 536bad9..9069346 100644 --- a/src/6_future_example.md +++ b/src/6_future_example.md @@ -44,7 +44,7 @@ The first thing an `executor` does when it gets a `Future` is polling it. Rust provides a way for the Reactor and Executor to communicate through the `Waker`. The reactor stores this `Waker` and calls `Waker::wake()` on it once a `Future` has resolved and should be polled again. -> Notice that this chapter has a bonus section called [A Proper Way to Park our Thread](./6_future_example.md#bonus-section---a-proper-way-to-park-our-thread) which shows how to avoid `thread::park`. +> Notice that this chapter has a bonus section called [A Proper Way to Park our Thread](./6_future_example.md#bonus-section---a-proper-way-to-park-our-thread) which shows how to avoid `thread::park`. **Our Executor will look like this:** @@ -55,7 +55,7 @@ fn block_on(mut future: F) -> F::Output { // the first thing we do is to construct a `Waker` which we'll pass on to // the `reactor` so it can wake us up when an event is ready. let mywaker = Arc::new(MyWaker{ thread: thread::current() }); - let waker = waker_into_waker(Arc::into_raw(mywaker)); + let waker = mywaker_into_waker(Arc::into_raw(mywaker)); // The context struct is just a wrapper for a `Waker` object. Maybe in the // future this will do more, but right now it's just a wrapper. @@ -89,10 +89,10 @@ In all the examples you'll see in this chapter I've chosen to comment the code extensively. I find it easier to follow along that way so I'll not repeat myself here and focus only on some important aspects that might need further explanation. -It's worth noting that simply calling `thread::sleep` as we do here can lead to +It's worth noting that simply calling `thread::park` as we do here can lead to both deadlocks and errors. We'll explain a bit more later and fix this if you -read all the way to the [Bonus Section](./6_future_example.md##bonus-section---a-proper-way-to-park-our-thread) at -the end of this chapter. +read all the way to the [Bonus Section](./6_future_example.md#bonus-section---a-proper-way-to-park-our-thread) +at the end of this chapter. For now, we keep it as simple and easy to understand as we can by just going to sleep. @@ -104,10 +104,10 @@ exact same challenges as we do when borrowing across `yield` points. > `Context` is just a wrapper around the `Waker`. At the time of writing this book it's nothing more. In the future it might be possible that the `Context` -object will do more than just wrapping a `Future` so having this extra +object will do more than just wrapping a `Waker` so having this extra abstraction gives some flexibility. -As explained in the [chapter about generators](./3_generators_pin.md), we use +As explained in the [chapter about Pin](./5_pin.md), we use `Pin` and the guarantees that give us to allow `Future`s to have self references. @@ -175,7 +175,7 @@ const VTABLE: RawWakerVTable = unsafe { // Instead of implementing this on the `MyWaker` object in `impl Mywaker...` we // just use this pattern instead since it saves us some lines of code. -fn waker_into_waker(s: *const MyWaker) -> Waker { +fn mywaker_into_waker(s: *const MyWaker) -> Waker { let raw_waker = RawWaker::new(s as *const (), &VTABLE); unsafe { Waker::from_raw(raw_waker) } } @@ -299,7 +299,6 @@ To be able to run the code here in the browser there is not much real I/O we can do so just pretend that this is actually represents some useful I/O operation for the sake of this example. - **Our Reactor will look like this:** ```rust, noplaypen, ignore @@ -494,7 +493,7 @@ fn main() { # let mywaker = Arc::new(MyWaker { # thread: thread::current(), # }); -# let waker = waker_into_waker(Arc::into_raw(mywaker)); +# let waker = mywaker_into_waker(Arc::into_raw(mywaker)); # let mut cx = Context::from_waker(&waker); # # // SAFETY: we shadow `future` so it can't be accessed again. @@ -542,7 +541,7 @@ fn main() { # ) # }; # -# fn waker_into_waker(s: *const MyWaker) -> Waker { +# fn mywaker_into_waker(s: *const MyWaker) -> Waker { # let raw_waker = RawWaker::new(s as *const (), &VTABLE); # unsafe { Waker::from_raw(raw_waker) } # } @@ -735,7 +734,7 @@ Don't forget the exercises in the last chapter 😊. ## Bonus Section - a Proper Way to Park our Thread -As we explained earlier in our chapter, simply calling `thread::sleep` is not really +As we explained earlier in our chapter, simply calling `thread::park` is not really sufficient to implement a proper reactor. You can also reach a tool like the `Parker` in crossbeam: [crossbeam::sync::Parker][crossbeam_parker] diff --git a/src/7_finished_example.md b/src/7_finished_example.md index 25e24a1..f42dd97 100644 --- a/src/7_finished_example.md +++ b/src/7_finished_example.md @@ -209,9 +209,7 @@ impl Drop for Reactor { ## A little side note -The comments delimiting the Executor, `Future` implementation and Reactor, -emphasize on what is part of the language (Future and Waker) and what is not (runtime specifics). +The comments delimiting the Executor, `Future` implementation and Reactor, +emphasize on what is part of the language (Future and Waker) and what is not (runtime specifics). Therefore, the comments associate the `Waker` with the `Future` implementation, despite its strong relation with the Executor. - - diff --git a/src/conclusion.md b/src/conclusion.md index 17d6dec..fcaf020 100644 --- a/src/conclusion.md +++ b/src/conclusion.md @@ -76,7 +76,7 @@ linked to in the book, here are some of my suggestions: [The Tokio Blog](https://tokio.rs/blog/2019-10-scheduler/) -[Stjepan's blog with a series where he implements an Executor](https://stjepang.github.io/) +[Stjepan's blog with a series where he implements an Executor](https://web.archive.org/web/20200207092849/https://stjepang.github.io/2020/01/31/build-your-own-executor.html) [Jon Gjengset's video on The Why, What and How of Pinning in Rust](https://youtu.be/DkMwYxfSYNQ) @@ -84,4 +84,4 @@ linked to in the book, here are some of my suggestions: [condvar_std]: https://doc.rust-lang.org/stable/std/sync/struct.Condvar.html [condvar_wiki]: https://en.wikipedia.org/wiki/Monitor_(synchronization)#Condition_variables -[arcwake]: https://rust-lang-nursery.github.io/futures-api-docs/0.3.0-alpha.13/futures/task/trait.ArcWake.html \ No newline at end of file +[arcwake]: https://rust-lang-nursery.github.io/futures-api-docs/0.3.0-alpha.13/futures/task/trait.ArcWake.html diff --git a/src/introduction.md b/src/introduction.md index a95eb65..b01e9c5 100644 --- a/src/introduction.md +++ b/src/introduction.md @@ -16,13 +16,13 @@ topic of different types of executors and runtimes. We'll just implement a very simple runtime in this book introducing some concepts but it's enough to get started. -[Stjepan Glavina](https://github.com/stjepang) has made an excellent series of -articles about async runtimes and executors, and if the rumors are right there -is more to come from him in the near future. +[Stjepan Glavina](https://web.archive.org/web/20200812203230/https://github.com/stjepang) +has made an excellent series of articles about async runtimes and executors, +and if the rumors are right there is more to come from him in the near future. The way you should go about it is to read this book first, then continue -reading the [articles from stjepang](https://stjepang.github.io/) to learn more -about runtimes and how they work, especially: +reading [Stjepan's articles](https://web.archive.org/web/20200610130514/https://stjepang.github.io/) +to learn more about runtimes and how they work, especially: 1. [Build your own block_on()](https://web.archive.org/web/20200511234503/https://stjepang.github.io/2020/01/25/build-your-own-block-on.html) 2. [Build your own executor](https://web.archive.org/web/20200207092849/https://stjepang.github.io/2020/01/31/build-your-own-executor.html)