Files
books-futures-explained/book/2_waker_context.html
2020-02-25 19:02:19 +01:00

396 lines
20 KiB
HTML

<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Waker and Context - Futures Explained in 200 Lines of Rust</title>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="This book aims to explain Futures in Rust using an example driven approach.">
<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 class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">1.</strong> Some background information</a></li><li><a href="2_waker_context.html" class="active"><strong aria-hidden="true">2.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">3.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">4.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">5.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">6.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
<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>
<a href="https://github.com/cfsamson/books-futures-explained" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></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="#waker-and-context" id="waker-and-context">Waker and Context</a></h1>
<blockquote>
<p><strong>Relevant for:</strong></p>
<ul>
<li>Understanding how the Waker object is constructed</li>
<li>Learning how the runtime know when a leaf-future can resume</li>
<li>Learning the basics of dynamic dispatch and trait objects</li>
</ul>
<p>The <code>Waker</code> type is described as part of <a href="https://github.com/rust-lang/rfcs/blob/master/text/2592-futures.md#waking-up">RFC#2592</a>.</p>
</blockquote>
<h2><a class="header" href="#the-waker" id="the-waker">The Waker</a></h2>
<p>The <code>Waker</code> type allows for a loose coupling between the reactor-part and the executor-part of a runtime.</p>
<p>By having a wake up mechanism that is <em>not</em> tied to the thing that executes
the future, runtime-implementors can come up with interesting new wake-up
mechanisms. An example of this can be spawning a thread to do some work that
eventually notifies the future, completely independent of the current runtime.</p>
<p>Without a waker, the executor would be the <em>only</em> way to notify a running
task, whereas with the waker, we get a loose coupling where it's easy to
extend the ecosystem with new leaf-level tasks.</p>
<blockquote>
<p>If you want to read more about the reasoning behind the <code>Waker</code> type I can
recommend <a href="https://boats.gitlab.io/blog/post/wakers-i/">Withoutboats articles series about them</a>.</p>
</blockquote>
<h2><a class="header" href="#the-context-type" id="the-context-type">The Context type</a></h2>
<p>As the docs state as of now this type only wrapps a <code>Waker</code>, but it gives some
flexibility for future evolutions of the API in Rust. The context can hold
task-local storage and provide space for debugging hooks in later iterations.</p>
<h2><a class="header" href="#understanding-the-waker" id="understanding-the-waker">Understanding the <code>Waker</code></a></h2>
<p>One of the most confusing things 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 us to use dynamic dispatch to call methods on a <em>type erased</em> trait
object we construct our selves.</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>To get a better understanding of how we implement the <code>Waker</code> in Rust, we need
to take a step back and talk about some fundamentals. Let's start by taking a
look at the size of some different pointer types in Rust. </p>
<p>Run the following code <em>(You'll have to press &quot;play&quot; to see the output)</em>:</p>
<pre><pre class="playpen"><code class="language-rust"># use std::mem::size_of;
trait SomeTrait { }
fn main() {
println!(&quot;======== The size of different pointers in Rust: ========&quot;);
println!(&quot;&amp;dyn Trait:-----{}&quot;, size_of::&lt;&amp;dyn SomeTrait&gt;());
println!(&quot;&amp;[&amp;dyn Trait]:--{}&quot;, size_of::&lt;&amp;[&amp;dyn SomeTrait]&gt;());
println!(&quot;Box&lt;Trait&gt;:-----{}&quot;, size_of::&lt;Box&lt;SomeTrait&gt;&gt;());
println!(&quot;&amp;i32:-----------{}&quot;, size_of::&lt;&amp;i32&gt;());
println!(&quot;&amp;[i32]:---------{}&quot;, size_of::&lt;&amp;[i32]&gt;());
println!(&quot;Box&lt;i32&gt;:-------{}&quot;, size_of::&lt;Box&lt;i32&gt;&gt;());
println!(&quot;&amp;Box&lt;i32&gt;:------{}&quot;, size_of::&lt;&amp;Box&lt;i32&gt;&gt;());
println!(&quot;[&amp;dyn Trait;4]:-{}&quot;, size_of::&lt;[&amp;dyn SomeTrait; 4]&gt;());
println!(&quot;[i32;4]:--------{}&quot;, size_of::&lt;[i32; 4]&gt;());
}
</code></pre></pre>
<p>As you see from the output after running this, the sizes of the references varies.
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 &quot;fat pointers&quot; since they carry extra
information.</p>
<p><strong>Example <code>&amp;[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><strong>Example <code>&amp;dyn SomeTrait</code>:</strong></p>
<p>This is the type of fat pointer we'll concern ourselves about going forward.
<code>&amp;dyn SomeTrait</code> is a reference to a trait, or what Rust calls a <em>trait object</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 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>
<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(&amp;self) -&gt; i32;
fn sub(&amp;self) -&gt; i32;
fn mul(&amp;self) -&gt; i32;
}
// This will represent our home brewn fat pointer to a trait object
#[repr(C)]
struct FatPointer&lt;'a&gt; {
/// A reference is a pointer to an instantiated `Data` instance
data: &amp;'a mut Data,
/// Since we need to pass in literal values like length and alignment it's
/// easiest for us to convert pointers to usize-integers instead of the other way around.
vtable: *const usize,
}
// This is the data in our trait object. It's just two numbers we want to operate on.
struct Data {
a: i32,
b: i32,
}
// ====== function definitions ======
fn add(s: &amp;Data) -&gt; i32 {
s.a + s.b
}
fn sub(s: &amp;Data) -&gt; i32 {
s.a - s.b
}
fn mul(s: &amp;Data) -&gt; i32 {
s.a * s.b
}
fn main() {
let mut data = Data {a: 3, b: 2};
// vtable is like special purpose array of pointer-length types with a fixed
// format where the three first values has a special meaning like the
// length of the array is encoded in the array itself as the second value.
let vtable = vec![
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.
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
];
let fat_pointer = FatPointer { data: &amp;mut data, vtable: vtable.as_ptr()};
let test = unsafe { std::mem::transmute::&lt;FatPointer, &amp;dyn Test&gt;(fat_pointer) };
// And voalá, it's now a trait object we can call methods on
println!(&quot;Add: 3 + 2 = {}&quot;, test.add());
println!(&quot;Sub: 3 - 2 = {}&quot;, test.sub());
println!(&quot;Mul: 3 * 2 = {}&quot;, test.mul());
}
</code></pre></pre>
<p>Now that you know this you also know why how we implement the <code>Waker</code> type
in Rust.</p>
<p>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 why we do that and how it works will make this
much less mysterious.</p>
<h2><a class="header" href="#bonus-section" id="bonus-section">Bonus section</a></h2>
<p>You might wonder why the <code>Waker</code> was implemented like this and not just as a
normal trait?</p>
<p>The reason is flexibility. Implementing the Waker the way we do here gives a lot
of flexibility of choosing what memory management scheme to use.</p>
<p>The &quot;normal&quot; way is by using an <code>Arc</code> to use reference count keep track of when
a Waker object can be dropped. However, this is not the only way, you could also
use purely global functions and state, or any other way you wish.</p>
<p>This leaves a lot of options on the table for runtime implementors.</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="1_background_information.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="3_generators_pin.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_background_information.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="3_generators_pin.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>
<!-- Google Analytics Tag -->
<script type="text/javascript">
var localAddrs = ["localhost", "127.0.0.1", ""];
// make sure we don't activate google analytics if the developer is
// inspecting the book locally...
if (localAddrs.indexOf(document.location.hostname) === -1) {
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-157536992-1', 'auto');
ga('send', 'pageview');
}
</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>