continued version 2
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="1_background_information.html" class="active"><strong aria-hidden="true">1.</strong> Some background information</a></li><li><a href="2_trait_objects.html"><strong aria-hidden="true">2.</strong> Trait objects and fat pointers</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">3.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">4.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">5.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">6.</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="1_background_information.html" class="active"><strong aria-hidden="true">1.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">2.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">3.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">4.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">5.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">6.</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>
|
||||
@@ -163,60 +163,102 @@
|
||||
information that will help demystify some of the concepts we encounter.</p>
|
||||
<p>Actually, after going through these concepts, implementing futures will seem
|
||||
pretty simple. I promise.</p>
|
||||
<h2><a class="header" href="#async-in-rust" id="async-in-rust">Async in Rust</a></h2>
|
||||
<p>Let's get some of the common roadblocks out of the way first.</p>
|
||||
<p>Async in Rust is different from most other languages in the sense that Rust
|
||||
has a very lightweight runtime.</p>
|
||||
<p>Languages like C#, JavaScript, Java and GO, already includes a runtime
|
||||
for handling concurrency. So if you come from one of those languages this will
|
||||
<h2><a class="header" href="#futures" id="futures">Futures</a></h2>
|
||||
<p>So what is a future?</p>
|
||||
<p>A future is a representation of some operation which will complete in the
|
||||
future.</p>
|
||||
<p>Async in Rust uses a <code>Poll</code> based approach, in which an asynchronous task will
|
||||
have three phases.</p>
|
||||
<ol>
|
||||
<li><strong>The Poll phase.</strong> A Future is polled which result in the task progressing until
|
||||
a point where it can no longer make progress. We often refer to the part of the
|
||||
runtime which polls a Future as an executor.</li>
|
||||
<li><strong>The Wait phase.</strong> An event source, most often referred to as a reactor,
|
||||
registers that a Future is waiting for an event to happen and makes sure that it
|
||||
will wake the Future when that event is ready.</li>
|
||||
<li><strong>The Wake phase.</strong> The event happens and the Future is woken up. It's now up
|
||||
to the executor which polled the Future in step 1 to schedule the future to be
|
||||
polled again and make further progress until it completes or reaches a new point
|
||||
where it can't make further progress and the cycle repeats.</li>
|
||||
</ol>
|
||||
<p>Now, when we talk about futures I find it useful to make a distinction between
|
||||
<strong>non-leaf</strong> futures and <strong>leaf</strong> futures early on because in practice they're
|
||||
pretty different from one another.</p>
|
||||
<h3><a class="header" href="#leaf-futures" id="leaf-futures">Leaf futures</a></h3>
|
||||
<p>Runtimes create <em>leaf futures</em> which represents a resource like a socket.</p>
|
||||
<pre><code class="language-rust ignore noplaypen">// stream is a **leaf-future**
|
||||
let mut stream = tokio::net::TcpStream::connect("127.0.0.1:3000");
|
||||
</code></pre>
|
||||
<p>Operations on these resources, like a <code>Read</code> on a socket, will be non-blocking
|
||||
and return a future which we call a leaf future since it's the future which
|
||||
we're actually waiting on.</p>
|
||||
<p>It's unlikely that you'll implement a leaf future yourself unless you're writing
|
||||
a runtime, but we'll go through how they're constructed in this book as well.</p>
|
||||
<p>It's also unlikely that you'll pass a leaf-future to a runtime and run it to
|
||||
completion alone as you'll understand by reading the next paragraph.</p>
|
||||
<h3><a class="header" href="#non-leaf-futures" id="non-leaf-futures">Non-leaf-futures</a></h3>
|
||||
<p>Non-leaf-futures is the kind of futures we as <em>users</em> of a runtime writes
|
||||
ourselves using the <code>async</code> keyword to create a <strong>task</strong> which can be run on the
|
||||
executor.</p>
|
||||
<p>The bulk of an async program will consist of non-leaf-futures, which are a kind
|
||||
of pause-able computation. This is an important distinction since these futures represents a <em>set of operations</em>. Often, such a task will <code>await</code> a leaf future
|
||||
as one of many operations to complete the task.</p>
|
||||
<pre><code class="language-rust ignore noplaypen">// Non-leaf-future
|
||||
let non_leaf = async {
|
||||
let mut stream = TcpStream::connect("127.0.0.1:3000").await.unwrap();// <- yield
|
||||
println!("connected!");
|
||||
let result = stream.write(b"hello world\n").await; // <- yield
|
||||
println!("message sent!");
|
||||
...
|
||||
};
|
||||
</code></pre>
|
||||
<p>The key to these tasks is that they're able to yield control to the runtime's
|
||||
scheduler and then resume execution again where it left off at a later point.</p>
|
||||
<p>In contrast to leaf futures, these kind of futures does not themselves represent
|
||||
an I/O resource. When we poll these futures we either run some code or we yield
|
||||
to the scheduler while waiting for some resource to signal us that it's ready so
|
||||
we can resume where we left off.</p>
|
||||
<h2><a class="header" href="#runtimes" id="runtimes">Runtimes</a></h2>
|
||||
<p>Languages like C#, JavaScript, Java, GO and many others comes with a runtime
|
||||
for handling concurrency. So if you come from one of those languages this will
|
||||
seem a bit strange to you.</p>
|
||||
<p>In Rust you will have to make an active choice about which runtime to use.</p>
|
||||
<p>Rust is different from these languages in the sense that Rust doesn't come with
|
||||
a runtime for handling concurrency, so you need to use a library which provide
|
||||
this for you.</p>
|
||||
<p>Quite a bit of complexity attributed to <code>Futures</code> are actually complexity rooted
|
||||
in runtimes. Creating an efficient runtime is hard.</p>
|
||||
<p>Learning how to use one correctly requires quite a bit of effort as well, but
|
||||
you'll see that there are several similarities between these kind of runtimes so
|
||||
learning one makes learning the next much easier.</p>
|
||||
<p>The difference between Rust and other languages is that you have to make an
|
||||
active choice when it comes to picking a runtime. Most often, in other languages
|
||||
you'll just use the one provided for you.</p>
|
||||
<p>An async runtime can be divided into two parts:</p>
|
||||
<ol>
|
||||
<li>The Executor</li>
|
||||
<li>The Reactor</li>
|
||||
</ol>
|
||||
<p>When Rusts Futures were designed there was a desire to separate the job of
|
||||
notifying a <code>Future</code> that it can do more work, and actually doing the work
|
||||
on the <code>Future</code>.</p>
|
||||
<p>You can think of the former as the reactor's job, and the latter as the
|
||||
executors job. These two parts of a runtime interacts using the <code>Waker</code> type.</p>
|
||||
<p>The two most popular runtimes for <code>Futures</code> as of writing this is:</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/async-rs/async-std">async-std</a></li>
|
||||
<li><a href="https://github.com/tokio-rs/tokio">Tokio</a></li>
|
||||
</ul>
|
||||
<h3><a class="header" href="#what-rusts-standard-library-takes-care-of" id="what-rusts-standard-library-takes-care-of">What Rust's standard library takes care of</a></h3>
|
||||
<ol>
|
||||
<li>The definition of an interruptible task</li>
|
||||
<li>An efficient technique to start, suspend, resume and store tasks which are
|
||||
executed concurrently.</li>
|
||||
<li>A defined way to wake up a suspended task</li>
|
||||
<li>A common interface representing an operation which will be completed in the
|
||||
future through the <code>Future</code> trait.</li>
|
||||
<li>An ergonomic way of creating tasks which can be suspended and resumed through
|
||||
the <code>async</code> and <code>await</code> keywords.</li>
|
||||
<li>A defined interface wake up a suspended task through the <code>Waker</code> type.</li>
|
||||
</ol>
|
||||
<p>That's really what Rusts standard library does. As you see there is no definition
|
||||
of non-blocking I/O, how these tasks are created or how they're run.</p>
|
||||
<h3><a class="header" href="#what-you-need-to-find-elsewhere" id="what-you-need-to-find-elsewhere">What you need to find elsewhere</a></h3>
|
||||
<p>A runtime. Well, in Rust we normally divide the runtime into two parts:</p>
|
||||
<ul>
|
||||
<li>The Reactor</li>
|
||||
<li>The Executor</li>
|
||||
</ul>
|
||||
<p>Reactors create leaf <code>Futures</code>, and provides things like non-blocking sockets,
|
||||
an event queue and so on.</p>
|
||||
<p>Executors, accepts one or more asynchronous tasks called <code>Futures</code> and takes
|
||||
care of actually running the code we write, suspend the tasks when they're
|
||||
waiting for I/O and resume them.</p>
|
||||
<p>In theory, we could choose one <code>Reactor</code> and one <code>Executor</code> that have nothing
|
||||
to do with each other besides that one creates leaf <code>Futures</code> and the other one
|
||||
runs them, but in reality today you'll most often get both in a <code>Runtime</code>.</p>
|
||||
<p>There are mainly two such runtimes today <a href="https://github.com/async-rs/async-std">async_std</a> and <a href="https://github.com/tokio-rs/tokio">tokio</a>.</p>
|
||||
<p>Quite a bit of complexity attributed to <code>Futures</code> are actually complexity rooted
|
||||
in runtimes. Creating an efficient runtime is hard. </p>
|
||||
<p>Learning how to use one correctly can require quite a bit of effort as well, but you'll see that there are several similarities between these kind of runtimes so
|
||||
learning one makes learning the next much easier.</p>
|
||||
<p>The difference between Rust and other languages is that you have to make an
|
||||
active choice when it comes to picking a runtime. Most often you'll just use
|
||||
the one provided for you.</p>
|
||||
<h2><a class="header" href="#futures-10-and-futures-30" id="futures-10-and-futures-30">Futures 1.0 and Futures 3.0</a></h2>
|
||||
<p>I'll not spend too much time on this, but it feels wrong to not mention that
|
||||
there have been several iterations on how async should work in Rust.</p>
|
||||
<p><code>Futures 3.0</code> works with the relatively new <code>async/await</code> syntax in Rust and
|
||||
it's what we'll learn.</p>
|
||||
<p>Now, since this is rather recent, you can encounter creates that use <code>Futures 1.0</code>
|
||||
still. This will get resolved in time, but unfortunately it's not always easy
|
||||
to know in advance.</p>
|
||||
<p>A good sign is that if you're required to use combinators like <code>and_then</code> then
|
||||
you're using <code>Futures 1.0</code>.</p>
|
||||
<p>While they're not directly compatible, there is a tool that let's you relatively
|
||||
easily convert a <code>Future 1.0</code> to a <code>Future 3.0</code> and vice a versa. You can find
|
||||
all you need in the <a href="https://github.com/rust-lang/futures-rs"><code>futures-rs</code></a> crate and all
|
||||
<a href="https://rust-lang.github.io/futures-rs/blog/2019/04/18/compatibility-layer.html">information you need here</a>.</p>
|
||||
<h2><a class="header" href="#first-things-first" id="first-things-first">First things first</a></h2>
|
||||
<h2><a class="header" href="#bonus-section" id="bonus-section">Bonus section</a></h2>
|
||||
<p>If you find the concepts of concurrency and async programming confusing in
|
||||
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 Rusts
|
||||
@@ -243,7 +285,7 @@ it needs to be, so go on and read these chapters if you feel a bit unsure. </p>
|
||||
|
||||
|
||||
|
||||
<a rel="next" href="2_trait_objects.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next" href="2_waker_context.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
@@ -261,7 +303,7 @@ it needs to be, so go on and read these chapters if you feel a bit unsure. </p>
|
||||
|
||||
|
||||
|
||||
<a href="2_trait_objects.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a href="2_waker_context.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
@@ -270,6 +312,21 @@ it needs to be, so go on and read these chapters if you feel a bit unsure. </p>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Livereload script (if served using the cli tool) -->
|
||||
<script type="text/javascript">
|
||||
var socket = new WebSocket("ws://localhost:3001");
|
||||
socket.onmessage = function (event) {
|
||||
if (event.data === "reload") {
|
||||
socket.close();
|
||||
location.reload(true); // force reload from server (not from cache)
|
||||
}
|
||||
};
|
||||
|
||||
window.onbeforeunload = function() {
|
||||
socket.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<!-- Google Analytics Tag -->
|
||||
|
||||
Reference in New Issue
Block a user