final audit pass with minor changes
This commit is contained in:
@@ -78,7 +78,7 @@
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html" class="active"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
|
||||
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_async_await.html"><strong aria-hidden="true">4.</strong> Generators and async/await</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html" class="active"><strong aria-hidden="true">6.</strong> Implementing Futures</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
@@ -154,7 +154,8 @@
|
||||
executor which allows you to edit, run an play around with the code right here
|
||||
in your browser.</p>
|
||||
<p>I'll walk you through the example, but if you want to check it out closer, you
|
||||
can always <a href="https://github.com/cfsamson/examples-futures">clone the repository</a> and play around with the code yourself.</p>
|
||||
can always <a href="https://github.com/cfsamson/examples-futures">clone the repository</a> and play around with the code
|
||||
yourself or just copy it from the next chapter.</p>
|
||||
<p>There are several branches explained in the readme, but two are
|
||||
relevant for this chapter. The <code>main</code> branch is the example we go through here,
|
||||
and the <code>basic_example_commented</code> branch is this example with extensive
|
||||
@@ -220,29 +221,29 @@ fn block_on<F: Future>(mut future: F) -> F::Output {
|
||||
val
|
||||
}
|
||||
</code></pre>
|
||||
<p>Inn all the examples here I've chose to comment the code extensively. I find it
|
||||
easier to follow that way than dividing if up into many paragraphs.</p>
|
||||
<p>We'll see more about the <code>Waker</code> in the next paragraph, but just look at it like
|
||||
a <em>trait object</em> similar to the one we constructed in the first chapter.</p>
|
||||
<p>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.</p>
|
||||
<p>Now that you've read so much about <code>Generators</code> and <code>Pin</code> already this should
|
||||
be rather easy to understand. <code>Future</code> is a state machine, every <code>await</code> point
|
||||
is a <code>yield</code> point. We could borrow data across <code>await</code> points and we meet the
|
||||
exact same challenges as we do when borrowing across <code>yield</code> points.</p>
|
||||
<blockquote>
|
||||
<p><code>Context</code> is just a wrapper around the <code>Waker</code>. At the time of writing this
|
||||
book it's nothing more. In the future it might be possible that the <code>Context</code>
|
||||
object will do more than just wrapping a <code>Future</code> so having this extra
|
||||
abstraction gives some flexibility.</p>
|
||||
</blockquote>
|
||||
<p>You'll notice how we use <code>Pin</code> here to pin the future when we poll it.</p>
|
||||
<p>Now that you've read so much about <code>Generators</code> and <code>Pin</code> already this should
|
||||
be rather easy to understand. <code>Future</code> is a state machine, every <code>await</code> point
|
||||
is a <code>yield</code> point. We could borrow data across <code>await</code> points and we meet the
|
||||
exact same challenges as we do when borrowing across <code>yield</code> points.</p>
|
||||
<p>As we explained in the <a href="./3_generators_pin.html">chapter about generators</a>, we use
|
||||
<p>As explained in the <a href="./3_generators_pin.html">chapter about generators</a>, we use
|
||||
<code>Pin</code> and the guarantees that give us to allow <code>Futures</code> to have self
|
||||
references.</p>
|
||||
<h2><a class="header" href="#the-future-implementation" id="the-future-implementation">The <code>Future</code> implementation</a></h2>
|
||||
<p>In Rust we call an interruptible task a <code>Future</code>. Futures has a well defined interface, which means they can be used across the entire ecosystem. We can chain
|
||||
these <code>Futures</code> so that once a "leaf future" is ready we'll perform a set of
|
||||
operations.</p>
|
||||
<p>These chained operations can spawn new leaf futures themselves.</p>
|
||||
<p>Futures has a well defined interface, which means they can be used across the
|
||||
entire ecosystem. </p>
|
||||
<p>We can chain these <code>Futures</code> so that once a <strong>leaf-future</strong> is
|
||||
ready we'll perform a set of operations until either the task is finished or we
|
||||
reach yet another <strong>leaf-future</strong> which we'll wait for and yield control to the
|
||||
scheduler.</p>
|
||||
<p><strong>Our Future implementation looks like this:</strong></p>
|
||||
<pre><code class="language-rust noplaypen ignore">// This is the definition of our `Waker`. We use a regular thread-handle here.
|
||||
// It works but it's not a good solution. It's easy to fix though, I'll explain
|
||||
@@ -389,32 +390,31 @@ could cause the same deadlock if we're unlucky.</p>
|
||||
</blockquote>
|
||||
<p>There are several better solutions, here are some:</p>
|
||||
<ul>
|
||||
<li>Use <a href="https://doc.rust-lang.org/stable/std/sync/struct.Condvar.html">std::sync::CondVar</a></li>
|
||||
<li>Use <a href="https://docs.rs/crossbeam/0.7.3/crossbeam/sync/struct.Parker.html">crossbeam::sync::Parker</a></li>
|
||||
<li><a href="https://doc.rust-lang.org/stable/std/sync/struct.Condvar.html">std::sync::CondVar</a></li>
|
||||
<li><a href="https://docs.rs/crossbeam/0.7.3/crossbeam/sync/struct.Parker.html">crossbeam::sync::Parker</a></li>
|
||||
</ul>
|
||||
<h2><a class="header" href="#the-reactor" id="the-reactor">The Reactor</a></h2>
|
||||
<p>This is the home stretch, and not strictly <code>Future</code> related, but we need one
|
||||
to have an example to run.</p>
|
||||
<p>Since concurrency mostly makes sense when interacting with the outside world (or
|
||||
at least some peripheral), we need something to actually abstract over this
|
||||
interaction in an asynchronous way. </p>
|
||||
<p>This is the <code>Reactors</code> job. Most often you'll see reactors in Rust use a library called <a href="https://github.com/tokio-rs/mio">Mio</a>, which provides non
|
||||
blocking APIs and event notification for several platforms.</p>
|
||||
<p>The reactor will typically give you something like a <code>TcpStream</code> (or any other resource) which you'll use to create an I/O request. What you get in return
|
||||
is a <code>Future</code>. </p>
|
||||
interaction in an asynchronous way.</p>
|
||||
<p>This is the <code>Reactors</code> job. Most often you'll see reactors in Rust use a library
|
||||
called <a href="https://github.com/tokio-rs/mio">Mio</a>, which provides non blocking APIs and event notification for
|
||||
several platforms.</p>
|
||||
<p>The reactor will typically give you something like a <code>TcpStream</code> (or any other
|
||||
resource) which you'll use to create an I/O request. What you get in return is a
|
||||
<code>Future</code>. </p>
|
||||
<blockquote>
|
||||
<p>If the <code>Reactor</code> is registered as a global resource (which
|
||||
is pretty normal), our <code>Task</code> in would instead be a special <code>TcpStream</code> which
|
||||
registers interest with the global <code>Reactor</code> and no reference is needed.</p>
|
||||
<p>If our reactor did some real I/O work our <code>Task</code> in would instead be represent
|
||||
a non-blocking <code>TcpStream</code> which registers interest with the global <code>Reactor</code>.
|
||||
Passing around a reference to the Reactor itself is pretty uncommon but I find
|
||||
it makes reasoning about what's happening easier.</p>
|
||||
</blockquote>
|
||||
<p>We can call this kind of <code>Future</code> a "leaf Future", since it's some operation
|
||||
we'll actually wait on and which we can chain operations on which are performed
|
||||
once the leaf future is ready.</p>
|
||||
<p>The reactor we create here will also create <strong>leaf-futures</strong>, accept a waker and
|
||||
call it once the task is finished.</p>
|
||||
<p>The task we're implementing is the simplest I could find. It's a timer that
|
||||
only spawns a thread and puts it to sleep for a number of seconds we specify
|
||||
when acquiring the leaf-future.</p>
|
||||
<p>Our example task is a timer that only spawns a thread and puts it to sleep for
|
||||
the number of seconds we specify. The reactor we create here will create a
|
||||
<strong>leaf-future</strong> representing each timer. In return the Reactor receives a waker
|
||||
which it will call once the task is finished.</p>
|
||||
<p>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.</p>
|
||||
@@ -449,7 +449,7 @@ impl Reactor {
|
||||
let readylist = Arc::new(Mutex::new(vec![]));
|
||||
let rl_clone = readylist.clone();
|
||||
|
||||
// This `Vec` will hold handles to all threads we spawn so we can
|
||||
// This `Vec` will hold handles to all the threads we spawn so we can
|
||||
// join them later on and finish our programm in a good manner
|
||||
let mut handles = vec![];
|
||||
|
||||
@@ -536,11 +536,10 @@ impl Drop for Reactor {
|
||||
</code></pre>
|
||||
<p>It's a lot of code though, but essentially we just spawn off a new thread
|
||||
and make it sleep for some time which we specify when we create a <code>Task</code>.</p>
|
||||
<p>Now, let's test our code and see if it works. This code is actually runnable
|
||||
if you press the "play" button. Since we're sleeping for a couple of seconds
|
||||
here, just give it some time to run.</p>
|
||||
<p>In the last chapter we have the <a href="./8_finished_example.html">whole 200 lines in an editable window</a>. You can
|
||||
also copy that or edit it right in this book.</p>
|
||||
<p>Now, let's test our code and see if it works. Since we're sleeping for a couple
|
||||
of seconds here, just give it some time to run.</p>
|
||||
<p>In the last chapter we have the <a href="./8_finished_example.html">whole 200 lines in an editable window</a>
|
||||
which you can edit and change the way you like.</p>
|
||||
<pre><pre class="playpen"><code class="language-rust edition2018"># use std::{
|
||||
# future::Future, pin::Pin, sync::{mpsc::{channel, Sender}, Arc, Mutex},
|
||||
# task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
|
||||
@@ -758,19 +757,17 @@ two things:</p>
|
||||
<li>In what order the events register interest with the reactor</li>
|
||||
</ol>
|
||||
<p>The last point is relevant when we move on the the last paragraph.</p>
|
||||
<h2><a class="header" href="#asyncawait-and-concurrent-futures" id="asyncawait-and-concurrent-futures">Async/Await and concurrent Futures</a></h2>
|
||||
<h2><a class="header" href="#asyncawait-and-concurrecy" id="asyncawait-and-concurrecy">Async/Await and concurrecy</a></h2>
|
||||
<p>The <code>async</code> keyword can be used on functions as in <code>async fn(...)</code> or on a
|
||||
block as in <code>async { ... }</code>. Both will turn your function, or block, into a
|
||||
<code>Future</code>.</p>
|
||||
<p>These <code>Futures</code> are rather simple. Imagine our generator from a few chapters
|
||||
back. Every <code>await</code> point is like a <code>yield</code> point.</p>
|
||||
<p>Instead of <code>yielding</code> a value we pass in, it yields the <code>Future</code> we're awaiting,
|
||||
so when we poll a future the first time we run the code up until the first
|
||||
<code>await</code> point where it yields a new Future we poll and so on until we reach
|
||||
a <strong>leaf-future</strong>.</p>
|
||||
<p>Now, as is the case in our code, our <code>mainfut</code> contains two non-leaf futures
|
||||
which it awaits, and all that happens is that these state machines are polled
|
||||
until some "leaf future" in the end either returns <code>Ready</code> or <code>Pending</code>.</p>
|
||||
<p>Instead of <code>yielding</code> a value we pass in, we yield the result of calling <code>poll</code> on
|
||||
the next <code>Future</code> we're awaiting.</p>
|
||||
<p>Our <code>mainfut</code> contains two non-leaf futures which it will call <code>poll</code> on. <strong>Non-leaf-futures</strong>
|
||||
has a <code>poll</code> method that simply polls their inner futures and these state machines
|
||||
are polled until some "leaf future" in the end either returns <code>Ready</code> or <code>Pending</code>.</p>
|
||||
<p>The way our example is right now, it's not much better than regular synchronous
|
||||
code. For us to actually await multiple futures at the same time we somehow need
|
||||
to <code>spawn</code> them so the executor starts running them concurrently.</p>
|
||||
@@ -782,15 +779,22 @@ Future got 2 at time: 3.00.
|
||||
<pre><code class="language-ignore">Future got 1 at time: 1.00.
|
||||
Future got 2 at time: 2.00.
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>Note that this doesn't mean they need to run in parallel. They <em>can</em> run in
|
||||
parallel but there is no requirement. Remember that we're waiting for some
|
||||
external resource so we can fire off many such calls on a single thread and
|
||||
handle each event as it resolves.</p>
|
||||
</blockquote>
|
||||
<p>Now, this is the point where I'll refer you to some better resources for
|
||||
implementing just that. You should have a pretty good understanding of the
|
||||
concept of Futures by now.</p>
|
||||
implementing a better executor. You should have a pretty good understanding of
|
||||
the concept of Futures by now helping you along the way.</p>
|
||||
<p>The next step should be getting to know how more advanced runtimes work and
|
||||
how they implement different ways of running Futures to completion.</p>
|
||||
<p><a href="./conclusion.html#building-a-better-exectuor">If I were you I would read this next, and try to implement it for our example.</a>.</p>
|
||||
<p>That's actually it for now. There are probably much more to learn, but I think it
|
||||
will be easier once the fundamental concepts are there and that further
|
||||
exploration will get a lot easier.</p>
|
||||
<p>That's actually it for now. There as probably much more to learn, this is enough
|
||||
for today. </p>
|
||||
<p>I hope exploring Futures and async in general gets easier after this read and I
|
||||
do really hope that you do continue to explore further.</p>
|
||||
<p>Don't forget the exercises in the last chapter 😊.</p>
|
||||
|
||||
</main>
|
||||
|
||||
Reference in New Issue
Block a user