finished all but the main example
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><a href="0_0_introduction.html" class="active"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="1_0_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html"><strong aria-hidden="true">2.3.</strong> Pin</a></li></ol></li><li><a href="2_0_future_example.html"><strong aria-hidden="true">3.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html"><strong aria-hidden="true">4.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
<ol class="chapter"><li><a href="0_0_introduction.html" class="active"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="1_0_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="1_1_trait_objects.html"><strong aria-hidden="true">3.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="1_4_reactor_executor.html"><strong aria-hidden="true">6.</strong> Reactor/Executor Pattern</a></li><li><a href="2_0_future_example.html"><strong aria-hidden="true">7.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html"><strong aria-hidden="true">8.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
@@ -149,73 +149,31 @@
|
||||
<p>This book aims to explain <code>Futures</code> in Rust using an example driven approach.</p>
|
||||
<p>The goal is to get a better understanding of <code>Futures</code> by implementing a toy
|
||||
<code>Reactor</code>, a very simple <code>Executor</code> and our own <code>Futures</code>. </p>
|
||||
<p>We'll start off solving a small problem without <code>Futures</code>, <code>Wakers</code> or async/await
|
||||
and then gradually adapt our example so it implements all these concepts, and
|
||||
can be solved using the executor provided by both <code>tokio</code> and <code>async_str</code>.</p>
|
||||
<p>In the end I've made some reader excercises you can do if you want to fix some
|
||||
of the most glaring ommissions and shortcuts we took and create a slightly better
|
||||
<p>We'll start off a bit differently than most other explanations. Instead of
|
||||
deferring some of the details about what's special about futures in Rust we
|
||||
try to tackle that head on first. We'll be as brief as possible, but as thorough
|
||||
as needed. This way, most question will be answered and explored up front. </p>
|
||||
<p>We'll end up with futures that can run an any executor like <code>tokio</code> and <code>async_str</code>.</p>
|
||||
<p>In the end I've made some reader exercises you can do if you want to fix some
|
||||
of the most glaring omissions and shortcuts we took and create a slightly better
|
||||
example yourself.</p>
|
||||
<h2><a class="header" href="#what-does-this-book-give-you-that-isnt-covered-elsewhere" id="what-does-this-book-give-you-that-isnt-covered-elsewhere">What does this book give you that isn't covered elsewhere?</a></h2>
|
||||
<p>That's a valid question. There are many good resources and examples already. First
|
||||
of all, this book will point you to some background information that I have found
|
||||
very valuable to get an understanding of concurrent programming in general.</p>
|
||||
<p>I find that many discussions arise, not because <code>Futures</code> is a hard concept to
|
||||
grasp, but that concurrent programming is a hard concept in general.</p>
|
||||
<p>Secondly, I've always found small runnable examples very exiting to learn from. It's
|
||||
of all, this book will focus on <code>Futures</code> and <code>async/await</code> specifically and
|
||||
not in the context of any specific runtime.</p>
|
||||
<p>Secondly, I've always found small runnable examples very exiting to learn from.
|
||||
Thanks to Mdbook the examples can even be edited and explored further. It's
|
||||
all code that you can download, play with and learn from.</p>
|
||||
<h2><a class="header" href="#what-well-do-and-not" id="what-well-do-and-not">What we'll do and not</a></h2>
|
||||
<p><strong>We'll:</strong></p>
|
||||
<ul>
|
||||
<li>Implement our own <code>Futures</code> and get to know the <code>Reactor/Executor</code> pattern</li>
|
||||
<li>Implement our own waker and learn why it's a bit foreign compared to other types</li>
|
||||
<li>Talk a bit about runtime complexity and what to keep in mind when writing async Rust.</li>
|
||||
<li>Make sure all examples can be run on the playground</li>
|
||||
<li>Not rely on any helpers or libraries, but try to face the complexity and learn</li>
|
||||
</ul>
|
||||
<p><strong>We'll not:</strong></p>
|
||||
<ul>
|
||||
<li>Talk about how futures are implemented in Rust the language, the state machine and so on</li>
|
||||
<li>Explain how the different runtimes differ, however, you'll hopefully be a bit
|
||||
better off if you read this before you go research them</li>
|
||||
<li>Explain concurrent programming, but I will supply sources</li>
|
||||
</ul>
|
||||
<p>I do want to explore Rusts internal implementation but that will be for a later
|
||||
book.</p>
|
||||
<p>We'll and end up with an understandable example including a <code>Future</code>
|
||||
implementation, an <code>Executor</code> and a <code>Reactor</code> in less than 200 lines of code.
|
||||
We don't rely on any dependencies or real I/O which means it's very easy to
|
||||
explore further and try your own ideas.</p>
|
||||
<h2><a class="header" href="#credits-and-thanks" id="credits-and-thanks">Credits and thanks</a></h2>
|
||||
<p>I'll like to take the chance of thanking the people behind <code>mio</code>, <code>tokio</code>,
|
||||
<code>async_std</code>, <code>Futures</code>, <code>libc</code>, <code>crossbeam</code> and many other libraries which so
|
||||
much is built upon. Reading and exploring some of this code is nothing less than
|
||||
impressive.</p>
|
||||
<h2><a class="header" href="#why-is-futures-in-rust-hard-to-understand" id="why-is-futures-in-rust-hard-to-understand">Why is <code>Futures</code> in Rust hard to understand</a></h2>
|
||||
<p>Well, I think it has to do with several things:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Futures has a very interesting implementation, compiling down to a state
|
||||
machine using generators to suspend and resume execution. In a language such as
|
||||
Rust this is pretty hard to do ergonomically and safely. You are exposed to some
|
||||
if this complexity when working with futures and want to understand them, not
|
||||
only learn how to use them.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Rust doesn't provide a runtime. That means you'll actually have to choose one
|
||||
yourself and actually know what a <code>Reactor</code> and an <code>Executor</code> is. While not
|
||||
too difficult, you need to make more choices than you need in GO and other
|
||||
languages designed with a concurrent programming in mind and ships with a
|
||||
runtime.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Futures exist in two versions, Futures 1.0 and Futures 3.0. Futures 1.0 was
|
||||
known to have some issues regarding ergonomics. Turns out that modelling
|
||||
async coding after <code>Promises</code> in JavaScript can turn in to extremely long errors
|
||||
and type signatures with a type system as Rust.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>Futures 3.0 are not compatible with Futures 1.0 without performing some work.</p>
|
||||
<ol start="4">
|
||||
<li>Async await syntax was recently stabilized</li>
|
||||
</ol>
|
||||
<p>what we'll
|
||||
really do is to stub out a <code>Reactor</code>, and <code>Executor</code> and implement</p>
|
||||
impressive. Even the RFCs that much of the design is built upon is written in a
|
||||
way that mortal people can understand, and that requires a lot of work. So thanks!</p>
|
||||
|
||||
</main>
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="1_0_background_information.html" class="active"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html"><strong aria-hidden="true">2.3.</strong> Pin</a></li></ol></li><li><a href="2_0_future_example.html"><strong aria-hidden="true">3.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html"><strong aria-hidden="true">4.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="1_0_background_information.html" class="active"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="1_1_trait_objects.html"><strong aria-hidden="true">3.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="1_4_reactor_executor.html"><strong aria-hidden="true">6.</strong> Reactor/Executor Pattern</a></li><li><a href="2_0_future_example.html"><strong aria-hidden="true">7.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html"><strong aria-hidden="true">8.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
@@ -146,9 +146,71 @@
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1><a class="header" href="#some-background-information" id="some-background-information">Some background information</a></h1>
|
||||
<blockquote>
|
||||
<p><strong>Relevant for:</strong></p>
|
||||
<ul>
|
||||
<li>High level introduction to concurrency in Rust</li>
|
||||
<li>Knowing what Rust provides and not when working with async</li>
|
||||
<li>Understanding why we need runtimes </li>
|
||||
<li>Knowing that Rust has <code>Futures 1.0</code> and <code>Futures 3.0</code>, and how to deal with them</li>
|
||||
<li>Getting pointers to further reading on concurrency in general</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<p>Before we start implementing our <code>Futures</code> , we'll go through some background
|
||||
information that will help demystify some of the concepts we encounter.</p>
|
||||
<h2><a class="header" href="#concurrency-in-general" id="concurrency-in-general">Concurrency in general</a></h2>
|
||||
<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 an extremely lightweight runtime.</p>
|
||||
<p>In languages like C#, JavaScript, Java and GO, the runtime is already there. So
|
||||
if you come from one of those languages this will seem a bit strange to you.</p>
|
||||
<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 extremely 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>
|
||||
</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 resumes 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 one creates leaf <code>Futures</code> and 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. Learning how to use one
|
||||
correctly can be hard as well, but both are excellent and it's just like
|
||||
learning any new library.</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 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 verca. 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>
|
||||
<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
|
||||
@@ -159,7 +221,11 @@ try to give a high level overview that will make it easier to learn Rusts
|
||||
<li><a href="https://cfsamson.github.io/book-exploring-async-basics/5_strategies_for_handling_io.html">Async Basics - Strategies for handling I/O</a></li>
|
||||
<li><a href="https://cfsamson.github.io/book-exploring-async-basics/6_epoll_kqueue_iocp.html">Async Basics - Epoll, Kqueue and IOCP</a></li>
|
||||
</ul>
|
||||
<p>r</p>
|
||||
<p>Now learning these concepts by studying futures is making it much harder than
|
||||
it needs to be, so go on and read these chapters. I'll be right here when
|
||||
you're back. </p>
|
||||
<p>However, if you feel that you have the basics covered, then go right on. </p>
|
||||
<p>Let's get moving!</p>
|
||||
|
||||
</main>
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="1_0_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="1_1_trait_objects.html" class="active"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html"><strong aria-hidden="true">2.3.</strong> Pin</a></li></ol></li><li><a href="2_0_future_example.html"><strong aria-hidden="true">3.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html"><strong aria-hidden="true">4.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="1_0_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="1_1_trait_objects.html" class="active"><strong aria-hidden="true">3.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="1_4_reactor_executor.html"><strong aria-hidden="true">6.</strong> Reactor/Executor Pattern</a></li><li><a href="2_0_future_example.html"><strong aria-hidden="true">7.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html"><strong aria-hidden="true">8.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
@@ -146,62 +146,73 @@
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1><a class="header" href="#trait-objects-and-fat-pointers" id="trait-objects-and-fat-pointers">Trait objects and fat pointers</a></h1>
|
||||
<blockquote>
|
||||
<p><strong>Relevant for:</strong></p>
|
||||
<ul>
|
||||
<li>Understanding how the Waker object is constructed</li>
|
||||
<li>Getting a basic feel for "type erased" objects and what they are</li>
|
||||
<li>Learning the basics of dynamic dispatch</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<h2><a class="header" href="#trait-objects-and-dynamic-dispatch" id="trait-objects-and-dynamic-dispatch">Trait objects and dynamic dispatch</a></h2>
|
||||
<p>The single most confusing topic we encounter when implementing our own <code>Futures</code>
|
||||
<p>One of the most confusing topic we encounter when implementing our own <code>Futures</code>
|
||||
is how we implement a <code>Waker</code> . Creating a <code>Waker</code> involves creating a <code>vtable</code>
|
||||
which allows using dynamic dispatch to call methods on a <em>type erased</em> trait
|
||||
which allows us to use dynamic dispatch to call methods on a <em>type erased</em> trait
|
||||
object we construct our selves.</p>
|
||||
<p>If you want to know more about dynamic dispatch in Rust I can recommend this article:</p>
|
||||
<p>https://alschwalm.com/blog/static/2017/03/07/exploring-dynamic-dispatch-in-rust/</p>
|
||||
<blockquote>
|
||||
<p>If you want to know more about dynamic dispatch in Rust I can recommend an article written by Adam Schwalm called <a href="https://alschwalm.com/blog/static/2017/03/07/exploring-dynamic-dispatch-in-rust/">Exploring Dynamic Dispatch in Rust</a>.</p>
|
||||
</blockquote>
|
||||
<p>Let's explain this a bit more in detail.</p>
|
||||
<h2><a class="header" href="#fat-pointers-in-rust" id="fat-pointers-in-rust">Fat pointers in Rust</a></h2>
|
||||
<p>Let's take a look at the size of some different pointer types in Rust. If we
|
||||
run the following code:</p>
|
||||
run the following code. <em>(You'll have to press "play" to see the output)</em>:</p>
|
||||
<pre><pre class="playpen"><code class="language-rust"># use std::mem::size_of;
|
||||
trait SomeTrait { }
|
||||
|
||||
fn main() {
|
||||
println!("Size of Box<i32>: {}", size_of::<Box<i32>>());
|
||||
println!("Size of &i32: {}", size_of::<&i32>());
|
||||
println!("Size of &Box<i32>: {}", size_of::<&Box<i32>>());
|
||||
println!("Size of Box<Trait>: {}", size_of::<Box<SomeTrait>>());
|
||||
println!("Size of &dyn Trait: {}", size_of::<&dyn SomeTrait>());
|
||||
println!("Size of &[i32]: {}", size_of::<&[i32]>());
|
||||
println!("Size of &[&dyn Trait]: {}", size_of::<&[&dyn SomeTrait]>());
|
||||
println!("Size of [i32; 10]: {}", size_of::<[i32; 10]>());
|
||||
println!("Size of [&dyn Trait; 10]: {}", size_of::<[&dyn SomeTrait; 10]>());
|
||||
println!("======== The size of different pointers in Rust: ========");
|
||||
println!("&dyn Trait:-----{}", size_of::<&dyn SomeTrait>());
|
||||
println!("&[&dyn Trait]:--{}", size_of::<&[&dyn SomeTrait]>());
|
||||
println!("Box<Trait>:-----{}", size_of::<Box<SomeTrait>>());
|
||||
println!("&i32:-----------{}", size_of::<&i32>());
|
||||
println!("&[i32]:---------{}", size_of::<&[i32]>());
|
||||
println!("Box<i32>:-------{}", size_of::<Box<i32>>());
|
||||
println!("&Box<i32>:------{}", size_of::<&Box<i32>>());
|
||||
println!("[&dyn Trait;4]:-{}", size_of::<[&dyn SomeTrait; 4]>());
|
||||
println!("[i32;4]:--------{}", size_of::<[i32; 4]>());
|
||||
}
|
||||
</code></pre></pre>
|
||||
<p>As you see from the output after running this, the sizes of the references varies.
|
||||
Most are 8 bytes (which is a pointer size on 64 bit systems), but some are 16
|
||||
Many are 8 bytes (which is a pointer size on 64 bit systems), but some are 16
|
||||
bytes.</p>
|
||||
<p>The 16 byte sized pointers are called "fat pointers" since they carry more extra
|
||||
information.</p>
|
||||
<p><strong>In the case of <code>&[i32]</code> :</strong> </p>
|
||||
<ul>
|
||||
<li>The first 8 bytes is the actual pointer to the first element in the array</li>
|
||||
</ul>
|
||||
<p>(or part of an array the slice refers to)</p>
|
||||
<p><strong>Example <code>&[i32]</code> :</strong> </p>
|
||||
<ul>
|
||||
<li>The first 8 bytes is the actual pointer to the first element in the array (or part of an array the slice refers to)</li>
|
||||
<li>The second 8 bytes is the length of the slice.</li>
|
||||
</ul>
|
||||
<p>The one we'll concern ourselves about is the references to traits, or
|
||||
<em>trait objects</em> as they're called in Rust.</p>
|
||||
<p><code>&dyn SomeTrait</code> is an example of a <em>trait object</em> </p>
|
||||
<p><strong>Example <code>&dyn SomeTrait</code>:</strong></p>
|
||||
<p>This is the type of fat pointer we'll concern ourselves about going forward.
|
||||
<code>&dyn SomeTrait</code> is a reference to a trait, or what Rust calls <em>trait objects</em>.</p>
|
||||
<p>The layout for a pointer to a <em>trait object</em> looks like this: </p>
|
||||
<ul>
|
||||
<li>The first 8 bytes points to the <code>data</code> for the trait object</li>
|
||||
<li>The second 8 bytes points to the <code>vtable</code> for the trait object</li>
|
||||
</ul>
|
||||
<p>The reason for this is to allow us to refer to an object we know nothing about
|
||||
except that it implements the methods defined by our trait. To allow this we use
|
||||
dynamic dispatch.</p>
|
||||
except that it implements the methods defined by our trait. To allow accomplish this we use <em>dynamic dispatch</em>.</p>
|
||||
<p>Let's explain this in code instead of words by implementing our own trait
|
||||
object from these parts:</p>
|
||||
<pre><pre class="playpen"><code class="language-rust">// A reference to a trait object is a fat pointer: (data_ptr, vtable_ptr)
|
||||
<blockquote>
|
||||
<p>This is an example of <em>editable</em> code. You can change everything in the example
|
||||
and try to run it. If you want to go back, press the undo symbol. Keep an eye
|
||||
out for these as we go forward. Many examples will be editable.</p>
|
||||
</blockquote>
|
||||
<pre><pre class="playpen"><code class="language-rust editable">// A reference to a trait object is a fat pointer: (data_ptr, vtable_ptr)
|
||||
trait Test {
|
||||
fn add(&self) -> i32;
|
||||
fn sub(&self) -> i32;
|
||||
fn add(&self) -> i32;
|
||||
fn sub(&self) -> i32;
|
||||
fn mul(&self) -> i32;
|
||||
}
|
||||
|
||||
@@ -241,10 +252,10 @@ fn main() {
|
||||
0, // pointer to `Drop` (which we're not implementing here)
|
||||
6, // lenght of vtable
|
||||
8, // alignment
|
||||
|
||||
// we need to make sure we add these in the same order as defined in the Trait.
|
||||
// Try changing the order of add and sub and see what happens.
|
||||
add as usize, // function pointer
|
||||
sub as usize, // function pointer
|
||||
add as usize, // function pointer - try changing the order of `add`
|
||||
sub as usize, // function pointer - and `sub` to see what happens
|
||||
mul as usize, // function pointer
|
||||
];
|
||||
|
||||
@@ -258,59 +269,9 @@ fn main() {
|
||||
}
|
||||
|
||||
</code></pre></pre>
|
||||
<p>If you run this code by pressing the "play" button at the top you'll se it
|
||||
outputs just what we expect.</p>
|
||||
<p>This code example is editable so you can change it
|
||||
and run it to see what happens.</p>
|
||||
<p>The reason we go through this will be clear later on when we implement our own
|
||||
<code>Waker</code> we'll actually set up a <code>vtable</code> like we do here to and knowing what
|
||||
it is will make this much less mysterious.</p>
|
||||
<h2><a class="header" href="#reactorexecutor-pattern" id="reactorexecutor-pattern">Reactor/Executor pattern</a></h2>
|
||||
<p>If you don't know what this is, you should take a few minutes and read about
|
||||
it. You will encounter the term <code>Reactor</code> and <code>Executor</code> a lot when working
|
||||
with async code in Rust.</p>
|
||||
<p>I have written a quick introduction explaining this pattern before which you
|
||||
can take a look at here:</p>
|
||||
<p><a href="https://cfsamsonbooks.gitbook.io/epoll-kqueue-iocp-explained/appendix-1/reactor-executor-pattern"><img src="./assets/reactorexecutor.png" alt="homepage" /></a></p>
|
||||
<div style="text-align:center">
|
||||
<a href="https://cfsamsonbooks.gitbook.io/epoll-kqueue-iocp-explained/appendix-1/reactor-executor-pattern">Epoll, Kqueue and IOCP Explained - The Reactor-Executor Pattern</a>
|
||||
</div>
|
||||
<p>I'll re-iterate the most important parts here.</p>
|
||||
<p>This pattern consists of at least 2 parts:</p>
|
||||
<ol>
|
||||
<li>A reactor
|
||||
<ul>
|
||||
<li>handles some kind of event queue</li>
|
||||
<li>has the responsibility of respoonding to events</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>An executor
|
||||
<ul>
|
||||
<li>Often has a scheduler</li>
|
||||
<li>Holds a set of suspended tasks, and has the responsibility of resuming
|
||||
them when an event has occurred</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>The concept of a task
|
||||
<ul>
|
||||
<li>A set of operations that can be stopped half way and resumed later on</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
<p>This is a pattern not only used in Rust, but it's very popular in Rust due to
|
||||
how well it separates concerns between handling and scheduling tasks, and queing
|
||||
and responding to I/O events.</p>
|
||||
<p>The only thing Rust as a language defines is the <em>task</em>. In Rust we call an
|
||||
incorruptible task a <code>Future</code>. Futures has a well defined interface, which means
|
||||
they can be used across the entire ecosystem.</p>
|
||||
<p>In addition, Rust provides a way for the Reactor and Executor to communicate
|
||||
through the <code>Waker</code>. We'll get to know these in the following chapters.</p>
|
||||
<p>Providing these pieces let's Rust take care a lot of the ergonomic "friction"
|
||||
programmers meet when faced with async code, and still not dictate any
|
||||
preferred runtime to actually do the scheduling and I/O queues.</p>
|
||||
<p>It's important to know that Rust doesn't provide a runtime, so you have to choose
|
||||
one. <a href="https://github.com/async-rs/async-std">async std</a> and <a href="https://github.com/tokio-rs/tokio">tokio</a> are two popular ones.</p>
|
||||
<p>With that out of the way, let's move on to our main example.</p>
|
||||
|
||||
</main>
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="1_0_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html" class="active"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html"><strong aria-hidden="true">2.3.</strong> Pin</a></li></ol></li><li><a href="2_0_future_example.html"><strong aria-hidden="true">3.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html"><strong aria-hidden="true">4.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="1_0_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="1_1_trait_objects.html"><strong aria-hidden="true">3.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html" class="active"><strong aria-hidden="true">4.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="1_4_reactor_executor.html"><strong aria-hidden="true">6.</strong> Reactor/Executor Pattern</a></li><li><a href="2_0_future_example.html"><strong aria-hidden="true">7.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html"><strong aria-hidden="true">8.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
@@ -145,10 +145,7 @@
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1><a class="header" href="#generators-and-pin" id="generators-and-pin">Generators and Pin</a></h1>
|
||||
<p>So the second difficult part that there seems to be a lot of questions about
|
||||
is Generators and the <code>Pin</code> type.</p>
|
||||
<h2><a class="header" href="#generators" id="generators">Generators</a></h2>
|
||||
<h1><a class="header" href="#generators" id="generators">Generators</a></h1>
|
||||
<blockquote>
|
||||
<p><strong>Relevant for:</strong></p>
|
||||
<ul>
|
||||
@@ -160,6 +157,11 @@ is Generators and the <code>Pin</code> type.</p>
|
||||
well written and I can recommend reading through it (it talks as much about
|
||||
async/await as it does about generators).</p>
|
||||
</blockquote>
|
||||
<p>The second difficult part that there seems to be a lot of questions about
|
||||
is Generators and the <code>Pin</code> type. Since they're related we'll start off by
|
||||
exploring generators first. By doing that we'll soon get to see why
|
||||
we need to be able to "pin" some data to a fixed location in memory and
|
||||
get an introduction to <code>Pin</code> as well.</p>
|
||||
<p>Basically, there were three main options that were discussed when Rust was
|
||||
desiging how the language would handle concurrency:</p>
|
||||
<ol>
|
||||
@@ -171,8 +173,8 @@ desiging how the language would handle concurrency:</p>
|
||||
<p>I've written about green threads before. Go check out
|
||||
<a href="https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/">Green Threads Explained in 200 lines of Rust</a> if you're interested.</p>
|
||||
<p>Green threads uses the same mechanisms as an OS does by creating a thread for
|
||||
each task, setting up a stack and forcing the CPU to save it's state and jump
|
||||
from one task(thread) to another. We yield control to the scheduler which then
|
||||
each task, setting up a stack, save the CPU's state and jump
|
||||
from one task(thread) to another by doing a "context switch". We yield control to the scheduler which then
|
||||
continues running a different task.</p>
|
||||
<p>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
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="1_0_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html" class="active"><strong aria-hidden="true">2.3.</strong> Pin</a></li></ol></li><li><a href="2_0_future_example.html"><strong aria-hidden="true">3.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html"><strong aria-hidden="true">4.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="1_0_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="1_1_trait_objects.html"><strong aria-hidden="true">3.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html" class="active"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="1_4_reactor_executor.html"><strong aria-hidden="true">6.</strong> Reactor/Executor Pattern</a></li><li><a href="2_0_future_example.html"><strong aria-hidden="true">7.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html"><strong aria-hidden="true">8.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
@@ -145,7 +145,7 @@
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h2><a class="header" href="#pin" id="pin">Pin</a></h2>
|
||||
<h1><a class="header" href="#pin" id="pin">Pin</a></h1>
|
||||
<blockquote>
|
||||
<p><strong>Relevant for</strong></p>
|
||||
<ol>
|
||||
@@ -156,23 +156,60 @@
|
||||
</ol>
|
||||
<p><code>Pin</code> was suggested in <a href="https://github.com/rust-lang/rfcs/blob/master/text/2349-pin.md">RFC#2349</a></p>
|
||||
</blockquote>
|
||||
<p>Ping consists of the <code>Pin</code> type and the <code>Unpin</code> marker. Let's start off with some general rules:</p>
|
||||
<p>We already got a brief introduction of <code>Pin</code> in the previous chapters, so we'll
|
||||
start off here with some definitions and a set of rules to remember.</p>
|
||||
<h2><a class="header" href="#definitions" id="definitions">Definitions</a></h2>
|
||||
<p>Pin consists of the <code>Pin</code> type and the <code>Unpin</code> marker. Pin's purpose in life is
|
||||
to govern the rules that need to apply for types which implement <code>!Unpin</code>.</p>
|
||||
<p>Pin is only relevant for pointers. A reference to an object is a pointer.</p>
|
||||
<p>Yep, that's double negation for you, as in "does-not-implement-unpin". For this
|
||||
chapter and only this chapter we'll rename these markers to:</p>
|
||||
<blockquote>
|
||||
<p><code>!Unpin</code> = <code>MustStay</code> and <code>Unpin</code> = <code>CanMove</code></p>
|
||||
</blockquote>
|
||||
<p>It just makes it so much easier to understand them.</p>
|
||||
<h2><a class="header" href="#rules-to-remember" id="rules-to-remember">Rules to remember</a></h2>
|
||||
<ol>
|
||||
<li>Pin does nothing special, it only prevents the user of an API to violate some assumtions you make when writing your (most likely) unsafe code.</li>
|
||||
<li>Most standard library types implement <code>Unpin</code></li>
|
||||
<li><code>Unpin</code> means it's OK for this type to be moved even when pinned.</li>
|
||||
<li>If you <code>Box</code> a value, that boxed value automatcally implements <code>Unpin</code>.</li>
|
||||
<li>The main use case for <code>Pin</code> is to allow self referential types</li>
|
||||
<li>The implementation behind objects that doens't implement <code>Unpin</code> is most likely unsafe
|
||||
<ol>
|
||||
<li><code>Pin</code> prevents users from your code to break the assumtions you make when writing the <code>unsafe</code> implementation</li>
|
||||
<li>It doesn't solve the fact that you'll have to write unsafe code to actually implement it</li>
|
||||
</ol>
|
||||
<li>
|
||||
<p>If <code>T: CanMove</code> (which is the default), then <code>Pin<'a, T></code> is entirely equivalent to <code>&'a mut T</code>. in other words: <code>CanMove</code> means it's OK for this type to be moved even when pinned, so <code>Pin</code> will have no effect on such a type.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Getting a <code>&mut T</code> to a pinned pointer requires unsafe if <code>T: MustStay</code>. In other words: requiring a pinned pointer to a type which is <code>MustStay</code> prevents the <em>user</em> of that API from moving that value unless it choses to write <code>unsafe</code> code.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Pinning does nothing special with that memory like putting it into some "read only" memory or anything fancy. It only tells the compiler that some operations on this value should be forbidden. </p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Most standard library types implement <code>CanMove</code>. The same goes for most
|
||||
"normal" types you encounter in Rust. <code>Futures</code> and <code>Generators</code> are two
|
||||
exceptions.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>The main use case for <code>Pin</code> is to allow self referential types, the whole
|
||||
justification for stabilizing them was to allow that. There are still corner
|
||||
cases in the API which are being explored.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>The implementation behind objects that are <code>MustStay</code> is most likely unsafe.
|
||||
Moving such a type can cause the universe to crash. As of the time of writing
|
||||
this book, creating an reading fields of a self referential struct still requires <code>unsafe</code>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>You're not really meant to be implementing <code>MustStay</code>, but you can on nightly with a feature flag, or by adding <code>std::marker::PhantomPinned</code> to your type.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>When Pinning, you can either pin a value to memory either on the stack or
|
||||
on the heap.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Pinning a <code>MustStay</code> pointer to the stack requires <code>unsafe</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Pinning a <code>MustStay</code> pointer to the heap does not require <code>unsafe</code>. There is a shortcut for doing this using <code>Box::pin</code>.</p>
|
||||
</li>
|
||||
<li>You're not really meant to be implementing <code>!Unpin</code>, but you can on nightly with a feature flag</li>
|
||||
</ol>
|
||||
<blockquote>
|
||||
<p>Unsafe code does not mean it's litterally "unsafe", it only relieves the
|
||||
<p>Unsafe code does not mean it's literally "unsafe", it only relieves the
|
||||
guarantees you normally get from the compiler. An <code>unsafe</code> implementation can
|
||||
be perfectly safe to do, but you have no safety net.</p>
|
||||
</blockquote>
|
||||
@@ -220,11 +257,30 @@ impl Test {
|
||||
}
|
||||
}
|
||||
</code></pre></pre>
|
||||
<p>As you can see this results in unwanted behavior. The pointer to <code>b</code> stays the
|
||||
same and points to the old value. It's easy to get this to segfault, and fail
|
||||
in other spectacular ways as well.</p>
|
||||
<p>Pin essentially prevents the <strong>user</strong> of your unsafe code
|
||||
(even if that means yourself) move the value after it's pinned.</p>
|
||||
<p>Let's walk through this example since we'll be using it the rest of this chapter.</p>
|
||||
<p>We have a self-referential struct <code>Test</code>. <code>Test</code> needs an <code>init</code> method to be
|
||||
created which is strange but we'll need that to keep this example as short as
|
||||
possible.</p>
|
||||
<p><code>Test</code> provides two methods to get a reference to the value of the fields
|
||||
<code>a</code> and <code>b</code>. Since <code>b</code> is a reference to <code>a</code> we store it as a pointer since
|
||||
the borrowing rules of Rust doesn't allow us to define this lifetime.</p>
|
||||
<p>In our main method we first instantiate two instances of <code>Test</code> and print out
|
||||
the value of the fields on <code>test1</code>. We get:</p>
|
||||
<pre><code class="language-rust ignore">a: test1, b: test1
|
||||
</code></pre>
|
||||
<p>Next we swap the data stored at the memory location which <code>test1</code> is pointing to
|
||||
with the data stored at the memory location <code>test2</code> is pointing to and vice a verca.</p>
|
||||
<p>We should expect that printing the fields of <code>test2</code> should display the same as
|
||||
<code>test1</code> (since the object we printed before the swap has moved there now).</p>
|
||||
<pre><code class="language-rust ignore">a: test1, b: test2
|
||||
</code></pre>
|
||||
<p>The pointer to <code>b</code> still points to the old location. That location is now
|
||||
occupied with the string "test2". This can be a bit hard to visualize so I made
|
||||
a figure that i hope can help.</p>
|
||||
<p><strong>Fig 1: Before and after swap</strong>
|
||||
<img src="../assets/swap_problem.jpg" alt="swap_problem" /></p>
|
||||
<p>As you can see this results in unwanted behavior. It's easy to get this to
|
||||
segfault, show UB and fail in other spectacular ways as well.</p>
|
||||
<p>If we change the example to using <code>Pin</code> instead:</p>
|
||||
<pre><pre class="playpen"><code class="language-rust editable">use std::pin::Pin;
|
||||
use std::marker::PhantomPinned;
|
||||
@@ -286,13 +342,14 @@ impl Test {
|
||||
|
||||
</code></pre></pre>
|
||||
<p>Now, what we've done here is pinning a stack address. That will always be
|
||||
<code>unsafe</code> if our type implements <code>!Unpin</code>, in other words. That our type is not
|
||||
<code>Unpin</code> which is the norm.</p>
|
||||
<code>unsafe</code> if our type implements <code>!Unpin</code> (aka <code>MustStay</code>). </p>
|
||||
<p>We use some tricks here, including requiring an <code>init</code>. If we want to fix that
|
||||
and let users avoid <code>unsafe</code> we need to place our data on the heap.</p>
|
||||
and let users avoid <code>unsafe</code> we need to pin our data on the heap instead.</p>
|
||||
<blockquote>
|
||||
<p>Stack pinning will always depend on the current stack frame we're in, so we
|
||||
can't create a self referential object in one stack frame and return it since
|
||||
any pointers we take to "self" is invalidated.</p>
|
||||
</blockquote>
|
||||
<p>The next example solves some of our friction at the cost of a heap allocation.</p>
|
||||
<pre><pre class="playpen"><code class="language-rust editbable">use std::pin::Pin;
|
||||
use std::marker::PhantomPinned;
|
||||
@@ -339,19 +396,10 @@ impl Test {
|
||||
}
|
||||
}
|
||||
</code></pre></pre>
|
||||
<p>Seeing this we're ready to sum up with a few more points to remember about
|
||||
pinning:</p>
|
||||
<ol>
|
||||
<li>Pinning only makes sense to do for types that are <code>!Unpin</code></li>
|
||||
<li>Pinning a <code>!Unpin</code> pointer to the stack will requires <code>unsafe</code></li>
|
||||
<li>Pinning a boxed value will not require <code>unsafe</code>, even if the type is <code>!Unpin</code></li>
|
||||
<li>If T: Unpin (which is the default), then Pin<'a, T> is entirely equivalent to &'a mut T.</li>
|
||||
<li>Getting a <code>&mut T</code> to a pinned pointer requires unsafe if <code>T: !Unpin</code></li>
|
||||
<li>Pinning is really only useful when implementing self-referential types.<br />
|
||||
For all intents and purposes you can think of <code>!Unpin</code> = self-referential-type</li>
|
||||
</ol>
|
||||
<p>The fact that boxing (heap allocating) a value that implements <code>!Unpin</code> is safe
|
||||
makes sense. Once the data is allocated on the heap it will have a stable address. </p>
|
||||
makes sense. Once the data is allocated on the heap it will have a stable address.</p>
|
||||
<p>There is no need for us as users of the API to take special care and ensure
|
||||
that the self-referential pointer stays valid.</p>
|
||||
<p>There are ways to safely give some guarantees on stack pinning as well, but right
|
||||
now you need to use a crate like <a href="https://github.com/rust-lang/rfcs/blob/master/text/2349-pin.md">pin_utils</a>:<a href="https://github.com/rust-lang/rfcs/blob/master/text/2349-pin.md">pin_utils</a> to do that.</p>
|
||||
<h3><a class="header" href="#projectionstructural-pinning" id="projectionstructural-pinning">Projection/structural pinning</a></h3>
|
||||
@@ -378,7 +426,7 @@ we're soon finished.</p>
|
||||
|
||||
|
||||
|
||||
<a rel="next" href="2_0_future_example.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next" href="1_4_reactor_executor.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
@@ -396,7 +444,7 @@ we're soon finished.</p>
|
||||
|
||||
|
||||
|
||||
<a href="2_0_future_example.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a href="1_4_reactor_executor.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
|
||||
304
book/1_4_reactor_executor.html
Normal file
304
book/1_4_reactor_executor.html
Normal file
@@ -0,0 +1,304 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="sidebar-visible no-js">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Reactor/Executor Pattern - Futures Explained in 200 Lines of Rust</title>
|
||||
|
||||
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<link rel="shortcut icon" href="favicon.png">
|
||||
<link rel="stylesheet" href="css/variables.css">
|
||||
<link rel="stylesheet" href="css/general.css">
|
||||
<link rel="stylesheet" href="css/chrome.css">
|
||||
<link rel="stylesheet" href="css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="highlight.css">
|
||||
<link rel="stylesheet" href="tomorrow-night.css">
|
||||
<link rel="stylesheet" href="ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
<body class="light">
|
||||
<!-- Provide site root to javascript -->
|
||||
<script type="text/javascript">
|
||||
var path_to_root = "";
|
||||
var default_theme = "light";
|
||||
</script>
|
||||
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script type="text/javascript">
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script type="text/javascript">
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
document.body.className = theme;
|
||||
document.querySelector('html').className = theme + ' js';
|
||||
</script>
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script type="text/javascript">
|
||||
var html = document.querySelector('html');
|
||||
var sidebar = 'hidden';
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
}
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="1_0_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="1_1_trait_objects.html"><strong aria-hidden="true">3.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="1_4_reactor_executor.html" class="active"><strong aria-hidden="true">6.</strong> Reactor/Executor Pattern</a></li><li><a href="2_0_future_example.html"><strong aria-hidden="true">7.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html"><strong aria-hidden="true">8.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
|
||||
<div id="menu-bar" class="menu-bar">
|
||||
<div id="menu-bar-sticky-container">
|
||||
<div class="left-buttons">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Futures Explained in 200 Lines of Rust</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script type="text/javascript">
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1><a class="header" href="#reactorexecutor-pattern" id="reactorexecutor-pattern">Reactor/Executor Pattern</a></h1>
|
||||
<blockquote>
|
||||
<p><strong>Relevant for:</strong></p>
|
||||
<ul>
|
||||
<li>Getting a high level overview of a common runtime model in Rust</li>
|
||||
<li>Introducing these terms so we're on the same page when referring to them</li>
|
||||
<li>Getting pointers on where to get more information about this pattern</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<p>If you don't know what this is, you should take a few minutes and read about
|
||||
it. You will encounter the term <code>Reactor</code> and <code>Executor</code> a lot when working
|
||||
with async code in Rust.</p>
|
||||
<p>I have written a quick introduction explaining this pattern before which you
|
||||
can take a look at here:</p>
|
||||
<p><a href="https://cfsamsonbooks.gitbook.io/epoll-kqueue-iocp-explained/appendix-1/reactor-executor-pattern"><img src="./assets/reactorexecutor.png" alt="homepage" /></a></p>
|
||||
<div style="text-align:center">
|
||||
<a href="https://cfsamsonbooks.gitbook.io/epoll-kqueue-iocp-explained/appendix-1/reactor-executor-pattern">Epoll, Kqueue and IOCP Explained - The Reactor-Executor Pattern</a>
|
||||
</div>
|
||||
<p>I'll re-iterate the most important parts here.</p>
|
||||
<p><strong>This pattern consists of at least 2 parts:</strong></p>
|
||||
<ol>
|
||||
<li><strong>A reactor</strong>
|
||||
<ul>
|
||||
<li>handles some kind of event queue</li>
|
||||
<li>has the responsibility of respoonding to events</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>An executor</strong>
|
||||
<ul>
|
||||
<li>Often has a scheduler</li>
|
||||
<li>Holds a set of suspended tasks, and has the responsibility of resuming
|
||||
them when an event has occurred</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>The concept of a task</strong>
|
||||
<ul>
|
||||
<li>A set of operations that can be stopped half way and resumed later on</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
<p>This kind of pattern common outside of Rust as well, but it's especially popular in Rust due to how well it alignes with the API provided by Rusts standard library. This model separates concerns between handling and scheduling tasks, and queing and responding to I/O events.</p>
|
||||
<h2><a class="header" href="#the-reactor" id="the-reactor">The Reactor</a></h2>
|
||||
<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>
|
||||
<p>We can call this kind of <code>Future</code> a "leaf Future`, since it's some operation
|
||||
we'll actually wait on and that we can chain operations on which are performed
|
||||
once the leaf future is ready. </p>
|
||||
<h2><a class="header" href="#the-task" id="the-task">The Task</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 operations can spawn new leaf futures themselves.</p>
|
||||
<h2><a class="header" href="#the-executor" id="the-executor">The executor</a></h2>
|
||||
<p>The executors task is to take one or more futures and run them to completion.</p>
|
||||
<p>The first thing an <code>executor</code> does when it get's a <code>Future</code> is polling it.</p>
|
||||
<p><strong>When polled one of three things can happen:</strong></p>
|
||||
<ul>
|
||||
<li>The future returns <code>Ready</code> and we schedule whatever chained operations to run</li>
|
||||
<li>The future hasn't been polled before so we pass it a <code>Waker</code> and suspend it</li>
|
||||
<li>The futures has been polled before but is not ready and returns <code>Pending</code></li>
|
||||
</ul>
|
||||
<p>Rust provides a way for the Reactor and Executor to communicate through the <code>Waker</code>. The reactor stores this <code>Waker</code> and calls <code>Waker::wake()</code> on it once
|
||||
a <code>Future</code> has resolved and should be polled again.</p>
|
||||
<p>We'll get to know these concepts better in the following chapters.</p>
|
||||
<p>Providing these pieces let's Rust take care a lot of the ergonomic "friction"
|
||||
programmers meet when faced with async code, and still not dictate any
|
||||
preferred runtime to actually do the scheduling and I/O queues.</p>
|
||||
<p>With that out of the way, let's move on to actually implement all this in our
|
||||
example.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
|
||||
<a rel="prev" href="1_3_pin.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<a rel="next" href="2_0_future_example.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
|
||||
<a href="1_3_pin.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<a href="2_0_future_example.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
</nav>
|
||||
|
||||
</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>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="ace.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="editor.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="mode-rust.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="theme-dawn.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
|
||||
|
||||
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
|
||||
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
|
||||
<script src="book.js" type="text/javascript" charset="utf-8"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -78,7 +78,7 @@
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="1_0_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html"><strong aria-hidden="true">2.3.</strong> Pin</a></li></ol></li><li><a href="2_0_future_example.html" class="active"><strong aria-hidden="true">3.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html"><strong aria-hidden="true">4.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="1_0_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="1_1_trait_objects.html"><strong aria-hidden="true">3.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="1_4_reactor_executor.html"><strong aria-hidden="true">6.</strong> Reactor/Executor Pattern</a></li><li><a href="2_0_future_example.html" class="active"><strong aria-hidden="true">7.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html"><strong aria-hidden="true">8.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
@@ -145,7 +145,323 @@
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<pre><pre class="playpen"><code class="language-rust">
|
||||
<h1><a class="header" href="#futures-in-rust" id="futures-in-rust">Futures in Rust</a></h1>
|
||||
<p>We'll create our own <code>Futures</code> together with a fake reactor and a simple
|
||||
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 clone the repository and play around with the code yourself. There
|
||||
are two branches. The <code>basic_example</code> is this code, and the <code>basic_example_commented</code>
|
||||
is this example with extensive comments.</p>
|
||||
<h2><a class="header" href="#implementing-our-own-futures" id="implementing-our-own-futures">Implementing our own Futures</a></h2>
|
||||
<p>Let's start with why we wrote this book, by implementing our own <code>Futures</code>.</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},
|
||||
thread::{self, JoinHandle}, time::{Duration, Instant}
|
||||
};
|
||||
|
||||
fn main() {
|
||||
// This is just to make it easier for us to see when our Future was resolved
|
||||
let start = Instant::now();
|
||||
|
||||
// Many runtimes create a glocal `reactor` we pass it as an argument
|
||||
let reactor = Reactor::new();
|
||||
// Since we'll share this between threads we wrap it in a
|
||||
// atmically-refcounted- mutex.
|
||||
let reactor = Arc::new(Mutex::new(reactor));
|
||||
|
||||
// We create two tasks:
|
||||
// - first parameter is the `reactor`
|
||||
// - the second is a timeout in seconds
|
||||
// - the third is an `id` to identify the task
|
||||
let future1 = Task::new(reactor.clone(), 2, 1);
|
||||
let future2 = Task::new(reactor.clone(), 1, 2);
|
||||
|
||||
// an `async` block works the same way as an `async fn` in that it compiles
|
||||
// our code into a state machine, `yielding` at every `await` point.
|
||||
let fut1 = async {
|
||||
let val = future1.await;
|
||||
let dur = (Instant::now() - start).as_secs_f32();
|
||||
println!("Future got {} at time: {:.2}.", val, dur);
|
||||
};
|
||||
|
||||
let fut2 = async {
|
||||
let val = future2.await;
|
||||
let dur = (Instant::now() - start).as_secs_f32();
|
||||
println!("Future got {} at time: {:.2}.", val, dur);
|
||||
};
|
||||
|
||||
// Our executor can only run one and one future, this is pretty normal
|
||||
// though. You have a set of operations containing many futures that
|
||||
// ends up as a single future that drives them all to completion.
|
||||
let mainfut = async {
|
||||
fut1.await;
|
||||
fut2.await;
|
||||
};
|
||||
|
||||
// This executor will block the main thread until the futures is resolved
|
||||
block_on(mainfut);
|
||||
// When we're done, we want to shut down our reactor thread so our program
|
||||
// ends nicely.
|
||||
reactor.lock().map(|mut r| r.close()).unwrap();
|
||||
}
|
||||
|
||||
//// ============================ EXECUTOR ====================================
|
||||
|
||||
// Our executor takes any object which implements the `Future` trait
|
||||
fn block_on<F: Future>(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));
|
||||
// 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.
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
|
||||
// We poll in a loop, but it's not a busy loop. It will only run when
|
||||
// an event occurs, or a thread has a "spurious wakeup" (an unexpected wakeup
|
||||
// that can happen for no good reason).
|
||||
let val = loop {
|
||||
// So, since we run this on one thread and run one future to completion
|
||||
// we can pin the `Future` to the stack. This is unsafe, but saves an
|
||||
// allocation. We could `Box::pin` it too if we wanted. This is however
|
||||
// safe since we don't move the `Future` here.
|
||||
let pinned = unsafe { Pin::new_unchecked(&mut future) };
|
||||
match Future::poll(pinned, &mut cx) {
|
||||
// when the Future is ready we're finished
|
||||
Poll::Ready(val) => break val,
|
||||
// If we get a `pending` future we just go to sleep...
|
||||
Poll::Pending => thread::park(),
|
||||
};
|
||||
};
|
||||
val
|
||||
}
|
||||
|
||||
// ====================== FUTURE IMPLEMENTATION ==============================
|
||||
|
||||
// This is the definition of our `Waker`. We use a regular thread-handle here.
|
||||
// It works but it's not a good solution. If one of our `Futures` holds a handle
|
||||
// to our thread and takes it with it to a different thread the followinc could
|
||||
// happen:
|
||||
// 1. Our future calls `unpark` from a different thread
|
||||
// 2. Our `executor` thinks that data is ready and wakes up and polls the future
|
||||
// 3. The future is not ready yet but one nanosecond later the `Reactor` gets
|
||||
// an event and calles `wake()` which also unparks our thread.
|
||||
// 4. This could all happen before we go to sleep again since these processes
|
||||
// run in parallel.
|
||||
// 5. Our reactor has called `wake` but our thread is still sleeping since it was
|
||||
// awake alredy at that point.
|
||||
// 6. We're deadlocked and our program stops working
|
||||
// There are many better soloutions, here are some:
|
||||
// - Use `std::sync::CondVar`
|
||||
// - Use [crossbeam::sync::Parker](https://docs.rs/crossbeam/0.7.3/crossbeam/sync/struct.Parker.html)
|
||||
#[derive(Clone)]
|
||||
struct MyWaker {
|
||||
thread: thread::Thread,
|
||||
}
|
||||
|
||||
// This is the definition of our `Future`. It keeps all the information we
|
||||
// need. This one holds a reference to our `reactor`, that's just to make
|
||||
// this example as easy as possible. It doesn't need to hold a reference to
|
||||
// the whole reactor, but it needs to be able to register itself with the
|
||||
// reactor.
|
||||
#[derive(Clone)]
|
||||
pub struct Task {
|
||||
id: usize,
|
||||
reactor: Arc<Mutex<Reactor>>,
|
||||
data: u64,
|
||||
is_registered: bool,
|
||||
}
|
||||
|
||||
// These are function definitions we'll use for our waker. Remember the
|
||||
// "Trait Objects" chapter from the book.
|
||||
fn mywaker_wake(s: &MyWaker) {
|
||||
let waker_ptr: *const MyWaker = s;
|
||||
let waker_arc = unsafe {Arc::from_raw(waker_ptr)};
|
||||
waker_arc.thread.unpark();
|
||||
}
|
||||
|
||||
// Since we use an `Arc` cloning is just increasing the refcount on the smart
|
||||
// pointer.
|
||||
fn mywaker_clone(s: &MyWaker) -> RawWaker {
|
||||
let arc = unsafe { Arc::from_raw(s).clone() };
|
||||
std::mem::forget(arc.clone()); // increase ref count
|
||||
RawWaker::new(Arc::into_raw(arc) as *const (), &VTABLE)
|
||||
}
|
||||
|
||||
// This is actually a "helper funtcion" to create a `Waker` vtable. In contrast
|
||||
// to when we created a `Trait Object` from scratch we don't need to concern
|
||||
// ourselves with the actual layout of the `vtable` and only provide a fixed
|
||||
// set of functions
|
||||
const VTABLE: RawWakerVTable = unsafe {
|
||||
RawWakerVTable::new(
|
||||
|s| mywaker_clone(&*(s as *const MyWaker)), // clone
|
||||
|s| mywaker_wake(&*(s as *const MyWaker)), // wake
|
||||
|s| mywaker_wake(*(s as *const &MyWaker)), // wake by ref
|
||||
|s| drop(Arc::from_raw(s as *const MyWaker)), // decrease refcount
|
||||
)
|
||||
};
|
||||
|
||||
// Instead of implementing this on the `MyWaker` oject 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 {
|
||||
let raw_waker = RawWaker::new(s as *const (), &VTABLE);
|
||||
unsafe { Waker::from_raw(raw_waker) }
|
||||
}
|
||||
|
||||
impl Task {
|
||||
fn new(reactor: Arc<Mutex<Reactor>>, data: u64, id: usize) -> Self {
|
||||
Task {
|
||||
id,
|
||||
reactor,
|
||||
data,
|
||||
is_registered: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is our `Future` implementation
|
||||
impl Future for Task {
|
||||
// The output for this kind of `leaf future` is just an `usize`. For other
|
||||
// futures this could be something more interesting like a byte stream.
|
||||
type Output = usize;
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut r = self.reactor.lock().unwrap();
|
||||
// we check with the `Reactor` if this future is in its "readylist"
|
||||
if r.is_ready(self.id) {
|
||||
// if it is, we return the data. In this case it's just the ID of
|
||||
// the task.
|
||||
Poll::Ready(self.id)
|
||||
} else if self.is_registered {
|
||||
// If the future is registered alredy, we just return `Pending`
|
||||
Poll::Pending
|
||||
} else {
|
||||
// If we get here, it must be the first time this `Future` is polled
|
||||
// so we register a task with our `reactor`
|
||||
r.register(self.data, cx.waker().clone(), self.id);
|
||||
// oh, we have to drop the lock on our `Mutex` here because we can't
|
||||
// have a shared and exclusive borrow at the same time
|
||||
drop(r);
|
||||
self.is_registered = true;
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============================== REACTOR ===================================
|
||||
|
||||
// This is a "fake" reactor. It does no real I/O, but that also makes our
|
||||
// code possible to run in the book and in the playground
|
||||
struct Reactor {
|
||||
// we need some way of registering a Task with the reactor. Normally this
|
||||
// would be an "interest" in an I/O event
|
||||
dispatcher: Sender<Event>,
|
||||
handle: Option<JoinHandle<()>>,
|
||||
// This is a list of tasks that are ready, which means they should be polled
|
||||
// for data.
|
||||
readylist: Arc<Mutex<Vec<usize>>>,
|
||||
}
|
||||
|
||||
// We just have two kind of events. A timeout event, a "timeout" event called
|
||||
// `Simple` and a `Close` event to close down our reactor.
|
||||
#[derive(Debug)]
|
||||
enum Event {
|
||||
Close,
|
||||
Simple(Waker, u64, usize),
|
||||
}
|
||||
|
||||
impl Reactor {
|
||||
fn new() -> Self {
|
||||
// The way we register new events with our reactor is using a regular
|
||||
// channel
|
||||
let (tx, rx) = channel::<Event>();
|
||||
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
|
||||
// join them later on and finish our programm in a good manner
|
||||
let mut handles = vec![];
|
||||
// This will be the "Reactor thread"
|
||||
let handle = thread::spawn(move || {
|
||||
// This simulates some I/O resource
|
||||
for event in rx {
|
||||
let rl_clone = rl_clone.clone();
|
||||
match event {
|
||||
// If we get a close event we break out of the loop we're in
|
||||
Event::Close => break,
|
||||
Event::Simple(waker, duration, id) => {
|
||||
|
||||
// When we get an event we simply spawn a new thread...
|
||||
let event_handle = thread::spawn(move || {
|
||||
//... which will just sleep for the number of seconds
|
||||
// we provided when creating the `Task`.
|
||||
thread::sleep(Duration::from_secs(duration));
|
||||
// When it's done sleeping we put the ID of this task
|
||||
// on the "readylist"
|
||||
rl_clone.lock().map(|mut rl| rl.push(id)).unwrap();
|
||||
// Then we call `wake` which will wake up our
|
||||
// executor and start polling the futures
|
||||
waker.wake();
|
||||
});
|
||||
|
||||
handles.push(event_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When we exit the Reactor we first join all the handles on
|
||||
// the child threads we've spawned so we catch any panics and
|
||||
// release all resources.
|
||||
for handle in handles {
|
||||
handle.join().unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
Reactor {
|
||||
readylist,
|
||||
dispatcher: tx,
|
||||
handle: Some(handle),
|
||||
}
|
||||
}
|
||||
|
||||
fn register(&mut self, duration: u64, waker: Waker, data: usize) {
|
||||
// registering an event is as simple as sending an `Event` through
|
||||
// the channel.
|
||||
self.dispatcher
|
||||
.send(Event::Simple(waker, duration, data))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn close(&mut self) {
|
||||
self.dispatcher.send(Event::Close).unwrap();
|
||||
}
|
||||
|
||||
// We need a way to check if any event's are ready. This will simply
|
||||
// look through the "readylist" for an event macthing the ID we want to
|
||||
// check for.
|
||||
fn is_ready(&self, id_to_check: usize) -> bool {
|
||||
self.readylist
|
||||
.lock()
|
||||
.map(|rl| rl.iter().any(|id| *id == id_to_check))
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
// When our `Reactor` is dropped we join the reactor thread with the thread
|
||||
// owning our `Reactor` so we catch any panics and release all resources.
|
||||
// It's not needed for this to work, but it really is a best practice to join
|
||||
// all threads you spawn.
|
||||
impl Drop for Reactor {
|
||||
fn drop(&mut self) {
|
||||
self.handle.take().map(|h| h.join().unwrap()).unwrap();
|
||||
}
|
||||
}
|
||||
</code></pre></pre>
|
||||
<h2><a class="header" href="#our-finished-code" id="our-finished-code">Our finished code</a></h2>
|
||||
<p>Here is the whole example. You can edit it right here in your browser and
|
||||
run it yourself. Have fun!</p>
|
||||
<pre><pre class="playpen"><code class="language-rust edition2018 editable">
|
||||
use std::{
|
||||
future::Future, pin::Pin, sync::{mpsc::{channel, Sender}, Arc, Mutex},
|
||||
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
|
||||
@@ -344,7 +660,7 @@ impl Drop for Reactor {
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
|
||||
<a rel="prev" href="1_3_pin.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="1_4_reactor_executor.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
@@ -362,7 +678,7 @@ impl Drop for Reactor {
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
|
||||
<a href="1_3_pin.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a href="1_4_reactor_executor.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="1_0_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html"><strong aria-hidden="true">2.3.</strong> Pin</a></li></ol></li><li><a href="2_0_future_example.html"><strong aria-hidden="true">3.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html" class="active"><strong aria-hidden="true">4.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="1_0_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="1_1_trait_objects.html"><strong aria-hidden="true">3.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="1_4_reactor_executor.html"><strong aria-hidden="true">6.</strong> Reactor/Executor Pattern</a></li><li><a href="2_0_future_example.html"><strong aria-hidden="true">7.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html" class="active"><strong aria-hidden="true">8.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
|
||||
BIN
book/assets/swap_problem.jpg
Normal file
BIN
book/assets/swap_problem.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 165 KiB |
@@ -78,7 +78,7 @@
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="1_0_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html"><strong aria-hidden="true">2.3.</strong> Pin</a></li></ol></li><li><a href="2_0_future_example.html"><strong aria-hidden="true">3.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html"><strong aria-hidden="true">4.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="1_0_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="1_1_trait_objects.html"><strong aria-hidden="true">3.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="1_4_reactor_executor.html"><strong aria-hidden="true">6.</strong> Reactor/Executor Pattern</a></li><li><a href="2_0_future_example.html"><strong aria-hidden="true">7.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html"><strong aria-hidden="true">8.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
@@ -149,73 +149,31 @@
|
||||
<p>This book aims to explain <code>Futures</code> in Rust using an example driven approach.</p>
|
||||
<p>The goal is to get a better understanding of <code>Futures</code> by implementing a toy
|
||||
<code>Reactor</code>, a very simple <code>Executor</code> and our own <code>Futures</code>. </p>
|
||||
<p>We'll start off solving a small problem without <code>Futures</code>, <code>Wakers</code> or async/await
|
||||
and then gradually adapt our example so it implements all these concepts, and
|
||||
can be solved using the executor provided by both <code>tokio</code> and <code>async_str</code>.</p>
|
||||
<p>In the end I've made some reader excercises you can do if you want to fix some
|
||||
of the most glaring ommissions and shortcuts we took and create a slightly better
|
||||
<p>We'll start off a bit differently than most other explanations. Instead of
|
||||
deferring some of the details about what's special about futures in Rust we
|
||||
try to tackle that head on first. We'll be as brief as possible, but as thorough
|
||||
as needed. This way, most question will be answered and explored up front. </p>
|
||||
<p>We'll end up with futures that can run an any executor like <code>tokio</code> and <code>async_str</code>.</p>
|
||||
<p>In the end I've made some reader exercises you can do if you want to fix some
|
||||
of the most glaring omissions and shortcuts we took and create a slightly better
|
||||
example yourself.</p>
|
||||
<h2><a class="header" href="#what-does-this-book-give-you-that-isnt-covered-elsewhere" id="what-does-this-book-give-you-that-isnt-covered-elsewhere">What does this book give you that isn't covered elsewhere?</a></h2>
|
||||
<p>That's a valid question. There are many good resources and examples already. First
|
||||
of all, this book will point you to some background information that I have found
|
||||
very valuable to get an understanding of concurrent programming in general.</p>
|
||||
<p>I find that many discussions arise, not because <code>Futures</code> is a hard concept to
|
||||
grasp, but that concurrent programming is a hard concept in general.</p>
|
||||
<p>Secondly, I've always found small runnable examples very exiting to learn from. It's
|
||||
of all, this book will focus on <code>Futures</code> and <code>async/await</code> specifically and
|
||||
not in the context of any specific runtime.</p>
|
||||
<p>Secondly, I've always found small runnable examples very exiting to learn from.
|
||||
Thanks to Mdbook the examples can even be edited and explored further. It's
|
||||
all code that you can download, play with and learn from.</p>
|
||||
<h2><a class="header" href="#what-well-do-and-not" id="what-well-do-and-not">What we'll do and not</a></h2>
|
||||
<p><strong>We'll:</strong></p>
|
||||
<ul>
|
||||
<li>Implement our own <code>Futures</code> and get to know the <code>Reactor/Executor</code> pattern</li>
|
||||
<li>Implement our own waker and learn why it's a bit foreign compared to other types</li>
|
||||
<li>Talk a bit about runtime complexity and what to keep in mind when writing async Rust.</li>
|
||||
<li>Make sure all examples can be run on the playground</li>
|
||||
<li>Not rely on any helpers or libraries, but try to face the complexity and learn</li>
|
||||
</ul>
|
||||
<p><strong>We'll not:</strong></p>
|
||||
<ul>
|
||||
<li>Talk about how futures are implemented in Rust the language, the state machine and so on</li>
|
||||
<li>Explain how the different runtimes differ, however, you'll hopefully be a bit
|
||||
better off if you read this before you go research them</li>
|
||||
<li>Explain concurrent programming, but I will supply sources</li>
|
||||
</ul>
|
||||
<p>I do want to explore Rusts internal implementation but that will be for a later
|
||||
book.</p>
|
||||
<p>We'll and end up with an understandable example including a <code>Future</code>
|
||||
implementation, an <code>Executor</code> and a <code>Reactor</code> in less than 200 lines of code.
|
||||
We don't rely on any dependencies or real I/O which means it's very easy to
|
||||
explore further and try your own ideas.</p>
|
||||
<h2><a class="header" href="#credits-and-thanks" id="credits-and-thanks">Credits and thanks</a></h2>
|
||||
<p>I'll like to take the chance of thanking the people behind <code>mio</code>, <code>tokio</code>,
|
||||
<code>async_std</code>, <code>Futures</code>, <code>libc</code>, <code>crossbeam</code> and many other libraries which so
|
||||
much is built upon. Reading and exploring some of this code is nothing less than
|
||||
impressive.</p>
|
||||
<h2><a class="header" href="#why-is-futures-in-rust-hard-to-understand" id="why-is-futures-in-rust-hard-to-understand">Why is <code>Futures</code> in Rust hard to understand</a></h2>
|
||||
<p>Well, I think it has to do with several things:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Futures has a very interesting implementation, compiling down to a state
|
||||
machine using generators to suspend and resume execution. In a language such as
|
||||
Rust this is pretty hard to do ergonomically and safely. You are exposed to some
|
||||
if this complexity when working with futures and want to understand them, not
|
||||
only learn how to use them.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Rust doesn't provide a runtime. That means you'll actually have to choose one
|
||||
yourself and actually know what a <code>Reactor</code> and an <code>Executor</code> is. While not
|
||||
too difficult, you need to make more choices than you need in GO and other
|
||||
languages designed with a concurrent programming in mind and ships with a
|
||||
runtime.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Futures exist in two versions, Futures 1.0 and Futures 3.0. Futures 1.0 was
|
||||
known to have some issues regarding ergonomics. Turns out that modelling
|
||||
async coding after <code>Promises</code> in JavaScript can turn in to extremely long errors
|
||||
and type signatures with a type system as Rust.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>Futures 3.0 are not compatible with Futures 1.0 without performing some work.</p>
|
||||
<ol start="4">
|
||||
<li>Async await syntax was recently stabilized</li>
|
||||
</ol>
|
||||
<p>what we'll
|
||||
really do is to stub out a <code>Reactor</code>, and <code>Executor</code> and implement</p>
|
||||
impressive. Even the RFCs that much of the design is built upon is written in a
|
||||
way that mortal people can understand, and that requires a lot of work. So thanks!</p>
|
||||
|
||||
</main>
|
||||
|
||||
|
||||
794
book/print.html
794
book/print.html
@@ -80,7 +80,7 @@
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="1_0_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html"><strong aria-hidden="true">2.3.</strong> Pin</a></li></ol></li><li><a href="2_0_future_example.html"><strong aria-hidden="true">3.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html"><strong aria-hidden="true">4.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
<ol class="chapter"><li><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><a href="1_0_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="1_1_trait_objects.html"><strong aria-hidden="true">3.</strong> Trait objects and fat pointers</a></li><li><a href="1_2_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators and Pin</a></li><li><a href="1_3_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="1_4_reactor_executor.html"><strong aria-hidden="true">6.</strong> Reactor/Executor Pattern</a></li><li><a href="2_0_future_example.html"><strong aria-hidden="true">7.</strong> The main example</a></li><li><a href="2_1_concurrent_futures.html"><strong aria-hidden="true">8.</strong> Bonus 1: concurrent futures</a></li></ol>
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
@@ -151,77 +151,97 @@
|
||||
<p>This book aims to explain <code>Futures</code> in Rust using an example driven approach.</p>
|
||||
<p>The goal is to get a better understanding of <code>Futures</code> by implementing a toy
|
||||
<code>Reactor</code>, a very simple <code>Executor</code> and our own <code>Futures</code>. </p>
|
||||
<p>We'll start off solving a small problem without <code>Futures</code>, <code>Wakers</code> or async/await
|
||||
and then gradually adapt our example so it implements all these concepts, and
|
||||
can be solved using the executor provided by both <code>tokio</code> and <code>async_str</code>.</p>
|
||||
<p>In the end I've made some reader excercises you can do if you want to fix some
|
||||
of the most glaring ommissions and shortcuts we took and create a slightly better
|
||||
<p>We'll start off a bit differently than most other explanations. Instead of
|
||||
deferring some of the details about what's special about futures in Rust we
|
||||
try to tackle that head on first. We'll be as brief as possible, but as thorough
|
||||
as needed. This way, most question will be answered and explored up front. </p>
|
||||
<p>We'll end up with futures that can run an any executor like <code>tokio</code> and <code>async_str</code>.</p>
|
||||
<p>In the end I've made some reader exercises you can do if you want to fix some
|
||||
of the most glaring omissions and shortcuts we took and create a slightly better
|
||||
example yourself.</p>
|
||||
<h2><a class="header" href="#what-does-this-book-give-you-that-isnt-covered-elsewhere" id="what-does-this-book-give-you-that-isnt-covered-elsewhere">What does this book give you that isn't covered elsewhere?</a></h2>
|
||||
<p>That's a valid question. There are many good resources and examples already. First
|
||||
of all, this book will point you to some background information that I have found
|
||||
very valuable to get an understanding of concurrent programming in general.</p>
|
||||
<p>I find that many discussions arise, not because <code>Futures</code> is a hard concept to
|
||||
grasp, but that concurrent programming is a hard concept in general.</p>
|
||||
<p>Secondly, I've always found small runnable examples very exiting to learn from. It's
|
||||
of all, this book will focus on <code>Futures</code> and <code>async/await</code> specifically and
|
||||
not in the context of any specific runtime.</p>
|
||||
<p>Secondly, I've always found small runnable examples very exiting to learn from.
|
||||
Thanks to Mdbook the examples can even be edited and explored further. It's
|
||||
all code that you can download, play with and learn from.</p>
|
||||
<h2><a class="header" href="#what-well-do-and-not" id="what-well-do-and-not">What we'll do and not</a></h2>
|
||||
<p><strong>We'll:</strong></p>
|
||||
<ul>
|
||||
<li>Implement our own <code>Futures</code> and get to know the <code>Reactor/Executor</code> pattern</li>
|
||||
<li>Implement our own waker and learn why it's a bit foreign compared to other types</li>
|
||||
<li>Talk a bit about runtime complexity and what to keep in mind when writing async Rust.</li>
|
||||
<li>Make sure all examples can be run on the playground</li>
|
||||
<li>Not rely on any helpers or libraries, but try to face the complexity and learn</li>
|
||||
</ul>
|
||||
<p><strong>We'll not:</strong></p>
|
||||
<ul>
|
||||
<li>Talk about how futures are implemented in Rust the language, the state machine and so on</li>
|
||||
<li>Explain how the different runtimes differ, however, you'll hopefully be a bit
|
||||
better off if you read this before you go research them</li>
|
||||
<li>Explain concurrent programming, but I will supply sources</li>
|
||||
</ul>
|
||||
<p>I do want to explore Rusts internal implementation but that will be for a later
|
||||
book.</p>
|
||||
<p>We'll and end up with an understandable example including a <code>Future</code>
|
||||
implementation, an <code>Executor</code> and a <code>Reactor</code> in less than 200 lines of code.
|
||||
We don't rely on any dependencies or real I/O which means it's very easy to
|
||||
explore further and try your own ideas.</p>
|
||||
<h2><a class="header" href="#credits-and-thanks" id="credits-and-thanks">Credits and thanks</a></h2>
|
||||
<p>I'll like to take the chance of thanking the people behind <code>mio</code>, <code>tokio</code>,
|
||||
<code>async_std</code>, <code>Futures</code>, <code>libc</code>, <code>crossbeam</code> and many other libraries which so
|
||||
much is built upon. Reading and exploring some of this code is nothing less than
|
||||
impressive.</p>
|
||||
<h2><a class="header" href="#why-is-futures-in-rust-hard-to-understand" id="why-is-futures-in-rust-hard-to-understand">Why is <code>Futures</code> in Rust hard to understand</a></h2>
|
||||
<p>Well, I think it has to do with several things:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Futures has a very interesting implementation, compiling down to a state
|
||||
machine using generators to suspend and resume execution. In a language such as
|
||||
Rust this is pretty hard to do ergonomically and safely. You are exposed to some
|
||||
if this complexity when working with futures and want to understand them, not
|
||||
only learn how to use them.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Rust doesn't provide a runtime. That means you'll actually have to choose one
|
||||
yourself and actually know what a <code>Reactor</code> and an <code>Executor</code> is. While not
|
||||
too difficult, you need to make more choices than you need in GO and other
|
||||
languages designed with a concurrent programming in mind and ships with a
|
||||
runtime.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Futures exist in two versions, Futures 1.0 and Futures 3.0. Futures 1.0 was
|
||||
known to have some issues regarding ergonomics. Turns out that modelling
|
||||
async coding after <code>Promises</code> in JavaScript can turn in to extremely long errors
|
||||
and type signatures with a type system as Rust.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>Futures 3.0 are not compatible with Futures 1.0 without performing some work.</p>
|
||||
<ol start="4">
|
||||
<li>Async await syntax was recently stabilized</li>
|
||||
</ol>
|
||||
<p>what we'll
|
||||
really do is to stub out a <code>Reactor</code>, and <code>Executor</code> and implement</p>
|
||||
impressive. Even the RFCs that much of the design is built upon is written in a
|
||||
way that mortal people can understand, and that requires a lot of work. So thanks!</p>
|
||||
<h1><a class="header" href="#some-background-information" id="some-background-information">Some background information</a></h1>
|
||||
<blockquote>
|
||||
<p><strong>Relevant for:</strong></p>
|
||||
<ul>
|
||||
<li>High level introduction to concurrency in Rust</li>
|
||||
<li>Knowing what Rust provides and not when working with async</li>
|
||||
<li>Understanding why we need runtimes </li>
|
||||
<li>Knowing that Rust has <code>Futures 1.0</code> and <code>Futures 3.0</code>, and how to deal with them</li>
|
||||
<li>Getting pointers to further reading on concurrency in general</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<p>Before we start implementing our <code>Futures</code> , we'll go through some background
|
||||
information that will help demystify some of the concepts we encounter.</p>
|
||||
<h2><a class="header" href="#concurrency-in-general" id="concurrency-in-general">Concurrency in general</a></h2>
|
||||
<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 an extremely lightweight runtime.</p>
|
||||
<p>In languages like C#, JavaScript, Java and GO, the runtime is already there. So
|
||||
if you come from one of those languages this will seem a bit strange to you.</p>
|
||||
<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 extremely 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>
|
||||
</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 resumes 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 one creates leaf <code>Futures</code> and 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. Learning how to use one
|
||||
correctly can be hard as well, but both are excellent and it's just like
|
||||
learning any new library.</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 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 verca. 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>
|
||||
<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
|
||||
@@ -232,64 +252,79 @@ try to give a high level overview that will make it easier to learn Rusts
|
||||
<li><a href="https://cfsamson.github.io/book-exploring-async-basics/5_strategies_for_handling_io.html">Async Basics - Strategies for handling I/O</a></li>
|
||||
<li><a href="https://cfsamson.github.io/book-exploring-async-basics/6_epoll_kqueue_iocp.html">Async Basics - Epoll, Kqueue and IOCP</a></li>
|
||||
</ul>
|
||||
<p>r</p>
|
||||
<p>Now learning these concepts by studying futures is making it much harder than
|
||||
it needs to be, so go on and read these chapters. I'll be right here when
|
||||
you're back. </p>
|
||||
<p>However, if you feel that you have the basics covered, then go right on. </p>
|
||||
<p>Let's get moving!</p>
|
||||
<h1><a class="header" href="#trait-objects-and-fat-pointers" id="trait-objects-and-fat-pointers">Trait objects and fat pointers</a></h1>
|
||||
<blockquote>
|
||||
<p><strong>Relevant for:</strong></p>
|
||||
<ul>
|
||||
<li>Understanding how the Waker object is constructed</li>
|
||||
<li>Getting a basic feel for "type erased" objects and what they are</li>
|
||||
<li>Learning the basics of dynamic dispatch</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<h2><a class="header" href="#trait-objects-and-dynamic-dispatch" id="trait-objects-and-dynamic-dispatch">Trait objects and dynamic dispatch</a></h2>
|
||||
<p>The single most confusing topic we encounter when implementing our own <code>Futures</code>
|
||||
<p>One of the most confusing topic we encounter when implementing our own <code>Futures</code>
|
||||
is how we implement a <code>Waker</code> . Creating a <code>Waker</code> involves creating a <code>vtable</code>
|
||||
which allows using dynamic dispatch to call methods on a <em>type erased</em> trait
|
||||
which allows us to use dynamic dispatch to call methods on a <em>type erased</em> trait
|
||||
object we construct our selves.</p>
|
||||
<p>If you want to know more about dynamic dispatch in Rust I can recommend this article:</p>
|
||||
<p>https://alschwalm.com/blog/static/2017/03/07/exploring-dynamic-dispatch-in-rust/</p>
|
||||
<blockquote>
|
||||
<p>If you want to know more about dynamic dispatch in Rust I can recommend an article written by Adam Schwalm called <a href="https://alschwalm.com/blog/static/2017/03/07/exploring-dynamic-dispatch-in-rust/">Exploring Dynamic Dispatch in Rust</a>.</p>
|
||||
</blockquote>
|
||||
<p>Let's explain this a bit more in detail.</p>
|
||||
<h2><a class="header" href="#fat-pointers-in-rust" id="fat-pointers-in-rust">Fat pointers in Rust</a></h2>
|
||||
<p>Let's take a look at the size of some different pointer types in Rust. If we
|
||||
run the following code:</p>
|
||||
run the following code. <em>(You'll have to press "play" to see the output)</em>:</p>
|
||||
<pre><pre class="playpen"><code class="language-rust"># use std::mem::size_of;
|
||||
trait SomeTrait { }
|
||||
|
||||
fn main() {
|
||||
println!("Size of Box<i32>: {}", size_of::<Box<i32>>());
|
||||
println!("Size of &i32: {}", size_of::<&i32>());
|
||||
println!("Size of &Box<i32>: {}", size_of::<&Box<i32>>());
|
||||
println!("Size of Box<Trait>: {}", size_of::<Box<SomeTrait>>());
|
||||
println!("Size of &dyn Trait: {}", size_of::<&dyn SomeTrait>());
|
||||
println!("Size of &[i32]: {}", size_of::<&[i32]>());
|
||||
println!("Size of &[&dyn Trait]: {}", size_of::<&[&dyn SomeTrait]>());
|
||||
println!("Size of [i32; 10]: {}", size_of::<[i32; 10]>());
|
||||
println!("Size of [&dyn Trait; 10]: {}", size_of::<[&dyn SomeTrait; 10]>());
|
||||
println!("======== The size of different pointers in Rust: ========");
|
||||
println!("&dyn Trait:-----{}", size_of::<&dyn SomeTrait>());
|
||||
println!("&[&dyn Trait]:--{}", size_of::<&[&dyn SomeTrait]>());
|
||||
println!("Box<Trait>:-----{}", size_of::<Box<SomeTrait>>());
|
||||
println!("&i32:-----------{}", size_of::<&i32>());
|
||||
println!("&[i32]:---------{}", size_of::<&[i32]>());
|
||||
println!("Box<i32>:-------{}", size_of::<Box<i32>>());
|
||||
println!("&Box<i32>:------{}", size_of::<&Box<i32>>());
|
||||
println!("[&dyn Trait;4]:-{}", size_of::<[&dyn SomeTrait; 4]>());
|
||||
println!("[i32;4]:--------{}", size_of::<[i32; 4]>());
|
||||
}
|
||||
</code></pre></pre>
|
||||
<p>As you see from the output after running this, the sizes of the references varies.
|
||||
Most are 8 bytes (which is a pointer size on 64 bit systems), but some are 16
|
||||
Many are 8 bytes (which is a pointer size on 64 bit systems), but some are 16
|
||||
bytes.</p>
|
||||
<p>The 16 byte sized pointers are called "fat pointers" since they carry more extra
|
||||
information.</p>
|
||||
<p><strong>In the case of <code>&[i32]</code> :</strong> </p>
|
||||
<ul>
|
||||
<li>The first 8 bytes is the actual pointer to the first element in the array</li>
|
||||
</ul>
|
||||
<p>(or part of an array the slice refers to)</p>
|
||||
<p><strong>Example <code>&[i32]</code> :</strong> </p>
|
||||
<ul>
|
||||
<li>The first 8 bytes is the actual pointer to the first element in the array (or part of an array the slice refers to)</li>
|
||||
<li>The second 8 bytes is the length of the slice.</li>
|
||||
</ul>
|
||||
<p>The one we'll concern ourselves about is the references to traits, or
|
||||
<em>trait objects</em> as they're called in Rust.</p>
|
||||
<p><code>&dyn SomeTrait</code> is an example of a <em>trait object</em> </p>
|
||||
<p><strong>Example <code>&dyn SomeTrait</code>:</strong></p>
|
||||
<p>This is the type of fat pointer we'll concern ourselves about going forward.
|
||||
<code>&dyn SomeTrait</code> is a reference to a trait, or what Rust calls <em>trait objects</em>.</p>
|
||||
<p>The layout for a pointer to a <em>trait object</em> looks like this: </p>
|
||||
<ul>
|
||||
<li>The first 8 bytes points to the <code>data</code> for the trait object</li>
|
||||
<li>The second 8 bytes points to the <code>vtable</code> for the trait object</li>
|
||||
</ul>
|
||||
<p>The reason for this is to allow us to refer to an object we know nothing about
|
||||
except that it implements the methods defined by our trait. To allow this we use
|
||||
dynamic dispatch.</p>
|
||||
except that it implements the methods defined by our trait. To allow accomplish this we use <em>dynamic dispatch</em>.</p>
|
||||
<p>Let's explain this in code instead of words by implementing our own trait
|
||||
object from these parts:</p>
|
||||
<pre><pre class="playpen"><code class="language-rust">// A reference to a trait object is a fat pointer: (data_ptr, vtable_ptr)
|
||||
<blockquote>
|
||||
<p>This is an example of <em>editable</em> code. You can change everything in the example
|
||||
and try to run it. If you want to go back, press the undo symbol. Keep an eye
|
||||
out for these as we go forward. Many examples will be editable.</p>
|
||||
</blockquote>
|
||||
<pre><pre class="playpen"><code class="language-rust editable">// A reference to a trait object is a fat pointer: (data_ptr, vtable_ptr)
|
||||
trait Test {
|
||||
fn add(&self) -> i32;
|
||||
fn sub(&self) -> i32;
|
||||
fn add(&self) -> i32;
|
||||
fn sub(&self) -> i32;
|
||||
fn mul(&self) -> i32;
|
||||
}
|
||||
|
||||
@@ -329,10 +364,10 @@ fn main() {
|
||||
0, // pointer to `Drop` (which we're not implementing here)
|
||||
6, // lenght of vtable
|
||||
8, // alignment
|
||||
|
||||
// we need to make sure we add these in the same order as defined in the Trait.
|
||||
// Try changing the order of add and sub and see what happens.
|
||||
add as usize, // function pointer
|
||||
sub as usize, // function pointer
|
||||
add as usize, // function pointer - try changing the order of `add`
|
||||
sub as usize, // function pointer - and `sub` to see what happens
|
||||
mul as usize, // function pointer
|
||||
];
|
||||
|
||||
@@ -346,63 +381,10 @@ fn main() {
|
||||
}
|
||||
|
||||
</code></pre></pre>
|
||||
<p>If you run this code by pressing the "play" button at the top you'll se it
|
||||
outputs just what we expect.</p>
|
||||
<p>This code example is editable so you can change it
|
||||
and run it to see what happens.</p>
|
||||
<p>The reason we go through this will be clear later on when we implement our own
|
||||
<code>Waker</code> we'll actually set up a <code>vtable</code> like we do here to and knowing what
|
||||
it is will make this much less mysterious.</p>
|
||||
<h2><a class="header" href="#reactorexecutor-pattern" id="reactorexecutor-pattern">Reactor/Executor pattern</a></h2>
|
||||
<p>If you don't know what this is, you should take a few minutes and read about
|
||||
it. You will encounter the term <code>Reactor</code> and <code>Executor</code> a lot when working
|
||||
with async code in Rust.</p>
|
||||
<p>I have written a quick introduction explaining this pattern before which you
|
||||
can take a look at here:</p>
|
||||
<p><a href="https://cfsamsonbooks.gitbook.io/epoll-kqueue-iocp-explained/appendix-1/reactor-executor-pattern"><img src="./assets/reactorexecutor.png" alt="homepage" /></a></p>
|
||||
<div style="text-align:center">
|
||||
<a href="https://cfsamsonbooks.gitbook.io/epoll-kqueue-iocp-explained/appendix-1/reactor-executor-pattern">Epoll, Kqueue and IOCP Explained - The Reactor-Executor Pattern</a>
|
||||
</div>
|
||||
<p>I'll re-iterate the most important parts here.</p>
|
||||
<p>This pattern consists of at least 2 parts:</p>
|
||||
<ol>
|
||||
<li>A reactor
|
||||
<ul>
|
||||
<li>handles some kind of event queue</li>
|
||||
<li>has the responsibility of respoonding to events</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>An executor
|
||||
<ul>
|
||||
<li>Often has a scheduler</li>
|
||||
<li>Holds a set of suspended tasks, and has the responsibility of resuming
|
||||
them when an event has occurred</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>The concept of a task
|
||||
<ul>
|
||||
<li>A set of operations that can be stopped half way and resumed later on</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
<p>This is a pattern not only used in Rust, but it's very popular in Rust due to
|
||||
how well it separates concerns between handling and scheduling tasks, and queing
|
||||
and responding to I/O events.</p>
|
||||
<p>The only thing Rust as a language defines is the <em>task</em>. In Rust we call an
|
||||
incorruptible task a <code>Future</code>. Futures has a well defined interface, which means
|
||||
they can be used across the entire ecosystem.</p>
|
||||
<p>In addition, Rust provides a way for the Reactor and Executor to communicate
|
||||
through the <code>Waker</code>. We'll get to know these in the following chapters.</p>
|
||||
<p>Providing these pieces let's Rust take care a lot of the ergonomic "friction"
|
||||
programmers meet when faced with async code, and still not dictate any
|
||||
preferred runtime to actually do the scheduling and I/O queues.</p>
|
||||
<p>It's important to know that Rust doesn't provide a runtime, so you have to choose
|
||||
one. <a href="https://github.com/async-rs/async-std">async std</a> and <a href="https://github.com/tokio-rs/tokio">tokio</a> are two popular ones.</p>
|
||||
<p>With that out of the way, let's move on to our main example.</p>
|
||||
<h1><a class="header" href="#generators-and-pin" id="generators-and-pin">Generators and Pin</a></h1>
|
||||
<p>So the second difficult part that there seems to be a lot of questions about
|
||||
is Generators and the <code>Pin</code> type.</p>
|
||||
<h2><a class="header" href="#generators" id="generators">Generators</a></h2>
|
||||
<h1><a class="header" href="#generators" id="generators">Generators</a></h1>
|
||||
<blockquote>
|
||||
<p><strong>Relevant for:</strong></p>
|
||||
<ul>
|
||||
@@ -414,6 +396,11 @@ is Generators and the <code>Pin</code> type.</p>
|
||||
well written and I can recommend reading through it (it talks as much about
|
||||
async/await as it does about generators).</p>
|
||||
</blockquote>
|
||||
<p>The second difficult part that there seems to be a lot of questions about
|
||||
is Generators and the <code>Pin</code> type. Since they're related we'll start off by
|
||||
exploring generators first. By doing that we'll soon get to see why
|
||||
we need to be able to "pin" some data to a fixed location in memory and
|
||||
get an introduction to <code>Pin</code> as well.</p>
|
||||
<p>Basically, there were three main options that were discussed when Rust was
|
||||
desiging how the language would handle concurrency:</p>
|
||||
<ol>
|
||||
@@ -425,8 +412,8 @@ desiging how the language would handle concurrency:</p>
|
||||
<p>I've written about green threads before. Go check out
|
||||
<a href="https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/">Green Threads Explained in 200 lines of Rust</a> if you're interested.</p>
|
||||
<p>Green threads uses the same mechanisms as an OS does by creating a thread for
|
||||
each task, setting up a stack and forcing the CPU to save it's state and jump
|
||||
from one task(thread) to another. We yield control to the scheduler which then
|
||||
each task, setting up a stack, save the CPU's state and jump
|
||||
from one task(thread) to another by doing a "context switch". We yield control to the scheduler which then
|
||||
continues running a different task.</p>
|
||||
<p>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
|
||||
@@ -864,7 +851,7 @@ they did their unsafe implementation.</li>
|
||||
</ol>
|
||||
<p>Now, the code which is created and the need for <code>Pin</code> to allow for borrowing
|
||||
across <code>yield</code> points should be pretty clear. </p>
|
||||
<h2><a class="header" href="#pin" id="pin">Pin</a></h2>
|
||||
<h1><a class="header" href="#pin" id="pin">Pin</a></h1>
|
||||
<blockquote>
|
||||
<p><strong>Relevant for</strong></p>
|
||||
<ol>
|
||||
@@ -875,23 +862,60 @@ across <code>yield</code> points should be pretty clear. </p>
|
||||
</ol>
|
||||
<p><code>Pin</code> was suggested in <a href="https://github.com/rust-lang/rfcs/blob/master/text/2349-pin.md">RFC#2349</a></p>
|
||||
</blockquote>
|
||||
<p>Ping consists of the <code>Pin</code> type and the <code>Unpin</code> marker. Let's start off with some general rules:</p>
|
||||
<p>We already got a brief introduction of <code>Pin</code> in the previous chapters, so we'll
|
||||
start off here with some definitions and a set of rules to remember.</p>
|
||||
<h2><a class="header" href="#definitions" id="definitions">Definitions</a></h2>
|
||||
<p>Pin consists of the <code>Pin</code> type and the <code>Unpin</code> marker. Pin's purpose in life is
|
||||
to govern the rules that need to apply for types which implement <code>!Unpin</code>.</p>
|
||||
<p>Pin is only relevant for pointers. A reference to an object is a pointer.</p>
|
||||
<p>Yep, that's double negation for you, as in "does-not-implement-unpin". For this
|
||||
chapter and only this chapter we'll rename these markers to:</p>
|
||||
<blockquote>
|
||||
<p><code>!Unpin</code> = <code>MustStay</code> and <code>Unpin</code> = <code>CanMove</code></p>
|
||||
</blockquote>
|
||||
<p>It just makes it so much easier to understand them.</p>
|
||||
<h2><a class="header" href="#rules-to-remember" id="rules-to-remember">Rules to remember</a></h2>
|
||||
<ol>
|
||||
<li>Pin does nothing special, it only prevents the user of an API to violate some assumtions you make when writing your (most likely) unsafe code.</li>
|
||||
<li>Most standard library types implement <code>Unpin</code></li>
|
||||
<li><code>Unpin</code> means it's OK for this type to be moved even when pinned.</li>
|
||||
<li>If you <code>Box</code> a value, that boxed value automatcally implements <code>Unpin</code>.</li>
|
||||
<li>The main use case for <code>Pin</code> is to allow self referential types</li>
|
||||
<li>The implementation behind objects that doens't implement <code>Unpin</code> is most likely unsafe
|
||||
<ol>
|
||||
<li><code>Pin</code> prevents users from your code to break the assumtions you make when writing the <code>unsafe</code> implementation</li>
|
||||
<li>It doesn't solve the fact that you'll have to write unsafe code to actually implement it</li>
|
||||
</ol>
|
||||
<li>
|
||||
<p>If <code>T: CanMove</code> (which is the default), then <code>Pin<'a, T></code> is entirely equivalent to <code>&'a mut T</code>. in other words: <code>CanMove</code> means it's OK for this type to be moved even when pinned, so <code>Pin</code> will have no effect on such a type.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Getting a <code>&mut T</code> to a pinned pointer requires unsafe if <code>T: MustStay</code>. In other words: requiring a pinned pointer to a type which is <code>MustStay</code> prevents the <em>user</em> of that API from moving that value unless it choses to write <code>unsafe</code> code.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Pinning does nothing special with that memory like putting it into some "read only" memory or anything fancy. It only tells the compiler that some operations on this value should be forbidden. </p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Most standard library types implement <code>CanMove</code>. The same goes for most
|
||||
"normal" types you encounter in Rust. <code>Futures</code> and <code>Generators</code> are two
|
||||
exceptions.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>The main use case for <code>Pin</code> is to allow self referential types, the whole
|
||||
justification for stabilizing them was to allow that. There are still corner
|
||||
cases in the API which are being explored.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>The implementation behind objects that are <code>MustStay</code> is most likely unsafe.
|
||||
Moving such a type can cause the universe to crash. As of the time of writing
|
||||
this book, creating an reading fields of a self referential struct still requires <code>unsafe</code>.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>You're not really meant to be implementing <code>MustStay</code>, but you can on nightly with a feature flag, or by adding <code>std::marker::PhantomPinned</code> to your type.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>When Pinning, you can either pin a value to memory either on the stack or
|
||||
on the heap.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Pinning a <code>MustStay</code> pointer to the stack requires <code>unsafe</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Pinning a <code>MustStay</code> pointer to the heap does not require <code>unsafe</code>. There is a shortcut for doing this using <code>Box::pin</code>.</p>
|
||||
</li>
|
||||
<li>You're not really meant to be implementing <code>!Unpin</code>, but you can on nightly with a feature flag</li>
|
||||
</ol>
|
||||
<blockquote>
|
||||
<p>Unsafe code does not mean it's litterally "unsafe", it only relieves the
|
||||
<p>Unsafe code does not mean it's literally "unsafe", it only relieves the
|
||||
guarantees you normally get from the compiler. An <code>unsafe</code> implementation can
|
||||
be perfectly safe to do, but you have no safety net.</p>
|
||||
</blockquote>
|
||||
@@ -939,11 +963,30 @@ impl Test {
|
||||
}
|
||||
}
|
||||
</code></pre></pre>
|
||||
<p>As you can see this results in unwanted behavior. The pointer to <code>b</code> stays the
|
||||
same and points to the old value. It's easy to get this to segfault, and fail
|
||||
in other spectacular ways as well.</p>
|
||||
<p>Pin essentially prevents the <strong>user</strong> of your unsafe code
|
||||
(even if that means yourself) move the value after it's pinned.</p>
|
||||
<p>Let's walk through this example since we'll be using it the rest of this chapter.</p>
|
||||
<p>We have a self-referential struct <code>Test</code>. <code>Test</code> needs an <code>init</code> method to be
|
||||
created which is strange but we'll need that to keep this example as short as
|
||||
possible.</p>
|
||||
<p><code>Test</code> provides two methods to get a reference to the value of the fields
|
||||
<code>a</code> and <code>b</code>. Since <code>b</code> is a reference to <code>a</code> we store it as a pointer since
|
||||
the borrowing rules of Rust doesn't allow us to define this lifetime.</p>
|
||||
<p>In our main method we first instantiate two instances of <code>Test</code> and print out
|
||||
the value of the fields on <code>test1</code>. We get:</p>
|
||||
<pre><code class="language-rust ignore">a: test1, b: test1
|
||||
</code></pre>
|
||||
<p>Next we swap the data stored at the memory location which <code>test1</code> is pointing to
|
||||
with the data stored at the memory location <code>test2</code> is pointing to and vice a verca.</p>
|
||||
<p>We should expect that printing the fields of <code>test2</code> should display the same as
|
||||
<code>test1</code> (since the object we printed before the swap has moved there now).</p>
|
||||
<pre><code class="language-rust ignore">a: test1, b: test2
|
||||
</code></pre>
|
||||
<p>The pointer to <code>b</code> still points to the old location. That location is now
|
||||
occupied with the string "test2". This can be a bit hard to visualize so I made
|
||||
a figure that i hope can help.</p>
|
||||
<p><strong>Fig 1: Before and after swap</strong>
|
||||
<img src="../assets/swap_problem.jpg" alt="swap_problem" /></p>
|
||||
<p>As you can see this results in unwanted behavior. It's easy to get this to
|
||||
segfault, show UB and fail in other spectacular ways as well.</p>
|
||||
<p>If we change the example to using <code>Pin</code> instead:</p>
|
||||
<pre><pre class="playpen"><code class="language-rust editable">use std::pin::Pin;
|
||||
use std::marker::PhantomPinned;
|
||||
@@ -1005,13 +1048,14 @@ impl Test {
|
||||
|
||||
</code></pre></pre>
|
||||
<p>Now, what we've done here is pinning a stack address. That will always be
|
||||
<code>unsafe</code> if our type implements <code>!Unpin</code>, in other words. That our type is not
|
||||
<code>Unpin</code> which is the norm.</p>
|
||||
<code>unsafe</code> if our type implements <code>!Unpin</code> (aka <code>MustStay</code>). </p>
|
||||
<p>We use some tricks here, including requiring an <code>init</code>. If we want to fix that
|
||||
and let users avoid <code>unsafe</code> we need to place our data on the heap.</p>
|
||||
and let users avoid <code>unsafe</code> we need to pin our data on the heap instead.</p>
|
||||
<blockquote>
|
||||
<p>Stack pinning will always depend on the current stack frame we're in, so we
|
||||
can't create a self referential object in one stack frame and return it since
|
||||
any pointers we take to "self" is invalidated.</p>
|
||||
</blockquote>
|
||||
<p>The next example solves some of our friction at the cost of a heap allocation.</p>
|
||||
<pre><pre class="playpen"><code class="language-rust editbable">use std::pin::Pin;
|
||||
use std::marker::PhantomPinned;
|
||||
@@ -1058,19 +1102,10 @@ impl Test {
|
||||
}
|
||||
}
|
||||
</code></pre></pre>
|
||||
<p>Seeing this we're ready to sum up with a few more points to remember about
|
||||
pinning:</p>
|
||||
<ol>
|
||||
<li>Pinning only makes sense to do for types that are <code>!Unpin</code></li>
|
||||
<li>Pinning a <code>!Unpin</code> pointer to the stack will requires <code>unsafe</code></li>
|
||||
<li>Pinning a boxed value will not require <code>unsafe</code>, even if the type is <code>!Unpin</code></li>
|
||||
<li>If T: Unpin (which is the default), then Pin<'a, T> is entirely equivalent to &'a mut T.</li>
|
||||
<li>Getting a <code>&mut T</code> to a pinned pointer requires unsafe if <code>T: !Unpin</code></li>
|
||||
<li>Pinning is really only useful when implementing self-referential types.<br />
|
||||
For all intents and purposes you can think of <code>!Unpin</code> = self-referential-type</li>
|
||||
</ol>
|
||||
<p>The fact that boxing (heap allocating) a value that implements <code>!Unpin</code> is safe
|
||||
makes sense. Once the data is allocated on the heap it will have a stable address. </p>
|
||||
makes sense. Once the data is allocated on the heap it will have a stable address.</p>
|
||||
<p>There is no need for us as users of the API to take special care and ensure
|
||||
that the self-referential pointer stays valid.</p>
|
||||
<p>There are ways to safely give some guarantees on stack pinning as well, but right
|
||||
now you need to use a crate like <a href="https://github.com/rust-lang/rfcs/blob/master/text/2349-pin.md">pin_utils</a>:<a href="https://github.com/rust-lang/rfcs/blob/master/text/2349-pin.md">pin_utils</a> to do that.</p>
|
||||
<h3><a class="header" href="#projectionstructural-pinning" id="projectionstructural-pinning">Projection/structural pinning</a></h3>
|
||||
@@ -1085,7 +1120,398 @@ extra care must be taken when implementing <code>Drop</code> for pinned types.</
|
||||
<h2><a class="header" href="#putting-it-all-together" id="putting-it-all-together">Putting it all together</a></h2>
|
||||
<p>This is exactly what we'll do when we implement our own <code>Futures</code> stay tuned,
|
||||
we're soon finished.</p>
|
||||
<pre><pre class="playpen"><code class="language-rust">
|
||||
<h1><a class="header" href="#reactorexecutor-pattern" id="reactorexecutor-pattern">Reactor/Executor Pattern</a></h1>
|
||||
<blockquote>
|
||||
<p><strong>Relevant for:</strong></p>
|
||||
<ul>
|
||||
<li>Getting a high level overview of a common runtime model in Rust</li>
|
||||
<li>Introducing these terms so we're on the same page when referring to them</li>
|
||||
<li>Getting pointers on where to get more information about this pattern</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<p>If you don't know what this is, you should take a few minutes and read about
|
||||
it. You will encounter the term <code>Reactor</code> and <code>Executor</code> a lot when working
|
||||
with async code in Rust.</p>
|
||||
<p>I have written a quick introduction explaining this pattern before which you
|
||||
can take a look at here:</p>
|
||||
<p><a href="https://cfsamsonbooks.gitbook.io/epoll-kqueue-iocp-explained/appendix-1/reactor-executor-pattern"><img src="./assets/reactorexecutor.png" alt="homepage" /></a></p>
|
||||
<div style="text-align:center">
|
||||
<a href="https://cfsamsonbooks.gitbook.io/epoll-kqueue-iocp-explained/appendix-1/reactor-executor-pattern">Epoll, Kqueue and IOCP Explained - The Reactor-Executor Pattern</a>
|
||||
</div>
|
||||
<p>I'll re-iterate the most important parts here.</p>
|
||||
<p><strong>This pattern consists of at least 2 parts:</strong></p>
|
||||
<ol>
|
||||
<li><strong>A reactor</strong>
|
||||
<ul>
|
||||
<li>handles some kind of event queue</li>
|
||||
<li>has the responsibility of respoonding to events</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>An executor</strong>
|
||||
<ul>
|
||||
<li>Often has a scheduler</li>
|
||||
<li>Holds a set of suspended tasks, and has the responsibility of resuming
|
||||
them when an event has occurred</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>The concept of a task</strong>
|
||||
<ul>
|
||||
<li>A set of operations that can be stopped half way and resumed later on</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
<p>This kind of pattern common outside of Rust as well, but it's especially popular in Rust due to how well it alignes with the API provided by Rusts standard library. This model separates concerns between handling and scheduling tasks, and queing and responding to I/O events.</p>
|
||||
<h2><a class="header" href="#the-reactor" id="the-reactor">The Reactor</a></h2>
|
||||
<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>
|
||||
<p>We can call this kind of <code>Future</code> a "leaf Future`, since it's some operation
|
||||
we'll actually wait on and that we can chain operations on which are performed
|
||||
once the leaf future is ready. </p>
|
||||
<h2><a class="header" href="#the-task" id="the-task">The Task</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 operations can spawn new leaf futures themselves.</p>
|
||||
<h2><a class="header" href="#the-executor" id="the-executor">The executor</a></h2>
|
||||
<p>The executors task is to take one or more futures and run them to completion.</p>
|
||||
<p>The first thing an <code>executor</code> does when it get's a <code>Future</code> is polling it.</p>
|
||||
<p><strong>When polled one of three things can happen:</strong></p>
|
||||
<ul>
|
||||
<li>The future returns <code>Ready</code> and we schedule whatever chained operations to run</li>
|
||||
<li>The future hasn't been polled before so we pass it a <code>Waker</code> and suspend it</li>
|
||||
<li>The futures has been polled before but is not ready and returns <code>Pending</code></li>
|
||||
</ul>
|
||||
<p>Rust provides a way for the Reactor and Executor to communicate through the <code>Waker</code>. The reactor stores this <code>Waker</code> and calls <code>Waker::wake()</code> on it once
|
||||
a <code>Future</code> has resolved and should be polled again.</p>
|
||||
<p>We'll get to know these concepts better in the following chapters.</p>
|
||||
<p>Providing these pieces let's Rust take care a lot of the ergonomic "friction"
|
||||
programmers meet when faced with async code, and still not dictate any
|
||||
preferred runtime to actually do the scheduling and I/O queues.</p>
|
||||
<p>With that out of the way, let's move on to actually implement all this in our
|
||||
example.</p>
|
||||
<h1><a class="header" href="#futures-in-rust" id="futures-in-rust">Futures in Rust</a></h1>
|
||||
<p>We'll create our own <code>Futures</code> together with a fake reactor and a simple
|
||||
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 clone the repository and play around with the code yourself. There
|
||||
are two branches. The <code>basic_example</code> is this code, and the <code>basic_example_commented</code>
|
||||
is this example with extensive comments.</p>
|
||||
<h2><a class="header" href="#implementing-our-own-futures" id="implementing-our-own-futures">Implementing our own Futures</a></h2>
|
||||
<p>Let's start with why we wrote this book, by implementing our own <code>Futures</code>.</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},
|
||||
thread::{self, JoinHandle}, time::{Duration, Instant}
|
||||
};
|
||||
|
||||
fn main() {
|
||||
// This is just to make it easier for us to see when our Future was resolved
|
||||
let start = Instant::now();
|
||||
|
||||
// Many runtimes create a glocal `reactor` we pass it as an argument
|
||||
let reactor = Reactor::new();
|
||||
// Since we'll share this between threads we wrap it in a
|
||||
// atmically-refcounted- mutex.
|
||||
let reactor = Arc::new(Mutex::new(reactor));
|
||||
|
||||
// We create two tasks:
|
||||
// - first parameter is the `reactor`
|
||||
// - the second is a timeout in seconds
|
||||
// - the third is an `id` to identify the task
|
||||
let future1 = Task::new(reactor.clone(), 2, 1);
|
||||
let future2 = Task::new(reactor.clone(), 1, 2);
|
||||
|
||||
// an `async` block works the same way as an `async fn` in that it compiles
|
||||
// our code into a state machine, `yielding` at every `await` point.
|
||||
let fut1 = async {
|
||||
let val = future1.await;
|
||||
let dur = (Instant::now() - start).as_secs_f32();
|
||||
println!("Future got {} at time: {:.2}.", val, dur);
|
||||
};
|
||||
|
||||
let fut2 = async {
|
||||
let val = future2.await;
|
||||
let dur = (Instant::now() - start).as_secs_f32();
|
||||
println!("Future got {} at time: {:.2}.", val, dur);
|
||||
};
|
||||
|
||||
// Our executor can only run one and one future, this is pretty normal
|
||||
// though. You have a set of operations containing many futures that
|
||||
// ends up as a single future that drives them all to completion.
|
||||
let mainfut = async {
|
||||
fut1.await;
|
||||
fut2.await;
|
||||
};
|
||||
|
||||
// This executor will block the main thread until the futures is resolved
|
||||
block_on(mainfut);
|
||||
// When we're done, we want to shut down our reactor thread so our program
|
||||
// ends nicely.
|
||||
reactor.lock().map(|mut r| r.close()).unwrap();
|
||||
}
|
||||
|
||||
//// ============================ EXECUTOR ====================================
|
||||
|
||||
// Our executor takes any object which implements the `Future` trait
|
||||
fn block_on<F: Future>(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));
|
||||
// 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.
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
|
||||
// We poll in a loop, but it's not a busy loop. It will only run when
|
||||
// an event occurs, or a thread has a "spurious wakeup" (an unexpected wakeup
|
||||
// that can happen for no good reason).
|
||||
let val = loop {
|
||||
// So, since we run this on one thread and run one future to completion
|
||||
// we can pin the `Future` to the stack. This is unsafe, but saves an
|
||||
// allocation. We could `Box::pin` it too if we wanted. This is however
|
||||
// safe since we don't move the `Future` here.
|
||||
let pinned = unsafe { Pin::new_unchecked(&mut future) };
|
||||
match Future::poll(pinned, &mut cx) {
|
||||
// when the Future is ready we're finished
|
||||
Poll::Ready(val) => break val,
|
||||
// If we get a `pending` future we just go to sleep...
|
||||
Poll::Pending => thread::park(),
|
||||
};
|
||||
};
|
||||
val
|
||||
}
|
||||
|
||||
// ====================== FUTURE IMPLEMENTATION ==============================
|
||||
|
||||
// This is the definition of our `Waker`. We use a regular thread-handle here.
|
||||
// It works but it's not a good solution. If one of our `Futures` holds a handle
|
||||
// to our thread and takes it with it to a different thread the followinc could
|
||||
// happen:
|
||||
// 1. Our future calls `unpark` from a different thread
|
||||
// 2. Our `executor` thinks that data is ready and wakes up and polls the future
|
||||
// 3. The future is not ready yet but one nanosecond later the `Reactor` gets
|
||||
// an event and calles `wake()` which also unparks our thread.
|
||||
// 4. This could all happen before we go to sleep again since these processes
|
||||
// run in parallel.
|
||||
// 5. Our reactor has called `wake` but our thread is still sleeping since it was
|
||||
// awake alredy at that point.
|
||||
// 6. We're deadlocked and our program stops working
|
||||
// There are many better soloutions, here are some:
|
||||
// - Use `std::sync::CondVar`
|
||||
// - Use [crossbeam::sync::Parker](https://docs.rs/crossbeam/0.7.3/crossbeam/sync/struct.Parker.html)
|
||||
#[derive(Clone)]
|
||||
struct MyWaker {
|
||||
thread: thread::Thread,
|
||||
}
|
||||
|
||||
// This is the definition of our `Future`. It keeps all the information we
|
||||
// need. This one holds a reference to our `reactor`, that's just to make
|
||||
// this example as easy as possible. It doesn't need to hold a reference to
|
||||
// the whole reactor, but it needs to be able to register itself with the
|
||||
// reactor.
|
||||
#[derive(Clone)]
|
||||
pub struct Task {
|
||||
id: usize,
|
||||
reactor: Arc<Mutex<Reactor>>,
|
||||
data: u64,
|
||||
is_registered: bool,
|
||||
}
|
||||
|
||||
// These are function definitions we'll use for our waker. Remember the
|
||||
// "Trait Objects" chapter from the book.
|
||||
fn mywaker_wake(s: &MyWaker) {
|
||||
let waker_ptr: *const MyWaker = s;
|
||||
let waker_arc = unsafe {Arc::from_raw(waker_ptr)};
|
||||
waker_arc.thread.unpark();
|
||||
}
|
||||
|
||||
// Since we use an `Arc` cloning is just increasing the refcount on the smart
|
||||
// pointer.
|
||||
fn mywaker_clone(s: &MyWaker) -> RawWaker {
|
||||
let arc = unsafe { Arc::from_raw(s).clone() };
|
||||
std::mem::forget(arc.clone()); // increase ref count
|
||||
RawWaker::new(Arc::into_raw(arc) as *const (), &VTABLE)
|
||||
}
|
||||
|
||||
// This is actually a "helper funtcion" to create a `Waker` vtable. In contrast
|
||||
// to when we created a `Trait Object` from scratch we don't need to concern
|
||||
// ourselves with the actual layout of the `vtable` and only provide a fixed
|
||||
// set of functions
|
||||
const VTABLE: RawWakerVTable = unsafe {
|
||||
RawWakerVTable::new(
|
||||
|s| mywaker_clone(&*(s as *const MyWaker)), // clone
|
||||
|s| mywaker_wake(&*(s as *const MyWaker)), // wake
|
||||
|s| mywaker_wake(*(s as *const &MyWaker)), // wake by ref
|
||||
|s| drop(Arc::from_raw(s as *const MyWaker)), // decrease refcount
|
||||
)
|
||||
};
|
||||
|
||||
// Instead of implementing this on the `MyWaker` oject 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 {
|
||||
let raw_waker = RawWaker::new(s as *const (), &VTABLE);
|
||||
unsafe { Waker::from_raw(raw_waker) }
|
||||
}
|
||||
|
||||
impl Task {
|
||||
fn new(reactor: Arc<Mutex<Reactor>>, data: u64, id: usize) -> Self {
|
||||
Task {
|
||||
id,
|
||||
reactor,
|
||||
data,
|
||||
is_registered: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is our `Future` implementation
|
||||
impl Future for Task {
|
||||
// The output for this kind of `leaf future` is just an `usize`. For other
|
||||
// futures this could be something more interesting like a byte stream.
|
||||
type Output = usize;
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut r = self.reactor.lock().unwrap();
|
||||
// we check with the `Reactor` if this future is in its "readylist"
|
||||
if r.is_ready(self.id) {
|
||||
// if it is, we return the data. In this case it's just the ID of
|
||||
// the task.
|
||||
Poll::Ready(self.id)
|
||||
} else if self.is_registered {
|
||||
// If the future is registered alredy, we just return `Pending`
|
||||
Poll::Pending
|
||||
} else {
|
||||
// If we get here, it must be the first time this `Future` is polled
|
||||
// so we register a task with our `reactor`
|
||||
r.register(self.data, cx.waker().clone(), self.id);
|
||||
// oh, we have to drop the lock on our `Mutex` here because we can't
|
||||
// have a shared and exclusive borrow at the same time
|
||||
drop(r);
|
||||
self.is_registered = true;
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============================== REACTOR ===================================
|
||||
|
||||
// This is a "fake" reactor. It does no real I/O, but that also makes our
|
||||
// code possible to run in the book and in the playground
|
||||
struct Reactor {
|
||||
// we need some way of registering a Task with the reactor. Normally this
|
||||
// would be an "interest" in an I/O event
|
||||
dispatcher: Sender<Event>,
|
||||
handle: Option<JoinHandle<()>>,
|
||||
// This is a list of tasks that are ready, which means they should be polled
|
||||
// for data.
|
||||
readylist: Arc<Mutex<Vec<usize>>>,
|
||||
}
|
||||
|
||||
// We just have two kind of events. A timeout event, a "timeout" event called
|
||||
// `Simple` and a `Close` event to close down our reactor.
|
||||
#[derive(Debug)]
|
||||
enum Event {
|
||||
Close,
|
||||
Simple(Waker, u64, usize),
|
||||
}
|
||||
|
||||
impl Reactor {
|
||||
fn new() -> Self {
|
||||
// The way we register new events with our reactor is using a regular
|
||||
// channel
|
||||
let (tx, rx) = channel::<Event>();
|
||||
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
|
||||
// join them later on and finish our programm in a good manner
|
||||
let mut handles = vec![];
|
||||
// This will be the "Reactor thread"
|
||||
let handle = thread::spawn(move || {
|
||||
// This simulates some I/O resource
|
||||
for event in rx {
|
||||
let rl_clone = rl_clone.clone();
|
||||
match event {
|
||||
// If we get a close event we break out of the loop we're in
|
||||
Event::Close => break,
|
||||
Event::Simple(waker, duration, id) => {
|
||||
|
||||
// When we get an event we simply spawn a new thread...
|
||||
let event_handle = thread::spawn(move || {
|
||||
//... which will just sleep for the number of seconds
|
||||
// we provided when creating the `Task`.
|
||||
thread::sleep(Duration::from_secs(duration));
|
||||
// When it's done sleeping we put the ID of this task
|
||||
// on the "readylist"
|
||||
rl_clone.lock().map(|mut rl| rl.push(id)).unwrap();
|
||||
// Then we call `wake` which will wake up our
|
||||
// executor and start polling the futures
|
||||
waker.wake();
|
||||
});
|
||||
|
||||
handles.push(event_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When we exit the Reactor we first join all the handles on
|
||||
// the child threads we've spawned so we catch any panics and
|
||||
// release all resources.
|
||||
for handle in handles {
|
||||
handle.join().unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
Reactor {
|
||||
readylist,
|
||||
dispatcher: tx,
|
||||
handle: Some(handle),
|
||||
}
|
||||
}
|
||||
|
||||
fn register(&mut self, duration: u64, waker: Waker, data: usize) {
|
||||
// registering an event is as simple as sending an `Event` through
|
||||
// the channel.
|
||||
self.dispatcher
|
||||
.send(Event::Simple(waker, duration, data))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn close(&mut self) {
|
||||
self.dispatcher.send(Event::Close).unwrap();
|
||||
}
|
||||
|
||||
// We need a way to check if any event's are ready. This will simply
|
||||
// look through the "readylist" for an event macthing the ID we want to
|
||||
// check for.
|
||||
fn is_ready(&self, id_to_check: usize) -> bool {
|
||||
self.readylist
|
||||
.lock()
|
||||
.map(|rl| rl.iter().any(|id| *id == id_to_check))
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
// When our `Reactor` is dropped we join the reactor thread with the thread
|
||||
// owning our `Reactor` so we catch any panics and release all resources.
|
||||
// It's not needed for this to work, but it really is a best practice to join
|
||||
// all threads you spawn.
|
||||
impl Drop for Reactor {
|
||||
fn drop(&mut self) {
|
||||
self.handle.take().map(|h| h.join().unwrap()).unwrap();
|
||||
}
|
||||
}
|
||||
</code></pre></pre>
|
||||
<h2><a class="header" href="#our-finished-code" id="our-finished-code">Our finished code</a></h2>
|
||||
<p>Here is the whole example. You can edit it right here in your browser and
|
||||
run it yourself. Have fun!</p>
|
||||
<pre><pre class="playpen"><code class="language-rust edition2018 editable">
|
||||
use std::{
|
||||
future::Future, pin::Pin, sync::{mpsc::{channel, Sender}, Arc, Mutex},
|
||||
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,5 +1,13 @@
|
||||
# Some background information
|
||||
|
||||
> **Relevant for:**
|
||||
>
|
||||
> - High level introduction to concurrency in Rust
|
||||
> - Knowing what Rust provides and not when working with async
|
||||
> - Understanding why we need runtimes
|
||||
> - Knowing that Rust has `Futures 1.0` and `Futures 3.0`, and how to deal with them
|
||||
> - Getting pointers to further reading on concurrency in general
|
||||
|
||||
Before we start implementing our `Futures` , we'll go through some background
|
||||
information that will help demystify some of the concepts we encounter.
|
||||
|
||||
@@ -91,12 +99,7 @@ Now learning these concepts by studying futures is making it much harder than
|
||||
it needs to be, so go on and read these chapters. I'll be right here when
|
||||
you're back.
|
||||
|
||||
However, if you feel that you have the basics covered, then go right on. The concepts we need to
|
||||
learn are:
|
||||
|
||||
1. Trait Objects and fat pointers
|
||||
2. Generators/stackless coroutines
|
||||
3. Pinning, what it is and why we need it
|
||||
However, if you feel that you have the basics covered, then go right on.
|
||||
|
||||
Let's get moving!
|
||||
|
||||
|
||||
@@ -8,58 +8,54 @@
|
||||
|
||||
## Trait objects and dynamic dispatch
|
||||
|
||||
The single most confusing topic we encounter when implementing our own `Futures`
|
||||
One of the most confusing topic we encounter when implementing our own `Futures`
|
||||
is how we implement a `Waker` . Creating a `Waker` involves creating a `vtable`
|
||||
which allows using dynamic dispatch to call methods on a _type erased_ trait
|
||||
which allows us to use dynamic dispatch to call methods on a _type erased_ trait
|
||||
object we construct our selves.
|
||||
|
||||
If you want to know more about dynamic dispatch in Rust I can recommend this article:
|
||||
|
||||
https://alschwalm.com/blog/static/2017/03/07/exploring-dynamic-dispatch-in-rust/
|
||||
>If you want to know more about dynamic dispatch in Rust I can recommend an article written by Adam Schwalm called [Exploring Dynamic Dispatch in Rust](https://alschwalm.com/blog/static/2017/03/07/exploring-dynamic-dispatch-in-rust/).
|
||||
|
||||
Let's explain this a bit more in detail.
|
||||
|
||||
## Fat pointers in Rust
|
||||
|
||||
Let's take a look at the size of some different pointer types in Rust. If we
|
||||
run the following code:
|
||||
run the following code. _(You'll have to press "play" to see the output)_:
|
||||
|
||||
``` rust
|
||||
# use std::mem::size_of;
|
||||
trait SomeTrait { }
|
||||
|
||||
fn main() {
|
||||
println!("Size of Box<i32>: {}", size_of::<Box<i32>>());
|
||||
println!("Size of &i32: {}", size_of::<&i32>());
|
||||
println!("Size of &Box<i32>: {}", size_of::<&Box<i32>>());
|
||||
println!("Size of Box<Trait>: {}", size_of::<Box<SomeTrait>>());
|
||||
println!("Size of &dyn Trait: {}", size_of::<&dyn SomeTrait>());
|
||||
println!("Size of &[i32]: {}", size_of::<&[i32]>());
|
||||
println!("Size of &[&dyn Trait]: {}", size_of::<&[&dyn SomeTrait]>());
|
||||
println!("Size of [i32; 10]: {}", size_of::<[i32; 10]>());
|
||||
println!("Size of [&dyn Trait; 10]: {}", size_of::<[&dyn SomeTrait; 10]>());
|
||||
println!("======== The size of different pointers in Rust: ========");
|
||||
println!("&dyn Trait:-----{}", size_of::<&dyn SomeTrait>());
|
||||
println!("&[&dyn Trait]:--{}", size_of::<&[&dyn SomeTrait]>());
|
||||
println!("Box<Trait>:-----{}", size_of::<Box<SomeTrait>>());
|
||||
println!("&i32:-----------{}", size_of::<&i32>());
|
||||
println!("&[i32]:---------{}", size_of::<&[i32]>());
|
||||
println!("Box<i32>:-------{}", size_of::<Box<i32>>());
|
||||
println!("&Box<i32>:------{}", size_of::<&Box<i32>>());
|
||||
println!("[&dyn Trait;4]:-{}", size_of::<[&dyn SomeTrait; 4]>());
|
||||
println!("[i32;4]:--------{}", size_of::<[i32; 4]>());
|
||||
}
|
||||
```
|
||||
|
||||
As you see from the output after running this, the sizes of the references varies.
|
||||
Most are 8 bytes (which is a pointer size on 64 bit systems), but some are 16
|
||||
Many are 8 bytes (which is a pointer size on 64 bit systems), but some are 16
|
||||
bytes.
|
||||
|
||||
The 16 byte sized pointers are called "fat pointers" since they carry more extra
|
||||
information.
|
||||
|
||||
**In the case of `&[i32]` :**
|
||||
|
||||
* The first 8 bytes is the actual pointer to the first element in the array
|
||||
|
||||
(or part of an array the slice refers to)
|
||||
**Example `&[i32]` :**
|
||||
|
||||
* The first 8 bytes is the actual pointer to the first element in the array (or part of an array the slice refers to)
|
||||
* The second 8 bytes is the length of the slice.
|
||||
|
||||
The one we'll concern ourselves about is the references to traits, or
|
||||
_trait objects_ as they're called in Rust.
|
||||
**Example `&dyn SomeTrait`:**
|
||||
|
||||
`&dyn SomeTrait` is an example of a _trait object_
|
||||
This is the type of fat pointer we'll concern ourselves about going forward.
|
||||
`&dyn SomeTrait` is a reference to a trait, or what Rust calls _trait objects_.
|
||||
|
||||
The layout for a pointer to a _trait object_ looks like this:
|
||||
|
||||
@@ -67,17 +63,19 @@ _trait objects_ as they're called in Rust.
|
||||
* The second 8 bytes points to the `vtable` for the trait object
|
||||
|
||||
The reason for this is to allow us to refer to an object we know nothing about
|
||||
except that it implements the methods defined by our trait. To allow this we use
|
||||
dynamic dispatch.
|
||||
except that it implements the methods defined by our trait. To allow accomplish this we use _dynamic dispatch_.
|
||||
|
||||
Let's explain this in code instead of words by implementing our own trait
|
||||
object from these parts:
|
||||
|
||||
``` rust
|
||||
>This is an example of _editable_ code. You can change everything in the example
|
||||
and try to run it. If you want to go back, press the undo symbol. Keep an eye
|
||||
out for these as we go forward. Many examples will be editable.
|
||||
```rust, editable
|
||||
// A reference to a trait object is a fat pointer: (data_ptr, vtable_ptr)
|
||||
trait Test {
|
||||
fn add(&self) -> i32;
|
||||
fn sub(&self) -> i32;
|
||||
fn add(&self) -> i32;
|
||||
fn sub(&self) -> i32;
|
||||
fn mul(&self) -> i32;
|
||||
}
|
||||
|
||||
@@ -117,10 +115,10 @@ fn main() {
|
||||
0, // pointer to `Drop` (which we're not implementing here)
|
||||
6, // lenght of vtable
|
||||
8, // alignment
|
||||
|
||||
// we need to make sure we add these in the same order as defined in the Trait.
|
||||
// Try changing the order of add and sub and see what happens.
|
||||
add as usize, // function pointer
|
||||
sub as usize, // function pointer
|
||||
add as usize, // function pointer - try changing the order of `add`
|
||||
sub as usize, // function pointer - and `sub` to see what happens
|
||||
mul as usize, // function pointer
|
||||
];
|
||||
|
||||
@@ -135,68 +133,6 @@ fn main() {
|
||||
|
||||
```
|
||||
|
||||
If you run this code by pressing the "play" button at the top you'll se it
|
||||
outputs just what we expect.
|
||||
|
||||
This code example is editable so you can change it
|
||||
and run it to see what happens.
|
||||
|
||||
The reason we go through this will be clear later on when we implement our own
|
||||
`Waker` we'll actually set up a `vtable` like we do here to and knowing what
|
||||
it is will make this much less mysterious.
|
||||
|
||||
## Reactor/Executor pattern
|
||||
|
||||
If you don't know what this is, you should take a few minutes and read about
|
||||
it. You will encounter the term `Reactor` and `Executor` a lot when working
|
||||
with async code in Rust.
|
||||
|
||||
I have written a quick introduction explaining this pattern before which you
|
||||
can take a look at here:
|
||||
|
||||
|
||||
[![homepage][1]][2]
|
||||
|
||||
<div style="text-align:center">
|
||||
<a href="https://cfsamsonbooks.gitbook.io/epoll-kqueue-iocp-explained/appendix-1/reactor-executor-pattern">Epoll, Kqueue and IOCP Explained - The Reactor-Executor Pattern</a>
|
||||
</div>
|
||||
|
||||
I'll re-iterate the most important parts here.
|
||||
|
||||
This pattern consists of at least 2 parts:
|
||||
|
||||
1. A reactor
|
||||
- handles some kind of event queue
|
||||
- has the responsibility of respoonding to events
|
||||
2. An executor
|
||||
- Often has a scheduler
|
||||
- Holds a set of suspended tasks, and has the responsibility of resuming
|
||||
them when an event has occurred
|
||||
3. The concept of a task
|
||||
- A set of operations that can be stopped half way and resumed later on
|
||||
|
||||
This is a pattern not only used in Rust, but it's very popular in Rust due to
|
||||
how well it separates concerns between handling and scheduling tasks, and queing
|
||||
and responding to I/O events.
|
||||
|
||||
The only thing Rust as a language defines is the _task_. In Rust we call an
|
||||
incorruptible task a `Future`. Futures has a well defined interface, which means
|
||||
they can be used across the entire ecosystem.
|
||||
|
||||
In addition, Rust provides a way for the Reactor and Executor to communicate
|
||||
through the `Waker`. We'll get to know these in the following chapters.
|
||||
|
||||
Providing these pieces let's Rust take care a lot of the ergonomic "friction"
|
||||
programmers meet when faced with async code, and still not dictate any
|
||||
preferred runtime to actually do the scheduling and I/O queues.
|
||||
|
||||
It's important to know that Rust doesn't provide a runtime, so you have to choose
|
||||
one. [async std](https://github.com/async-rs/async-std) and [tokio](https://github.com/tokio-rs/tokio) are two popular ones.
|
||||
|
||||
With that out of the way, let's move on to our main example.
|
||||
|
||||
|
||||
|
||||
|
||||
[1]: ./assets/reactorexecutor.png
|
||||
[2]: https://cfsamsonbooks.gitbook.io/epoll-kqueue-iocp-explained/appendix-1/reactor-executor-pattern
|
||||
it is will make this much less mysterious.
|
||||
@@ -1,12 +1,5 @@
|
||||
# Generators
|
||||
|
||||
So the second difficult part that there seems to be a lot of questions about
|
||||
is Generators and the `Pin` type. Since they're related we'll start off by
|
||||
undertanding generators first. By doing that we'll soon get to see why
|
||||
we need to be able to "pin" some data to a fixed location in memory and
|
||||
get an introduction to `Pin` as well.
|
||||
|
||||
|
||||
>**Relevant for:**
|
||||
>
|
||||
>- Understanding how the async/await syntax works since it's how `await` is implemented
|
||||
@@ -17,6 +10,12 @@ get an introduction to `Pin` as well.
|
||||
>well written and I can recommend reading through it (it talks as much about
|
||||
>async/await as it does about generators).
|
||||
|
||||
The second difficult part that there seems to be a lot of questions about
|
||||
is Generators and the `Pin` type. Since they're related we'll start off by
|
||||
exploring generators first. By doing that we'll soon get to see why
|
||||
we need to be able to "pin" some data to a fixed location in memory and
|
||||
get an introduction to `Pin` as well.
|
||||
|
||||
Basically, there were three main options that were discussed when Rust was
|
||||
desiging how the language would handle concurrency:
|
||||
|
||||
|
||||
120
src/1_3_pin.md
120
src/1_3_pin.md
@@ -1,4 +1,4 @@
|
||||
## Pin
|
||||
# Pin
|
||||
|
||||
> **Relevant for**
|
||||
>
|
||||
@@ -9,20 +9,53 @@
|
||||
>
|
||||
> `Pin` was suggested in [RFC#2349][rfc2349]
|
||||
|
||||
Pin consists of the `Pin` type and the `Unpin` marker. Let's start off with some general rules:
|
||||
We already got a brief introduction of `Pin` in the previous chapters, so we'll
|
||||
start off here with some definitions and a set of rules to remember.
|
||||
|
||||
1. Pin does nothing special, it only prevents the user of an API to violate some assumtions you make when writing your (most likely) unsafe code.
|
||||
2. Most standard library types implement `Unpin`
|
||||
3. `Unpin` means it's OK for this type to be moved even when pinned.
|
||||
4. If you `Box` a value, that boxed value automatcally implements `Unpin`.
|
||||
5. The main use case for `Pin` is to allow self referential types
|
||||
6. The implementation behind objects that doens't implement `Unpin` is most likely unsafe
|
||||
1. `Pin` prevents users from your code to break the assumtions you make when writing the `unsafe` implementation
|
||||
2. It doesn't solve the fact that you'll have to write unsafe code to actually implement it
|
||||
7. You're not really meant to be implementing `!Unpin`, but you can on nightly with a feature flag
|
||||
## Definitions
|
||||
|
||||
Pin consists of the `Pin` type and the `Unpin` marker. Pin's purpose in life is
|
||||
to govern the rules that need to apply for types which implement `!Unpin`.
|
||||
|
||||
> Unsafe code does not mean it's litterally "unsafe", it only relieves the
|
||||
Pin is only relevant for pointers. A reference to an object is a pointer.
|
||||
|
||||
Yep, that's double negation for you, as in "does-not-implement-unpin". For this
|
||||
chapter and only this chapter we'll rename these markers to:
|
||||
|
||||
> `!Unpin` = `MustStay` and `Unpin` = `CanMove`
|
||||
|
||||
It just makes it so much easier to understand them.
|
||||
|
||||
## Rules to remember
|
||||
|
||||
1. If `T: CanMove` (which is the default), then `Pin<'a, T>` is entirely equivalent to `&'a mut T`. in other words: `CanMove` means it's OK for this type to be moved even when pinned, so `Pin` will have no effect on such a type.
|
||||
|
||||
2. Getting a `&mut T` to a pinned pointer requires unsafe if `T: MustStay`. In other words: requiring a pinned pointer to a type which is `MustStay` prevents the _user_ of that API from moving that value unless it choses to write `unsafe` code.
|
||||
|
||||
3. Pinning does nothing special with that memory like putting it into some "read only" memory or anything fancy. It only tells the compiler that some operations on this value should be forbidden.
|
||||
|
||||
4. Most standard library types implement `CanMove`. The same goes for most
|
||||
"normal" types you encounter in Rust. `Futures` and `Generators` are two
|
||||
exceptions.
|
||||
|
||||
5. The main use case for `Pin` is to allow self referential types, the whole
|
||||
justification for stabilizing them was to allow that. There are still corner
|
||||
cases in the API which are being explored.
|
||||
|
||||
6. The implementation behind objects that are `MustStay` is most likely unsafe.
|
||||
Moving such a type can cause the universe to crash. As of the time of writing
|
||||
this book, creating an reading fields of a self referential struct still requires `unsafe`.
|
||||
|
||||
7. You're not really meant to be implementing `MustStay`, but you can on nightly with a feature flag, or by adding `std::marker::PhantomPinned` to your type.
|
||||
|
||||
8. When Pinning, you can either pin a value to memory either on the stack or
|
||||
on the heap.
|
||||
|
||||
1. Pinning a `MustStay` pointer to the stack requires `unsafe`
|
||||
|
||||
2. Pinning a `MustStay` pointer to the heap does not require `unsafe`. There is a shortcut for doing this using `Box::pin`.
|
||||
|
||||
> Unsafe code does not mean it's literally "unsafe", it only relieves the
|
||||
> guarantees you normally get from the compiler. An `unsafe` implementation can
|
||||
> be perfectly safe to do, but you have no safety net.
|
||||
|
||||
@@ -73,12 +106,42 @@ impl Test {
|
||||
}
|
||||
```
|
||||
|
||||
As you can see this results in unwanted behavior. The pointer to `b` stays the
|
||||
same and points to the old value. It's easy to get this to segfault, and fail
|
||||
in other spectacular ways as well.
|
||||
Let's walk through this example since we'll be using it the rest of this chapter.
|
||||
|
||||
Pin essentially prevents the **user** of your unsafe code
|
||||
(even if that means yourself) move the value after it's pinned.
|
||||
We have a self-referential struct `Test`. `Test` needs an `init` method to be
|
||||
created which is strange but we'll need that to keep this example as short as
|
||||
possible.
|
||||
|
||||
`Test` provides two methods to get a reference to the value of the fields
|
||||
`a` and `b`. Since `b` is a reference to `a` we store it as a pointer since
|
||||
the borrowing rules of Rust doesn't allow us to define this lifetime.
|
||||
|
||||
In our main method we first instantiate two instances of `Test` and print out
|
||||
the value of the fields on `test1`. We get:
|
||||
|
||||
```rust, ignore
|
||||
a: test1, b: test1
|
||||
```
|
||||
|
||||
|
||||
Next we swap the data stored at the memory location which `test1` is pointing to
|
||||
with the data stored at the memory location `test2` is pointing to and vice a verca.
|
||||
|
||||
We should expect that printing the fields of `test2` should display the same as
|
||||
`test1` (since the object we printed before the swap has moved there now).
|
||||
|
||||
```rust, ignore
|
||||
a: test1, b: test2
|
||||
```
|
||||
The pointer to `b` still points to the old location. That location is now
|
||||
occupied with the string "test2". This can be a bit hard to visualize so I made
|
||||
a figure that i hope can help.
|
||||
|
||||
**Fig 1: Before and after swap**
|
||||

|
||||
|
||||
As you can see this results in unwanted behavior. It's easy to get this to
|
||||
segfault, show UB and fail in other spectacular ways as well.
|
||||
|
||||
If we change the example to using `Pin` instead:
|
||||
|
||||
@@ -144,13 +207,12 @@ impl Test {
|
||||
```
|
||||
|
||||
Now, what we've done here is pinning a stack address. That will always be
|
||||
`unsafe` if our type implements `!Unpin`, in other words. That our type is not
|
||||
`Unpin` which is the norm.
|
||||
`unsafe` if our type implements `!Unpin` (aka `MustStay`).
|
||||
|
||||
We use some tricks here, including requiring an `init`. If we want to fix that
|
||||
and let users avoid `unsafe` we need to place our data on the heap.
|
||||
and let users avoid `unsafe` we need to pin our data on the heap instead.
|
||||
|
||||
Stack pinning will always depend on the current stack frame we're in, so we
|
||||
> Stack pinning will always depend on the current stack frame we're in, so we
|
||||
can't create a self referential object in one stack frame and return it since
|
||||
any pointers we take to "self" is invalidated.
|
||||
|
||||
@@ -203,19 +265,11 @@ impl Test {
|
||||
}
|
||||
```
|
||||
|
||||
Seeing this we're ready to sum up with a few more points to remember about
|
||||
pinning:
|
||||
|
||||
1. Pinning only makes sense to do for types that are `!Unpin`
|
||||
2. Pinning a `!Unpin` pointer to the stack will requires `unsafe`
|
||||
3. Pinning a boxed value will not require `unsafe`, even if the type is `!Unpin`
|
||||
4. If T: Unpin (which is the default), then Pin<'a, T> is entirely equivalent to &'a mut T.
|
||||
5. Getting a `&mut T` to a pinned pointer requires unsafe if `T: !Unpin`
|
||||
6. Pinning is really only useful when implementing self-referential types.
|
||||
For all intents and purposes you can think of `!Unpin` = self-referential-type
|
||||
|
||||
The fact that boxing (heap allocating) a value that implements `!Unpin` is safe
|
||||
makes sense. Once the data is allocated on the heap it will have a stable address.
|
||||
makes sense. Once the data is allocated on the heap it will have a stable address.
|
||||
|
||||
There is no need for us as users of the API to take special care and ensure
|
||||
that the self-referential pointer stays valid.
|
||||
|
||||
There are ways to safely give some guarantees on stack pinning as well, but right
|
||||
now you need to use a crate like [pin_utils]:[pin_utils] to do that.
|
||||
|
||||
91
src/1_4_reactor_executor.md
Normal file
91
src/1_4_reactor_executor.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# Reactor/Executor Pattern
|
||||
|
||||
> **Relevant for:**
|
||||
>
|
||||
> - Getting a high level overview of a common runtime model in Rust
|
||||
> - Introducing these terms so we're on the same page when referring to them
|
||||
> - Getting pointers on where to get more information about this pattern
|
||||
|
||||
If you don't know what this is, you should take a few minutes and read about
|
||||
it. You will encounter the term `Reactor` and `Executor` a lot when working
|
||||
with async code in Rust.
|
||||
|
||||
I have written a quick introduction explaining this pattern before which you
|
||||
can take a look at here:
|
||||
|
||||
|
||||
[![homepage][1]][2]
|
||||
|
||||
<div style="text-align:center">
|
||||
<a href="https://cfsamsonbooks.gitbook.io/epoll-kqueue-iocp-explained/appendix-1/reactor-executor-pattern">Epoll, Kqueue and IOCP Explained - The Reactor-Executor Pattern</a>
|
||||
</div>
|
||||
|
||||
I'll re-iterate the most important parts here.
|
||||
|
||||
**This pattern consists of at least 2 parts:**
|
||||
|
||||
1. **A reactor**
|
||||
- handles some kind of event queue
|
||||
- has the responsibility of respoonding to events
|
||||
2. **An executor**
|
||||
- Often has a scheduler
|
||||
- Holds a set of suspended tasks, and has the responsibility of resuming
|
||||
them when an event has occurred
|
||||
3. **The concept of a task**
|
||||
- A set of operations that can be stopped half way and resumed later on
|
||||
|
||||
This kind of pattern common outside of Rust as well, but it's especially popular in Rust due to how well it alignes with the API provided by Rusts standard library. This model separates concerns between handling and scheduling tasks, and queing and responding to I/O events.
|
||||
|
||||
## The Reactor
|
||||
|
||||
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.
|
||||
|
||||
This is the `Reactors` job. Most often you'll
|
||||
see reactors in rust use a library called [Mio][mio], which provides non
|
||||
blocking APIs and event notification for several platforms.
|
||||
|
||||
The reactor will typically give you something like a `TcpStream` (or any other resource) which you'll use to create an I/O request. What you get in return
|
||||
is a `Future`.
|
||||
|
||||
We can call this kind of `Future` a "leaf Future`, since it's some operation
|
||||
we'll actually wait on and that we can chain operations on which are performed
|
||||
once the leaf future is ready.
|
||||
|
||||
## The Task
|
||||
|
||||
In Rust we call an interruptible task a `Future`. Futures has a well defined interface, which means they can be used across the entire ecosystem. We can chain
|
||||
these `Futures` so that once a "leaf future" is ready we'll perform a set of
|
||||
operations.
|
||||
|
||||
These operations can spawn new leaf futures themselves.
|
||||
|
||||
## The executor
|
||||
|
||||
The executors task is to take one or more futures and run them to completion.
|
||||
|
||||
The first thing an `executor` does when it get's a `Future` is polling it.
|
||||
|
||||
**When polled one of three things can happen:**
|
||||
|
||||
- The future returns `Ready` and we schedule whatever chained operations to run
|
||||
- The future hasn't been polled before so we pass it a `Waker` and suspend it
|
||||
- The futures has been polled before but is not ready and returns `Pending`
|
||||
|
||||
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.
|
||||
|
||||
We'll get to know these concepts better in the following chapters.
|
||||
|
||||
Providing these pieces let's Rust take care a lot of the ergonomic "friction"
|
||||
programmers meet when faced with async code, and still not dictate any
|
||||
preferred runtime to actually do the scheduling and I/O queues.
|
||||
|
||||
|
||||
With that out of the way, let's move on to actually implement all this in our
|
||||
example.
|
||||
|
||||
[1]: ./assets/reactorexecutor.png
|
||||
[2]: https://cfsamsonbooks.gitbook.io/epoll-kqueue-iocp-explained/appendix-1/reactor-executor-pattern
|
||||
[mio]: https://github.com/tokio-rs/mio
|
||||
@@ -5,5 +5,6 @@
|
||||
- [Trait objects and fat pointers](./1_1_trait_objects.md)
|
||||
- [Generators and Pin](./1_2_generators_pin.md)
|
||||
- [Pin](./1_3_pin.md)
|
||||
- [Reactor/Executor Pattern](./1_4_reactor_executor.md)
|
||||
- [The main example](./2_0_future_example.md)
|
||||
- [Bonus 1: concurrent futures](2_1_concurrent_futures.md)
|
||||
|
||||
BIN
src/assets/swap_problem.jpg
Normal file
BIN
src/assets/swap_problem.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 165 KiB |
Reference in New Issue
Block a user