This commit is contained in:
Carl Fredrik Samson
2020-01-26 20:41:41 +01:00
parent d0a018bfd1
commit c52fa3085a
23 changed files with 1267 additions and 914 deletions

View File

@@ -1,5 +1,5 @@
<!DOCTYPE HTML> <!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light"> <html lang="en" class="sidebar-visible no-js">
<head> <head>
<!-- Book generated using mdBook --> <!-- Book generated using mdBook -->
<meta charset="UTF-8"> <meta charset="UTF-8">
@@ -32,11 +32,11 @@
</head> </head>
<body> <body class="light">
<!-- Provide site root to javascript --> <!-- Provide site root to javascript -->
<script type="text/javascript"> <script type="text/javascript">
var path_to_root = ""; var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light"; var default_theme = "light";
</script> </script>
<!-- Work around some values being stored in localStorage wrapped in quotes --> <!-- Work around some values being stored in localStorage wrapped in quotes -->
@@ -60,11 +60,8 @@
var theme; var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { } try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; } if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html'); document.body.className = theme;
html.classList.remove('no-js') document.querySelector('html').className = theme + ' js';
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script> </script>
<!-- Hide / unhide sidebar before it is displayed --> <!-- Hide / unhide sidebar before it is displayed -->
@@ -80,8 +77,8 @@
</script> </script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents"> <nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox"> <div class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded "><a href="0_0_introduction.html" class="active"><strong aria-hidden="true">1.</strong> Introduction</a></li><li class="expanded "><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li class="expanded "><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li class="expanded "><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li class="expanded "><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li class="expanded "><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li class="expanded "><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: 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="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
</div> </div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div> <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav> </nav>
@@ -148,7 +145,77 @@
<div id="content" class="content"> <div id="content" class="content">
<main> <main>
<h1><a class="header" href="#introduction" id="introduction">Introduction</a></h1> <h1><a class="header" href="#futures-explained-in-200-lines-of-rust" id="futures-explained-in-200-lines-of-rust">Futures Explained in 200 Lines of Rust</a></h1>
<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
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
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>
<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>
</main> </main>
@@ -180,30 +247,7 @@
</div> </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 type="text/javascript">
window.playpen_copyable = true;
</script>

View File

@@ -0,0 +1,215 @@
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Trait objects and fat pointers - 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="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html" class="active"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: 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="#trait-objects-and-fat-pointers" id="trait-objects-and-fat-pointers">Trait objects and fat pointers</a></h1>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="0_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="0_1_2_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="0_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="0_1_2_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>
<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>

View File

@@ -0,0 +1,215 @@
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Generators and Pin - 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="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html" class="active"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: 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="#generators-and-pin" id="generators-and-pin">Generators and Pin</a></h1>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="0_1_1_trait_objects.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="0_2_naive_implementation.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="0_1_1_trait_objects.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="0_2_naive_implementation.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>
<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>

View File

@@ -1,5 +1,5 @@
<!DOCTYPE HTML> <!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light"> <html lang="en" class="sidebar-visible no-js">
<head> <head>
<!-- Book generated using mdBook --> <!-- Book generated using mdBook -->
<meta charset="UTF-8"> <meta charset="UTF-8">
@@ -32,11 +32,11 @@
</head> </head>
<body> <body class="light">
<!-- Provide site root to javascript --> <!-- Provide site root to javascript -->
<script type="text/javascript"> <script type="text/javascript">
var path_to_root = ""; var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light"; var default_theme = "light";
</script> </script>
<!-- Work around some values being stored in localStorage wrapped in quotes --> <!-- Work around some values being stored in localStorage wrapped in quotes -->
@@ -60,11 +60,8 @@
var theme; var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { } try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; } if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html'); document.body.className = theme;
html.classList.remove('no-js') document.querySelector('html').className = theme + ' js';
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script> </script>
<!-- Hide / unhide sidebar before it is displayed --> <!-- Hide / unhide sidebar before it is displayed -->
@@ -80,8 +77,8 @@
</script> </script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents"> <nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox"> <div class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded "><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li class="expanded "><a href="0_1_background_information.html" class="active"><strong aria-hidden="true">2.</strong> Some background information</a></li><li class="expanded "><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li class="expanded "><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li class="expanded "><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li class="expanded "><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li class="expanded "><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: 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="0_1_background_information.html" class="active"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
</div> </div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div> <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav> </nav>
@@ -173,8 +170,8 @@ object we construct our selves.</p>
<h2><a class="header" href="#fat-pointers-in-rust" id="fat-pointers-in-rust">Fat pointers in Rust</a></h2> <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 <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:</p>
<pre><pre class="playpen"><code class="language-rust"><span class="boring">use std::mem::size_of; <pre><pre class="playpen"><code class="language-rust"># use std::mem::size_of;
</span>trait SomeTrait { } trait SomeTrait { }
fn main() { fn main() {
println!(&quot;Size of Box&lt;i32&gt;: {}&quot;, size_of::&lt;Box&lt;i32&gt;&gt;()); println!(&quot;Size of Box&lt;i32&gt;: {}&quot;, size_of::&lt;Box&lt;i32&gt;&gt;());
@@ -339,7 +336,7 @@ one. <a href="https://github.com/async-rs/async-std">async std</a> and <a href="
<a rel="next" href="0_2_naive_implementation.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right"> <a rel="next" href="0_1_1_trait_objects.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i> <i class="fa fa-angle-right"></i>
</a> </a>
@@ -357,7 +354,7 @@ one. <a href="https://github.com/async-rs/async-std">async std</a> and <a href="
<a href="0_2_naive_implementation.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right"> <a href="0_1_1_trait_objects.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i> <i class="fa fa-angle-right"></i>
</a> </a>
@@ -366,30 +363,7 @@ one. <a href="https://github.com/async-rs/async-std">async std</a> and <a href="
</div> </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 type="text/javascript">
window.playpen_copyable = true;
</script>

View File

@@ -1,5 +1,5 @@
<!DOCTYPE HTML> <!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light"> <html lang="en" class="sidebar-visible no-js">
<head> <head>
<!-- Book generated using mdBook --> <!-- Book generated using mdBook -->
<meta charset="UTF-8"> <meta charset="UTF-8">
@@ -32,11 +32,11 @@
</head> </head>
<body> <body class="light">
<!-- Provide site root to javascript --> <!-- Provide site root to javascript -->
<script type="text/javascript"> <script type="text/javascript">
var path_to_root = ""; var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light"; var default_theme = "light";
</script> </script>
<!-- Work around some values being stored in localStorage wrapped in quotes --> <!-- Work around some values being stored in localStorage wrapped in quotes -->
@@ -60,11 +60,8 @@
var theme; var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { } try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; } if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html'); document.body.className = theme;
html.classList.remove('no-js') document.querySelector('html').className = theme + ' js';
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script> </script>
<!-- Hide / unhide sidebar before it is displayed --> <!-- Hide / unhide sidebar before it is displayed -->
@@ -80,8 +77,8 @@
</script> </script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents"> <nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox"> <div class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded "><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li class="expanded "><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li class="expanded "><a href="0_2_naive_implementation.html" class="active"><strong aria-hidden="true">3.</strong> Naive example</a></li><li class="expanded "><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li class="expanded "><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li class="expanded "><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li class="expanded "><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: 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="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html" class="active"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
</div> </div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div> <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav> </nav>
@@ -297,125 +294,125 @@ fn main() {
executor_run(reactor, readylist); executor_run(reactor, readylist);
} }
<span class="boring">// ====== EXECUTOR ====== # // ====== EXECUTOR ======
</span><span class="boring">fn executor_run(mut reactor: Reactor, rl: Arc&lt;Mutex&lt;Vec&lt;usize&gt;&gt;&gt;) { # fn executor_run(mut reactor: Reactor, rl: Arc&lt;Mutex&lt;Vec&lt;usize&gt;&gt;&gt;) {
</span><span class="boring"> let start = Instant::now(); # let start = Instant::now();
</span><span class="boring"> loop { # loop {
</span><span class="boring"> let mut rl_locked = rl.lock().unwrap(); # let mut rl_locked = rl.lock().unwrap();
</span><span class="boring"> while let Some(event) = rl_locked.pop() { # while let Some(event) = rl_locked.pop() {
</span><span class="boring"> let dur = (Instant::now() - start).as_secs_f32(); # let dur = (Instant::now() - start).as_secs_f32();
</span><span class="boring"> println!(&quot;Event {} just happened at time: {:.2}.&quot;, event, dur); # println!(&quot;Event {} just happened at time: {:.2}.&quot;, event, dur);
</span><span class="boring"> reactor.outstanding.fetch_sub(1, Ordering::Relaxed); # reactor.outstanding.fetch_sub(1, Ordering::Relaxed);
</span><span class="boring"> } # }
</span><span class="boring"> drop(rl_locked); # drop(rl_locked);
</span><span class="boring"> #
</span><span class="boring"> if reactor.outstanding.load(Ordering::Relaxed) == 0 { # if reactor.outstanding.load(Ordering::Relaxed) == 0 {
</span><span class="boring"> reactor.close(); # reactor.close();
</span><span class="boring"> break; # break;
</span><span class="boring"> } # }
</span><span class="boring"> #
</span><span class="boring"> thread::park(); # thread::park();
</span><span class="boring"> } # }
</span><span class="boring">} # }
</span><span class="boring"> #
</span><span class="boring">// ====== &quot;FUTURE&quot; IMPL ====== # // ====== &quot;FUTURE&quot; IMPL ======
</span><span class="boring">#[derive(Debug)] # #[derive(Debug)]
</span><span class="boring">struct MyWaker { # struct MyWaker {
</span><span class="boring"> id: usize, # id: usize,
</span><span class="boring"> thread: thread::Thread, # thread: thread::Thread,
</span><span class="boring"> readylist: Arc&lt;Mutex&lt;Vec&lt;usize&gt;&gt;&gt;, # readylist: Arc&lt;Mutex&lt;Vec&lt;usize&gt;&gt;&gt;,
</span><span class="boring">} # }
</span><span class="boring"> #
</span><span class="boring">impl MyWaker { # impl MyWaker {
</span><span class="boring"> fn new(id: usize, thread: thread::Thread, readylist: Arc&lt;Mutex&lt;Vec&lt;usize&gt;&gt;&gt;) -&gt; Self { # fn new(id: usize, thread: thread::Thread, readylist: Arc&lt;Mutex&lt;Vec&lt;usize&gt;&gt;&gt;) -&gt; Self {
</span><span class="boring"> MyWaker { # MyWaker {
</span><span class="boring"> id, # id,
</span><span class="boring"> thread, # thread,
</span><span class="boring"> readylist, # readylist,
</span><span class="boring"> } # }
</span><span class="boring"> } # }
</span><span class="boring"> #
</span><span class="boring"> fn wake(&amp;self) { # fn wake(&amp;self) {
</span><span class="boring"> self.readylist.lock().map(|mut rl| rl.push(self.id)).unwrap(); # self.readylist.lock().map(|mut rl| rl.push(self.id)).unwrap();
</span><span class="boring"> self.thread.unpark(); # self.thread.unpark();
</span><span class="boring"> } # }
</span><span class="boring">} # }
</span><span class="boring"> #
</span><span class="boring"> #
</span><span class="boring">#[derive(Debug, Clone)] # #[derive(Debug, Clone)]
</span><span class="boring">pub struct Task { # pub struct Task {
</span><span class="boring"> id: usize, # id: usize,
</span><span class="boring"> pending: bool, # pending: bool,
</span><span class="boring">} # }
</span><span class="boring"> #
</span><span class="boring">// ===== REACTOR ===== # // ===== REACTOR =====
</span><span class="boring">struct Reactor { # struct Reactor {
</span><span class="boring"> dispatcher: Sender&lt;Event&gt;, # dispatcher: Sender&lt;Event&gt;,
</span><span class="boring"> handle: Option&lt;JoinHandle&lt;()&gt;&gt;, # handle: Option&lt;JoinHandle&lt;()&gt;&gt;,
</span><span class="boring"> outstanding: AtomicUsize, # outstanding: AtomicUsize,
</span><span class="boring">} # }
</span><span class="boring">#[derive(Debug)] # #[derive(Debug)]
</span><span class="boring">enum Event { # enum Event {
</span><span class="boring"> Close, # Close,
</span><span class="boring"> Simple(MyWaker, u64), # Simple(MyWaker, u64),
</span><span class="boring">} # }
</span><span class="boring"> #
</span><span class="boring">impl Reactor { # impl Reactor {
</span><span class="boring"> fn new() -&gt; Self { # fn new() -&gt; Self {
</span><span class="boring"> let (tx, rx) = channel::&lt;Event&gt;(); # let (tx, rx) = channel::&lt;Event&gt;();
</span><span class="boring"> let mut handles = vec![]; # let mut handles = vec![];
</span><span class="boring"> let handle = thread::spawn(move || { # let handle = thread::spawn(move || {
</span><span class="boring"> // This simulates some I/O resource # // This simulates some I/O resource
</span><span class="boring"> for event in rx { # for event in rx {
</span><span class="boring"> match event { # match event {
</span><span class="boring"> Event::Close =&gt; break, # Event::Close =&gt; break,
</span><span class="boring"> Event::Simple(mywaker, duration) =&gt; { # Event::Simple(mywaker, duration) =&gt; {
</span><span class="boring"> let event_handle = thread::spawn(move || { # let event_handle = thread::spawn(move || {
</span><span class="boring"> thread::sleep(Duration::from_secs(duration)); # thread::sleep(Duration::from_secs(duration));
</span><span class="boring"> mywaker.wake(); # mywaker.wake();
</span><span class="boring"> }); # });
</span><span class="boring"> handles.push(event_handle); # handles.push(event_handle);
</span><span class="boring"> } # }
</span><span class="boring"> } # }
</span><span class="boring"> } # }
</span><span class="boring"> #
</span><span class="boring"> for handle in handles { # for handle in handles {
</span><span class="boring"> handle.join().unwrap(); # handle.join().unwrap();
</span><span class="boring"> } # }
</span><span class="boring"> }); # });
</span><span class="boring"> #
</span><span class="boring"> Reactor { # Reactor {
</span><span class="boring"> dispatcher: tx, # dispatcher: tx,
</span><span class="boring"> handle: Some(handle), # handle: Some(handle),
</span><span class="boring"> outstanding: AtomicUsize::new(0), # outstanding: AtomicUsize::new(0),
</span><span class="boring"> } # }
</span><span class="boring"> } # }
</span><span class="boring"> #
</span><span class="boring"> fn register(&amp;mut self, duration: u64, mywaker: MyWaker) { # fn register(&amp;mut self, duration: u64, mywaker: MyWaker) {
</span><span class="boring"> self.dispatcher # self.dispatcher
</span><span class="boring"> .send(Event::Simple(mywaker, duration)) # .send(Event::Simple(mywaker, duration))
</span><span class="boring"> .unwrap(); # .unwrap();
</span><span class="boring"> self.outstanding.fetch_add(1, Ordering::Relaxed); # self.outstanding.fetch_add(1, Ordering::Relaxed);
</span><span class="boring"> } # }
</span><span class="boring"> #
</span><span class="boring"> fn close(&amp;mut self) { # fn close(&amp;mut self) {
</span><span class="boring"> self.dispatcher.send(Event::Close).unwrap(); # self.dispatcher.send(Event::Close).unwrap();
</span><span class="boring"> } # }
</span><span class="boring">} # }
</span><span class="boring"> #
</span><span class="boring">impl Drop for Reactor { # impl Drop for Reactor {
</span><span class="boring"> fn drop(&amp;mut self) { # fn drop(&amp;mut self) {
</span><span class="boring"> self.handle.take().map(|h| h.join().unwrap()).unwrap(); # self.handle.take().map(|h| h.join().unwrap()).unwrap();
</span><span class="boring"> } # }
</span><span class="boring">} # }
</span></code></pre></pre> </code></pre></pre>
</main> </main>
<nav class="nav-wrapper" aria-label="Page navigation"> <nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons --> <!-- Mobile navigation buttons -->
<a rel="prev" href="0_1_background_information.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left"> <a rel="prev" href="0_1_2_generators_pin.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i> <i class="fa fa-angle-left"></i>
</a> </a>
@@ -433,7 +430,7 @@ fn main() {
<nav class="nav-wide-wrapper" aria-label="Page navigation"> <nav class="nav-wide-wrapper" aria-label="Page navigation">
<a href="0_1_background_information.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left"> <a href="0_1_2_generators_pin.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i> <i class="fa fa-angle-left"></i>
</a> </a>
@@ -448,30 +445,7 @@ fn main() {
</div> </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 type="text/javascript">
window.playpen_copyable = true;
</script>

View File

@@ -1,5 +1,5 @@
<!DOCTYPE HTML> <!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light"> <html lang="en" class="sidebar-visible no-js">
<head> <head>
<!-- Book generated using mdBook --> <!-- Book generated using mdBook -->
<meta charset="UTF-8"> <meta charset="UTF-8">
@@ -32,11 +32,11 @@
</head> </head>
<body> <body class="light">
<!-- Provide site root to javascript --> <!-- Provide site root to javascript -->
<script type="text/javascript"> <script type="text/javascript">
var path_to_root = ""; var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light"; var default_theme = "light";
</script> </script>
<!-- Work around some values being stored in localStorage wrapped in quotes --> <!-- Work around some values being stored in localStorage wrapped in quotes -->
@@ -60,11 +60,8 @@
var theme; var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { } try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; } if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html'); document.body.className = theme;
html.classList.remove('no-js') document.querySelector('html').className = theme + ' js';
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script> </script>
<!-- Hide / unhide sidebar before it is displayed --> <!-- Hide / unhide sidebar before it is displayed -->
@@ -80,8 +77,8 @@
</script> </script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents"> <nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox"> <div class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded "><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li class="expanded "><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li class="expanded "><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li class="expanded "><a href="0_3_proper_waker.html" class="active"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li class="expanded "><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li class="expanded "><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li class="expanded "><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: 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="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html" class="active"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
</div> </div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div> <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav> </nav>
@@ -188,30 +185,7 @@
</div> </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 type="text/javascript">
window.playpen_copyable = true;
</script>

View File

@@ -1,5 +1,5 @@
<!DOCTYPE HTML> <!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light"> <html lang="en" class="sidebar-visible no-js">
<head> <head>
<!-- Book generated using mdBook --> <!-- Book generated using mdBook -->
<meta charset="UTF-8"> <meta charset="UTF-8">
@@ -32,11 +32,11 @@
</head> </head>
<body> <body class="light">
<!-- Provide site root to javascript --> <!-- Provide site root to javascript -->
<script type="text/javascript"> <script type="text/javascript">
var path_to_root = ""; var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light"; var default_theme = "light";
</script> </script>
<!-- Work around some values being stored in localStorage wrapped in quotes --> <!-- Work around some values being stored in localStorage wrapped in quotes -->
@@ -60,11 +60,8 @@
var theme; var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { } try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; } if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html'); document.body.className = theme;
html.classList.remove('no-js') document.querySelector('html').className = theme + ' js';
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script> </script>
<!-- Hide / unhide sidebar before it is displayed --> <!-- Hide / unhide sidebar before it is displayed -->
@@ -80,8 +77,8 @@
</script> </script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents"> <nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox"> <div class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded "><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li class="expanded "><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li class="expanded "><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li class="expanded "><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li class="expanded "><a href="0_4_proper_future.html" class="active"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li class="expanded "><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li class="expanded "><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: 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="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html" class="active"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
</div> </div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div> <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav> </nav>
@@ -188,30 +185,7 @@
</div> </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 type="text/javascript">
window.playpen_copyable = true;
</script>

View File

@@ -1,5 +1,5 @@
<!DOCTYPE HTML> <!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light"> <html lang="en" class="sidebar-visible no-js">
<head> <head>
<!-- Book generated using mdBook --> <!-- Book generated using mdBook -->
<meta charset="UTF-8"> <meta charset="UTF-8">
@@ -32,11 +32,11 @@
</head> </head>
<body> <body class="light">
<!-- Provide site root to javascript --> <!-- Provide site root to javascript -->
<script type="text/javascript"> <script type="text/javascript">
var path_to_root = ""; var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light"; var default_theme = "light";
</script> </script>
<!-- Work around some values being stored in localStorage wrapped in quotes --> <!-- Work around some values being stored in localStorage wrapped in quotes -->
@@ -60,11 +60,8 @@
var theme; var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { } try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; } if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html'); document.body.className = theme;
html.classList.remove('no-js') document.querySelector('html').className = theme + ' js';
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script> </script>
<!-- Hide / unhide sidebar before it is displayed --> <!-- Hide / unhide sidebar before it is displayed -->
@@ -80,8 +77,8 @@
</script> </script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents"> <nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox"> <div class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded "><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li class="expanded "><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li class="expanded "><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li class="expanded "><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li class="expanded "><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li class="expanded "><a href="0_5_async_wait.html" class="active"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li class="expanded "><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: 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="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html" class="active"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
</div> </div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div> <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav> </nav>
@@ -188,30 +185,7 @@
</div> </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 type="text/javascript">
window.playpen_copyable = true;
</script>

View File

@@ -1,5 +1,5 @@
<!DOCTYPE HTML> <!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light"> <html lang="en" class="sidebar-visible no-js">
<head> <head>
<!-- Book generated using mdBook --> <!-- Book generated using mdBook -->
<meta charset="UTF-8"> <meta charset="UTF-8">
@@ -32,11 +32,11 @@
</head> </head>
<body> <body class="light">
<!-- Provide site root to javascript --> <!-- Provide site root to javascript -->
<script type="text/javascript"> <script type="text/javascript">
var path_to_root = ""; var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light"; var default_theme = "light";
</script> </script>
<!-- Work around some values being stored in localStorage wrapped in quotes --> <!-- Work around some values being stored in localStorage wrapped in quotes -->
@@ -60,11 +60,8 @@
var theme; var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { } try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; } if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html'); document.body.className = theme;
html.classList.remove('no-js') document.querySelector('html').className = theme + ' js';
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script> </script>
<!-- Hide / unhide sidebar before it is displayed --> <!-- Hide / unhide sidebar before it is displayed -->
@@ -80,8 +77,8 @@
</script> </script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents"> <nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox"> <div class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded "><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li class="expanded "><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li class="expanded "><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li class="expanded "><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li class="expanded "><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li class="expanded "><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li class="expanded "><a href="0_6_concurrent_futures.html" class="active"><strong aria-hidden="true">7.</strong> Bonus: 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="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html" class="active"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
</div> </div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div> <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav> </nav>
@@ -180,30 +177,7 @@
</div> </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 type="text/javascript">
window.playpen_copyable = true;
</script>

View File

@@ -16,6 +16,9 @@ function playpen_text(playpen) {
} }
(function codeSnippets() { (function codeSnippets() {
// Hide Rust code lines prepended with a specific character
var hiding_character = "#";
function fetch_with_timeout(url, options, timeout = 6000) { function fetch_with_timeout(url, options, timeout = 6000) {
return Promise.race([ return Promise.race([
fetch(url, options), fetch(url, options),
@@ -52,15 +55,6 @@ function playpen_text(playpen) {
editor.addEventListener("change", function (e) { editor.addEventListener("change", function (e) {
update_play_button(playpen_block, playground_crates); update_play_button(playpen_block, playground_crates);
}); });
// add Ctrl-Enter command to execute rust code
editor.commands.addCommand({
name: "run",
bindKey: {
win: "Ctrl-Enter",
mac: "Ctrl-Enter"
},
exec: _editor => run_rust_code(playpen_block)
});
} }
} }
} }
@@ -167,59 +161,95 @@ function playpen_text(playpen) {
Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) { Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) {
var lines = Array.from(block.querySelectorAll('.boring')); var code_block = block;
var pre_block = block.parentNode;
// hide lines
var lines = code_block.innerHTML.split("\n");
var first_non_hidden_line = false;
var lines_hidden = false;
var trimmed_line = "";
for (var n = 0; n < lines.length; n++) {
trimmed_line = lines[n].trim();
if (trimmed_line[0] == hiding_character && trimmed_line[1] != hiding_character) {
if (first_non_hidden_line) {
lines[n] = "<span class=\"hidden\">" + "\n" + lines[n].replace(/(\s*)# ?/, "$1") + "</span>";
}
else {
lines[n] = "<span class=\"hidden\">" + lines[n].replace(/(\s*)# ?/, "$1") + "\n" + "</span>";
}
lines_hidden = true;
}
else if (first_non_hidden_line) {
lines[n] = "\n" + lines[n];
}
else {
first_non_hidden_line = true;
}
if (trimmed_line[0] == hiding_character && trimmed_line[1] == hiding_character) {
lines[n] = lines[n].replace("##", "#")
}
}
code_block.innerHTML = lines.join("");
// If no lines were hidden, return // If no lines were hidden, return
if (!lines.length) { return; } if (!lines_hidden) { return; }
block.classList.add("hide-boring");
var buttons = document.createElement('div'); var buttons = document.createElement('div');
buttons.className = 'buttons'; buttons.className = 'buttons';
buttons.innerHTML = "<button class=\"fa fa-expand\" title=\"Show hidden lines\" aria-label=\"Show hidden lines\"></button>"; buttons.innerHTML = "<button class=\"fa fa-expand\" title=\"Show hidden lines\" aria-label=\"Show hidden lines\"></button>";
// add expand button // add expand button
var pre_block = block.parentNode;
pre_block.insertBefore(buttons, pre_block.firstChild); pre_block.insertBefore(buttons, pre_block.firstChild);
pre_block.querySelector('.buttons').addEventListener('click', function (e) { pre_block.querySelector('.buttons').addEventListener('click', function (e) {
if (e.target.classList.contains('fa-expand')) { if (e.target.classList.contains('fa-expand')) {
var lines = pre_block.querySelectorAll('span.hidden');
e.target.classList.remove('fa-expand'); e.target.classList.remove('fa-expand');
e.target.classList.add('fa-compress'); e.target.classList.add('fa-compress');
e.target.title = 'Hide lines'; e.target.title = 'Hide lines';
e.target.setAttribute('aria-label', e.target.title); e.target.setAttribute('aria-label', e.target.title);
block.classList.remove('hide-boring'); Array.from(lines).forEach(function (line) {
line.classList.remove('hidden');
line.classList.add('unhidden');
});
} else if (e.target.classList.contains('fa-compress')) { } else if (e.target.classList.contains('fa-compress')) {
var lines = pre_block.querySelectorAll('span.unhidden');
e.target.classList.remove('fa-compress'); e.target.classList.remove('fa-compress');
e.target.classList.add('fa-expand'); e.target.classList.add('fa-expand');
e.target.title = 'Show hidden lines'; e.target.title = 'Show hidden lines';
e.target.setAttribute('aria-label', e.target.title); e.target.setAttribute('aria-label', e.target.title);
block.classList.add('hide-boring'); Array.from(lines).forEach(function (line) {
line.classList.remove('unhidden');
line.classList.add('hidden');
});
} }
}); });
}); });
if (window.playpen_copyable) { Array.from(document.querySelectorAll('pre code')).forEach(function (block) {
Array.from(document.querySelectorAll('pre code')).forEach(function (block) { var pre_block = block.parentNode;
var pre_block = block.parentNode; if (!pre_block.classList.contains('playpen')) {
if (!pre_block.classList.contains('playpen')) { var buttons = pre_block.querySelector(".buttons");
var buttons = pre_block.querySelector(".buttons"); if (!buttons) {
if (!buttons) { buttons = document.createElement('div');
buttons = document.createElement('div'); buttons.className = 'buttons';
buttons.className = 'buttons'; pre_block.insertBefore(buttons, pre_block.firstChild);
pre_block.insertBefore(buttons, pre_block.firstChild);
}
var clipButton = document.createElement('button');
clipButton.className = 'fa fa-copy clip-button';
clipButton.title = 'Copy to clipboard';
clipButton.setAttribute('aria-label', clipButton.title);
clipButton.innerHTML = '<i class=\"tooltiptext\"></i>';
buttons.insertBefore(clipButton, buttons.firstChild);
} }
});
} var clipButton = document.createElement('button');
clipButton.className = 'fa fa-copy clip-button';
clipButton.title = 'Copy to clipboard';
clipButton.setAttribute('aria-label', clipButton.title);
clipButton.innerHTML = '<i class=\"tooltiptext\"></i>';
buttons.insertBefore(clipButton, buttons.firstChild);
}
});
// Process playpen code blocks // Process playpen code blocks
Array.from(document.querySelectorAll(".playpen")).forEach(function (pre_block) { Array.from(document.querySelectorAll(".playpen")).forEach(function (pre_block) {
@@ -237,21 +267,19 @@ function playpen_text(playpen) {
runCodeButton.title = 'Run this code'; runCodeButton.title = 'Run this code';
runCodeButton.setAttribute('aria-label', runCodeButton.title); runCodeButton.setAttribute('aria-label', runCodeButton.title);
var copyCodeClipboardButton = document.createElement('button');
copyCodeClipboardButton.className = 'fa fa-copy clip-button';
copyCodeClipboardButton.innerHTML = '<i class="tooltiptext"></i>';
copyCodeClipboardButton.title = 'Copy to clipboard';
copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
buttons.insertBefore(runCodeButton, buttons.firstChild); buttons.insertBefore(runCodeButton, buttons.firstChild);
buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild);
runCodeButton.addEventListener('click', function (e) { runCodeButton.addEventListener('click', function (e) {
run_rust_code(pre_block); run_rust_code(pre_block);
}); });
if (window.playpen_copyable) {
var copyCodeClipboardButton = document.createElement('button');
copyCodeClipboardButton.className = 'fa fa-copy clip-button';
copyCodeClipboardButton.innerHTML = '<i class="tooltiptext"></i>';
copyCodeClipboardButton.title = 'Copy to clipboard';
copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild);
}
let code_block = pre_block.querySelector("code"); let code_block = pre_block.querySelector("code");
if (window.ace && code_block.classList.contains("editable")) { if (window.ace && code_block.classList.contains("editable")) {
var undoChangesButton = document.createElement('button'); var undoChangesButton = document.createElement('button');
@@ -293,7 +321,7 @@ function playpen_text(playpen) {
themeToggleButton.focus(); themeToggleButton.focus();
} }
function set_theme(theme, store = true) { function set_theme(theme) {
let ace_theme; let ace_theme;
if (theme == 'coal' || theme == 'navy') { if (theme == 'coal' || theme == 'navy') {
@@ -328,10 +356,9 @@ function playpen_text(playpen) {
try { previousTheme = localStorage.getItem('mdbook-theme'); } catch (e) { } try { previousTheme = localStorage.getItem('mdbook-theme'); } catch (e) { }
if (previousTheme === null || previousTheme === undefined) { previousTheme = default_theme; } if (previousTheme === null || previousTheme === undefined) { previousTheme = default_theme; }
if (store) { try { localStorage.setItem('mdbook-theme', theme); } catch (e) { }
try { localStorage.setItem('mdbook-theme', theme); } catch (e) { }
}
document.body.className = theme;
html.classList.remove(previousTheme); html.classList.remove(previousTheme);
html.classList.add(theme); html.classList.add(theme);
} }
@@ -341,7 +368,7 @@ function playpen_text(playpen) {
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { } try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; } if (theme === null || theme === undefined) { theme = default_theme; }
set_theme(theme, false); set_theme(theme);
themeToggleButton.addEventListener('click', function () { themeToggleButton.addEventListener('click', function () {
if (themePopup.style.display === 'block') { if (themePopup.style.display === 'block') {
@@ -363,7 +390,7 @@ function playpen_text(playpen) {
} }
}); });
// Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628 // Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang-nursery/mdBook/issues/628
document.addEventListener('click', function(e) { document.addEventListener('click', function(e) {
if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) { if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) {
hideThemes(); hideThemes();
@@ -408,7 +435,6 @@ function playpen_text(playpen) {
(function sidebar() { (function sidebar() {
var html = document.querySelector("html"); var html = document.querySelector("html");
var sidebar = document.getElementById("sidebar"); var sidebar = document.getElementById("sidebar");
var sidebarScrollBox = document.getElementById("sidebar-scrollbox");
var sidebarLinks = document.querySelectorAll('#sidebar a'); var sidebarLinks = document.querySelectorAll('#sidebar a');
var sidebarToggleButton = document.getElementById("sidebar-toggle"); var sidebarToggleButton = document.getElementById("sidebar-toggle");
var sidebarResizeHandle = document.getElementById("sidebar-resize-handle"); var sidebarResizeHandle = document.getElementById("sidebar-resize-handle");
@@ -425,17 +451,6 @@ function playpen_text(playpen) {
try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { } try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
} }
var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle');
function toggleSection(ev) {
ev.currentTarget.parentElement.classList.toggle('expanded');
}
Array.from(sidebarAnchorToggles).forEach(function (el) {
el.addEventListener('click', toggleSection);
});
function hideSidebar() { function hideSidebar() {
html.classList.remove('sidebar-visible') html.classList.remove('sidebar-visible')
html.classList.add('sidebar-hidden'); html.classList.add('sidebar-hidden');
@@ -507,7 +522,7 @@ function playpen_text(playpen) {
// Scroll sidebar to current active section // Scroll sidebar to current active section
var activeSection = sidebar.querySelector(".active"); var activeSection = sidebar.querySelector(".active");
if (activeSection) { if (activeSection) {
sidebarScrollBox.scrollTop = activeSection.offsetTop; sidebar.scrollTop = activeSection.offsetTop;
} }
})(); })();
@@ -600,6 +615,6 @@ function playpen_text(playpen) {
menu.classList.remove('bordered'); menu.classList.remove('bordered');
} }
previousScrollTop = Math.max(document.scrollingElement.scrollTop, 0); previousScrollTop = document.scrollingElement.scrollTop;
}, { passive: true }); }, { passive: true });
})(); })();

View File

@@ -8,9 +8,7 @@
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
background: var(--scrollbar); background: var(--scrollbar);
} }
html {
scrollbar-color: var(--scrollbar) var(--bg);
}
#searchresults a, #searchresults a,
.content a:link, .content a:link,
a:visited, a:visited,
@@ -45,7 +43,7 @@ a > .hljs {
position: relative; position: relative;
padding: 0 8px; padding: 0 8px;
z-index: 10; z-index: 10;
line-height: var(--menu-bar-height); line-height: 50px;
cursor: pointer; cursor: pointer;
transition: color 0.5s; transition: color 0.5s;
} }
@@ -73,7 +71,7 @@ a > .hljs {
} }
html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-container { html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-container {
transform: translateY(calc(-10px - var(--menu-bar-height))); transform: translateY(-60px);
} }
.left-buttons { .left-buttons {
@@ -87,8 +85,8 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
.menu-title { .menu-title {
display: inline-block; display: inline-block;
font-weight: 200; font-weight: 200;
font-size: 2rem; font-size: 20px;
line-height: var(--menu-bar-height); line-height: 50px;
text-align: center; text-align: center;
margin: 0; margin: 0;
flex: 1; flex: 1;
@@ -126,7 +124,7 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
text-decoration: none; text-decoration: none;
position: fixed; position: fixed;
top: 0; top: 50px; /* Height of menu-bar */
bottom: 0; bottom: 0;
margin: 0; margin: 0;
max-width: 150px; max-width: 150px;
@@ -137,14 +135,10 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
align-content: center; align-content: center;
flex-direction: column; flex-direction: column;
transition: color 0.5s, background-color 0.5s; transition: color 0.5s;
} }
.nav-chapters:hover { .nav-chapters:hover { text-decoration: none; }
text-decoration: none;
background-color: var(--theme-hover);
transition: background-color 0.15s, color 0.15s;
}
.nav-wrapper { .nav-wrapper {
margin-top: 50px; margin-top: 50px;
@@ -182,7 +176,8 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta
/* Inline code */ /* Inline code */
:not(pre) > .hljs { :not(pre) > .hljs {
display: inline; display: inline-block;
vertical-align: middle;
padding: 0.1em 0.3em; padding: 0.1em 0.3em;
border-radius: 3px; border-radius: 3px;
} }
@@ -375,13 +370,7 @@ ul#searchresults span.teaser em {
padding-left: 0; padding-left: 0;
line-height: 2.2em; line-height: 2.2em;
} }
.chapter ol {
width: 100%;
}
.chapter li { .chapter li {
display: flex;
color: var(--sidebar-non-existant); color: var(--sidebar-non-existant);
} }
.chapter li a { .chapter li a {
@@ -395,32 +384,10 @@ ul#searchresults span.teaser em {
color: var(--sidebar-active); color: var(--sidebar-active);
} }
.chapter li a.active { .chapter li .active {
color: var(--sidebar-active); color: var(--sidebar-active);
} }
.chapter li > a.toggle {
cursor: pointer;
display: block;
margin-left: auto;
padding: 0 10px;
user-select: none;
opacity: 0.68;
}
.chapter li > a.toggle div {
transition: transform 0.5s;
}
/* collapse the section */
.chapter li:not(.expanded) + li > ol {
display: none;
}
.chapter li.expanded > a.toggle div {
transform: rotate(90deg);
}
.spacer { .spacer {
width: 100%; width: 100%;
height: 3px; height: 3px;
@@ -446,7 +413,7 @@ ul#searchresults span.teaser em {
.theme-popup { .theme-popup {
position: absolute; position: absolute;
left: 10px; left: 10px;
top: var(--menu-bar-height); top: 50px;
z-index: 1000; z-index: 1000;
border-radius: 4px; border-radius: 4px;
font-size: 0.7em; font-size: 0.7em;

View File

@@ -2,11 +2,6 @@
@import 'variables.css'; @import 'variables.css';
:root {
/* Browser default font-size is 16px, this way 1 rem = 10px */
font-size: 62.5%;
}
html { html {
font-family: "Open Sans", sans-serif; font-family: "Open Sans", sans-serif;
color: var(--fg); color: var(--fg);
@@ -16,20 +11,19 @@ html {
body { body {
margin: 0; margin: 0;
font-size: 1.6rem; font-size: 1rem;
overflow-x: hidden; overflow-x: hidden;
} }
code { code {
font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace !important; font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace;
font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */ font-size: 0.875em; /* please adjust the ace font size accordingly in editor.js */
} }
.left { float: left; } .left { float: left; }
.right { float: right; } .right { float: right; }
.boring { opacity: 0.6; }
.hide-boring .boring { display: none; }
.hidden { display: none; } .hidden { display: none; }
.play-button.hidden { display: none; }
h2, h3 { margin-top: 2.5em; } h2, h3 { margin-top: 2.5em; }
h4, h5 { margin-top: 2em; } h4, h5 { margin-top: 2em; }
@@ -50,13 +44,6 @@ h4 a.header:target::before {
width: 30px; width: 30px;
} }
h1 a.header:target,
h2 a.header:target,
h3 a.header:target,
h4 a.header:target {
scroll-margin-top: calc(var(--menu-bar-height) + 0.5em);
}
.page { .page {
outline: 0; outline: 0;
padding: 0 var(--page-padding); padding: 0 var(--page-padding);
@@ -105,9 +92,6 @@ table thead td {
font-weight: 700; font-weight: 700;
border: none; border: none;
} }
table thead th {
padding: 3px 20px;
}
table thead tr { table thead tr {
border: 1px var(--table-header-bg) solid; border: 1px var(--table-header-bg) solid;
} }

View File

@@ -5,7 +5,6 @@
--sidebar-width: 300px; --sidebar-width: 300px;
--page-padding: 15px; --page-padding: 15px;
--content-max-width: 750px; --content-max-width: 750px;
--menu-bar-height: 50px;
} }
/* Themes */ /* Themes */
@@ -209,45 +208,3 @@
--searchresults-li-bg: #dec2a2; --searchresults-li-bg: #dec2a2;
--search-mark-bg: #e69f67; --search-mark-bg: #e69f67;
} }
@media (prefers-color-scheme: dark) {
.light.no-js {
--bg: hsl(200, 7%, 8%);
--fg: #98a3ad;
--sidebar-bg: #292c2f;
--sidebar-fg: #a1adb8;
--sidebar-non-existant: #505254;
--sidebar-active: #3473ad;
--sidebar-spacer: #393939;
--scrollbar: var(--sidebar-fg);
--icons: #43484d;
--icons-hover: #b3c0cc;
--links: #2b79a2;
--inline-code-color: #c5c8c6;;
--theme-popup-bg: #141617;
--theme-popup-border: #43484d;
--theme-hover: #1f2124;
--quote-bg: hsl(234, 21%, 18%);
--quote-border: hsl(234, 21%, 23%);
--table-border-color: hsl(200, 7%, 13%);
--table-header-bg: hsl(200, 7%, 28%);
--table-alternate-bg: hsl(200, 7%, 11%);
--searchbar-border-color: #aaa;
--searchbar-bg: #b7b7b7;
--searchbar-fg: #000;
--searchbar-shadow-color: #aaa;
--searchresults-header-fg: #666;
--searchresults-border-color: #98a3ad;
--searchresults-li-bg: #2b2b2f;
--search-mark-bg: #355c7d;
}
}

View File

@@ -6,14 +6,12 @@ window.editors = [];
} }
Array.from(document.querySelectorAll('.editable')).forEach(function(editable) { Array.from(document.querySelectorAll('.editable')).forEach(function(editable) {
let display_line_numbers = window.playpen_line_numbers || false;
let editor = ace.edit(editable); let editor = ace.edit(editable);
editor.setOptions({ editor.setOptions({
highlightActiveLine: false, highlightActiveLine: false,
showPrintMargin: false, showPrintMargin: false,
showLineNumbers: display_line_numbers, showLineNumbers: false,
showGutter: display_line_numbers, showGutter: false,
maxLines: Infinity, maxLines: Infinity,
fontSize: "0.875em" // please adjust the font size of the code in general.css fontSize: "0.875em" // please adjust the font size of the code in general.css
}); });

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
<!DOCTYPE HTML> <!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light"> <html lang="en" class="sidebar-visible no-js">
<head> <head>
<!-- Book generated using mdBook --> <!-- Book generated using mdBook -->
<meta charset="UTF-8"> <meta charset="UTF-8">
@@ -32,11 +32,11 @@
</head> </head>
<body> <body class="light">
<!-- Provide site root to javascript --> <!-- Provide site root to javascript -->
<script type="text/javascript"> <script type="text/javascript">
var path_to_root = ""; var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light"; var default_theme = "light";
</script> </script>
<!-- Work around some values being stored in localStorage wrapped in quotes --> <!-- Work around some values being stored in localStorage wrapped in quotes -->
@@ -60,11 +60,8 @@
var theme; var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { } try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; } if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html'); document.body.className = theme;
html.classList.remove('no-js') document.querySelector('html').className = theme + ' js';
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script> </script>
<!-- Hide / unhide sidebar before it is displayed --> <!-- Hide / unhide sidebar before it is displayed -->
@@ -80,8 +77,8 @@
</script> </script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents"> <nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox"> <div class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded "><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li class="expanded "><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li class="expanded "><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li class="expanded "><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li class="expanded "><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li class="expanded "><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li class="expanded "><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: 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="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
</div> </div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div> <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav> </nav>
@@ -148,7 +145,77 @@
<div id="content" class="content"> <div id="content" class="content">
<main> <main>
<h1><a class="header" href="#introduction" id="introduction">Introduction</a></h1> <h1><a class="header" href="#futures-explained-in-200-lines-of-rust" id="futures-explained-in-200-lines-of-rust">Futures Explained in 200 Lines of Rust</a></h1>
<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
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
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>
<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>
</main> </main>
@@ -157,10 +224,6 @@
<a rel="next" href="0_1_background_information.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> <div style="clear: both"></div>
</nav> </nav>
@@ -171,39 +234,12 @@
<a href="0_1_background_information.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav> </nav>
</div> </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 type="text/javascript">
window.playpen_copyable = true;
</script>

View File

@@ -1,5 +1,5 @@
<!DOCTYPE HTML> <!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light"> <html lang="en" class="sidebar-visible no-js">
<head> <head>
<!-- Book generated using mdBook --> <!-- Book generated using mdBook -->
<meta charset="UTF-8"> <meta charset="UTF-8">
@@ -34,11 +34,11 @@
</head> </head>
<body> <body class="light">
<!-- Provide site root to javascript --> <!-- Provide site root to javascript -->
<script type="text/javascript"> <script type="text/javascript">
var path_to_root = ""; var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light"; var default_theme = "light";
</script> </script>
<!-- Work around some values being stored in localStorage wrapped in quotes --> <!-- Work around some values being stored in localStorage wrapped in quotes -->
@@ -62,11 +62,8 @@
var theme; var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { } try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; } if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html'); document.body.className = theme;
html.classList.remove('no-js') document.querySelector('html').className = theme + ' js';
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script> </script>
<!-- Hide / unhide sidebar before it is displayed --> <!-- Hide / unhide sidebar before it is displayed -->
@@ -82,8 +79,8 @@
</script> </script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents"> <nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div id="sidebar-scrollbox" class="sidebar-scrollbox"> <div class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded "><a href="0_0_introduction.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li class="expanded "><a href="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li class="expanded "><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li class="expanded "><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li class="expanded "><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li class="expanded "><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li class="expanded "><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: 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="0_1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><ol class="section"><li><a href="0_1_1_trait_objects.html"><strong aria-hidden="true">2.1.</strong> Trait objects and fat pointers</a></li><li><a href="0_1_2_generators_pin.html"><strong aria-hidden="true">2.2.</strong> Generators and Pin</a></li></ol></li><li><a href="0_2_naive_implementation.html"><strong aria-hidden="true">3.</strong> Naive example</a></li><li><a href="0_3_proper_waker.html"><strong aria-hidden="true">4.</strong> Proper Waker</a></li><li><a href="0_4_proper_future.html"><strong aria-hidden="true">5.</strong> Proper Future</a></li><li><a href="0_5_async_wait.html"><strong aria-hidden="true">6.</strong> Supporting async/await</a></li><li><a href="0_6_concurrent_futures.html"><strong aria-hidden="true">7.</strong> Bonus: concurrent futures</a></li></ol>
</div> </div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div> <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav> </nav>
@@ -150,7 +147,77 @@
<div id="content" class="content"> <div id="content" class="content">
<main> <main>
<h1><a class="header" href="#introduction" id="introduction">Introduction</a></h1> <h1><a class="header" href="#futures-explained-in-200-lines-of-rust" id="futures-explained-in-200-lines-of-rust">Futures Explained in 200 Lines of Rust</a></h1>
<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
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
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>
<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>
<h1><a class="header" href="#some-background-information" id="some-background-information">Some background information</a></h1> <h1><a class="header" href="#some-background-information" id="some-background-information">Some background information</a></h1>
<p>Before we start implementing our <code>Futures</code> , we'll go through some background <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> information that will help demystify some of the concepts we encounter.</p>
@@ -176,8 +243,8 @@ object we construct our selves.</p>
<h2><a class="header" href="#fat-pointers-in-rust" id="fat-pointers-in-rust">Fat pointers in Rust</a></h2> <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 <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:</p>
<pre><pre class="playpen"><code class="language-rust"><span class="boring">use std::mem::size_of; <pre><pre class="playpen"><code class="language-rust"># use std::mem::size_of;
</span>trait SomeTrait { } trait SomeTrait { }
fn main() { fn main() {
println!(&quot;Size of Box&lt;i32&gt;: {}&quot;, size_of::&lt;Box&lt;i32&gt;&gt;()); println!(&quot;Size of Box&lt;i32&gt;: {}&quot;, size_of::&lt;Box&lt;i32&gt;&gt;());
@@ -330,6 +397,8 @@ 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 <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> 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> <p>With that out of the way, let's move on to our main example.</p>
<h1><a class="header" href="#trait-objects-and-fat-pointers" id="trait-objects-and-fat-pointers">Trait objects and fat pointers</a></h1>
<h1><a class="header" href="#generators-and-pin" id="generators-and-pin">Generators and Pin</a></h1>
<h1><a class="header" href="#naive-example" id="naive-example">Naive example</a></h1> <h1><a class="header" href="#naive-example" id="naive-example">Naive example</a></h1>
<pre><pre class="playpen"><code class="language-rust">use std::sync::atomic::{AtomicUsize, Ordering}; <pre><pre class="playpen"><code class="language-rust">use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc::{channel, Sender}; use std::sync::mpsc::{channel, Sender};
@@ -479,118 +548,118 @@ fn main() {
executor_run(reactor, readylist); executor_run(reactor, readylist);
} }
<span class="boring">// ====== EXECUTOR ====== # // ====== EXECUTOR ======
</span><span class="boring">fn executor_run(mut reactor: Reactor, rl: Arc&lt;Mutex&lt;Vec&lt;usize&gt;&gt;&gt;) { # fn executor_run(mut reactor: Reactor, rl: Arc&lt;Mutex&lt;Vec&lt;usize&gt;&gt;&gt;) {
</span><span class="boring"> let start = Instant::now(); # let start = Instant::now();
</span><span class="boring"> loop { # loop {
</span><span class="boring"> let mut rl_locked = rl.lock().unwrap(); # let mut rl_locked = rl.lock().unwrap();
</span><span class="boring"> while let Some(event) = rl_locked.pop() { # while let Some(event) = rl_locked.pop() {
</span><span class="boring"> let dur = (Instant::now() - start).as_secs_f32(); # let dur = (Instant::now() - start).as_secs_f32();
</span><span class="boring"> println!(&quot;Event {} just happened at time: {:.2}.&quot;, event, dur); # println!(&quot;Event {} just happened at time: {:.2}.&quot;, event, dur);
</span><span class="boring"> reactor.outstanding.fetch_sub(1, Ordering::Relaxed); # reactor.outstanding.fetch_sub(1, Ordering::Relaxed);
</span><span class="boring"> } # }
</span><span class="boring"> drop(rl_locked); # drop(rl_locked);
</span><span class="boring"> #
</span><span class="boring"> if reactor.outstanding.load(Ordering::Relaxed) == 0 { # if reactor.outstanding.load(Ordering::Relaxed) == 0 {
</span><span class="boring"> reactor.close(); # reactor.close();
</span><span class="boring"> break; # break;
</span><span class="boring"> } # }
</span><span class="boring"> #
</span><span class="boring"> thread::park(); # thread::park();
</span><span class="boring"> } # }
</span><span class="boring">} # }
</span><span class="boring"> #
</span><span class="boring">// ====== &quot;FUTURE&quot; IMPL ====== # // ====== &quot;FUTURE&quot; IMPL ======
</span><span class="boring">#[derive(Debug)] # #[derive(Debug)]
</span><span class="boring">struct MyWaker { # struct MyWaker {
</span><span class="boring"> id: usize, # id: usize,
</span><span class="boring"> thread: thread::Thread, # thread: thread::Thread,
</span><span class="boring"> readylist: Arc&lt;Mutex&lt;Vec&lt;usize&gt;&gt;&gt;, # readylist: Arc&lt;Mutex&lt;Vec&lt;usize&gt;&gt;&gt;,
</span><span class="boring">} # }
</span><span class="boring"> #
</span><span class="boring">impl MyWaker { # impl MyWaker {
</span><span class="boring"> fn new(id: usize, thread: thread::Thread, readylist: Arc&lt;Mutex&lt;Vec&lt;usize&gt;&gt;&gt;) -&gt; Self { # fn new(id: usize, thread: thread::Thread, readylist: Arc&lt;Mutex&lt;Vec&lt;usize&gt;&gt;&gt;) -&gt; Self {
</span><span class="boring"> MyWaker { # MyWaker {
</span><span class="boring"> id, # id,
</span><span class="boring"> thread, # thread,
</span><span class="boring"> readylist, # readylist,
</span><span class="boring"> } # }
</span><span class="boring"> } # }
</span><span class="boring"> #
</span><span class="boring"> fn wake(&amp;self) { # fn wake(&amp;self) {
</span><span class="boring"> self.readylist.lock().map(|mut rl| rl.push(self.id)).unwrap(); # self.readylist.lock().map(|mut rl| rl.push(self.id)).unwrap();
</span><span class="boring"> self.thread.unpark(); # self.thread.unpark();
</span><span class="boring"> } # }
</span><span class="boring">} # }
</span><span class="boring"> #
</span><span class="boring"> #
</span><span class="boring">#[derive(Debug, Clone)] # #[derive(Debug, Clone)]
</span><span class="boring">pub struct Task { # pub struct Task {
</span><span class="boring"> id: usize, # id: usize,
</span><span class="boring"> pending: bool, # pending: bool,
</span><span class="boring">} # }
</span><span class="boring"> #
</span><span class="boring">// ===== REACTOR ===== # // ===== REACTOR =====
</span><span class="boring">struct Reactor { # struct Reactor {
</span><span class="boring"> dispatcher: Sender&lt;Event&gt;, # dispatcher: Sender&lt;Event&gt;,
</span><span class="boring"> handle: Option&lt;JoinHandle&lt;()&gt;&gt;, # handle: Option&lt;JoinHandle&lt;()&gt;&gt;,
</span><span class="boring"> outstanding: AtomicUsize, # outstanding: AtomicUsize,
</span><span class="boring">} # }
</span><span class="boring">#[derive(Debug)] # #[derive(Debug)]
</span><span class="boring">enum Event { # enum Event {
</span><span class="boring"> Close, # Close,
</span><span class="boring"> Simple(MyWaker, u64), # Simple(MyWaker, u64),
</span><span class="boring">} # }
</span><span class="boring"> #
</span><span class="boring">impl Reactor { # impl Reactor {
</span><span class="boring"> fn new() -&gt; Self { # fn new() -&gt; Self {
</span><span class="boring"> let (tx, rx) = channel::&lt;Event&gt;(); # let (tx, rx) = channel::&lt;Event&gt;();
</span><span class="boring"> let mut handles = vec![]; # let mut handles = vec![];
</span><span class="boring"> let handle = thread::spawn(move || { # let handle = thread::spawn(move || {
</span><span class="boring"> // This simulates some I/O resource # // This simulates some I/O resource
</span><span class="boring"> for event in rx { # for event in rx {
</span><span class="boring"> match event { # match event {
</span><span class="boring"> Event::Close =&gt; break, # Event::Close =&gt; break,
</span><span class="boring"> Event::Simple(mywaker, duration) =&gt; { # Event::Simple(mywaker, duration) =&gt; {
</span><span class="boring"> let event_handle = thread::spawn(move || { # let event_handle = thread::spawn(move || {
</span><span class="boring"> thread::sleep(Duration::from_secs(duration)); # thread::sleep(Duration::from_secs(duration));
</span><span class="boring"> mywaker.wake(); # mywaker.wake();
</span><span class="boring"> }); # });
</span><span class="boring"> handles.push(event_handle); # handles.push(event_handle);
</span><span class="boring"> } # }
</span><span class="boring"> } # }
</span><span class="boring"> } # }
</span><span class="boring"> #
</span><span class="boring"> for handle in handles { # for handle in handles {
</span><span class="boring"> handle.join().unwrap(); # handle.join().unwrap();
</span><span class="boring"> } # }
</span><span class="boring"> }); # });
</span><span class="boring"> #
</span><span class="boring"> Reactor { # Reactor {
</span><span class="boring"> dispatcher: tx, # dispatcher: tx,
</span><span class="boring"> handle: Some(handle), # handle: Some(handle),
</span><span class="boring"> outstanding: AtomicUsize::new(0), # outstanding: AtomicUsize::new(0),
</span><span class="boring"> } # }
</span><span class="boring"> } # }
</span><span class="boring"> #
</span><span class="boring"> fn register(&amp;mut self, duration: u64, mywaker: MyWaker) { # fn register(&amp;mut self, duration: u64, mywaker: MyWaker) {
</span><span class="boring"> self.dispatcher # self.dispatcher
</span><span class="boring"> .send(Event::Simple(mywaker, duration)) # .send(Event::Simple(mywaker, duration))
</span><span class="boring"> .unwrap(); # .unwrap();
</span><span class="boring"> self.outstanding.fetch_add(1, Ordering::Relaxed); # self.outstanding.fetch_add(1, Ordering::Relaxed);
</span><span class="boring"> } # }
</span><span class="boring"> #
</span><span class="boring"> fn close(&amp;mut self) { # fn close(&amp;mut self) {
</span><span class="boring"> self.dispatcher.send(Event::Close).unwrap(); # self.dispatcher.send(Event::Close).unwrap();
</span><span class="boring"> } # }
</span><span class="boring">} # }
</span><span class="boring"> #
</span><span class="boring">impl Drop for Reactor { # impl Drop for Reactor {
</span><span class="boring"> fn drop(&amp;mut self) { # fn drop(&amp;mut self) {
</span><span class="boring"> self.handle.take().map(|h| h.join().unwrap()).unwrap(); # self.handle.take().map(|h| h.join().unwrap()).unwrap();
</span><span class="boring"> } # }
</span><span class="boring">} # }
</span></code></pre></pre> </code></pre></pre>
<h1><a class="header" href="#proper-waker" id="proper-waker">Proper Waker</a></h1> <h1><a class="header" href="#proper-waker" id="proper-waker">Proper Waker</a></h1>
<h1><a class="header" href="#proper-future" id="proper-future">Proper Future</a></h1> <h1><a class="header" href="#proper-future" id="proper-future">Proper Future</a></h1>
<h1><a class="header" href="#supporting-asyncawait" id="supporting-asyncawait">Supporting async/await</a></h1> <h1><a class="header" href="#supporting-asyncawait" id="supporting-asyncawait">Supporting async/await</a></h1>
@@ -618,30 +687,7 @@ fn main() {
</div> </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 type="text/javascript">
window.playpen_copyable = true;
</script>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

196
src/0_1_1_trait_objects.md Normal file
View File

@@ -0,0 +1,196 @@
# Trait objects and fat pointers
## Trait objects and dynamic dispatch
The single 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
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/
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:
``` 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]>());
}
```
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
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)
* 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.
`&dyn SomeTrait` is an example of a _trait object_
The layout for a pointer to a _trait object_ looks like this:
* The first 8 bytes points to the `data` for the trait object
* 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.
Let's explain this in code instead of words by implementing our own trait
object from these parts:
``` rust
// 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 mul(&self) -> i32;
}
// This will represent our home brewn fat pointer to a trait object
#[repr(C)]
struct FatPointer<'a> {
/// A reference is a pointer to an instantiated `Data` instance
data: &'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: &Data) -> i32 {
s.a + s.b
}
fn sub(s: &Data) -> i32 {
s.a - s.b
}
fn mul(s: &Data) -> 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.
// Try changing the order of add and sub and see what happens.
add as usize, // function pointer
sub as usize, // function pointer
mul as usize, // function pointer
];
let fat_pointer = FatPointer { data: &mut data, vtable: vtable.as_ptr()};
let test = unsafe { std::mem::transmute::<FatPointer, &dyn Test>(fat_pointer) };
// And voalá, it's now a trait object we can call methods on
println!("Add: 3 + 2 = {}", test.add());
println!("Sub: 3 - 2 = {}", test.sub());
println!("Mul: 3 * 2 = {}", test.mul());
}
```
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

View File

@@ -0,0 +1,28 @@
# Generators and Pin
So the second difficult part that there seems to be a lot of questions about
is Generators and the `Pin` type.
## Generators
```
**Relevant for:**
- Understanding how the async/await syntax works
- Why we need `Pin`
- Why Rusts async model is extremely efficient
```
The motivation for `Generators` can be found in [RFC#2033][rfc2033]. It's very
well written and I can recommend reading through it (it talks as much about
async/await as it does about generators).
Basically, there were three main options that were discussed when Rust was
desiging how the language would handle concurrency:
1. Stackfull coroutines, better known as green threads.
2. Using combinators.
3. Stackless coroutines, better known as generators.
[rfc2033]: https://github.com/rust-lang/rfcs/blob/master/text/2033-experimental-coroutines.md

View File

@@ -15,197 +15,3 @@ try to give a high level overview that will make it easier to learn Rusts
* [Async Basics - Strategies for handling I/O](https://cfsamson.github.io/book-exploring-async-basics/5_strategies_for_handling_io.html) * [Async Basics - Strategies for handling I/O](https://cfsamson.github.io/book-exploring-async-basics/5_strategies_for_handling_io.html)
* [Async Basics - Epoll, Kqueue and IOCP](https://cfsamson.github.io/book-exploring-async-basics/6_epoll_kqueue_iocp.html) * [Async Basics - Epoll, Kqueue and IOCP](https://cfsamson.github.io/book-exploring-async-basics/6_epoll_kqueue_iocp.html)
## Trait objects and dynamic dispatch
The single 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
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/
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:
``` 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]>());
}
```
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
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)
* 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.
`&dyn SomeTrait` is an example of a _trait object_
The layout for a pointer to a _trait object_ looks like this:
* The first 8 bytes points to the `data` for the trait object
* 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.
Let's explain this in code instead of words by implementing our own trait
object from these parts:
``` rust
// 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 mul(&self) -> i32;
}
// This will represent our home brewn fat pointer to a trait object
#[repr(C)]
struct FatPointer<'a> {
/// A reference is a pointer to an instantiated `Data` instance
data: &'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: &Data) -> i32 {
s.a + s.b
}
fn sub(s: &Data) -> i32 {
s.a - s.b
}
fn mul(s: &Data) -> 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.
// Try changing the order of add and sub and see what happens.
add as usize, // function pointer
sub as usize, // function pointer
mul as usize, // function pointer
];
let fat_pointer = FatPointer { data: &mut data, vtable: vtable.as_ptr()};
let test = unsafe { std::mem::transmute::<FatPointer, &dyn Test>(fat_pointer) };
// And voalá, it's now a trait object we can call methods on
println!("Add: 3 + 2 = {}", test.add());
println!("Sub: 3 - 2 = {}", test.sub());
println!("Mul: 3 * 2 = {}", test.mul());
}
```
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

View File

@@ -2,6 +2,8 @@
- [Introduction](./0_0_introduction.md) - [Introduction](./0_0_introduction.md)
- [Some background information](./0_1_background_information.md) - [Some background information](./0_1_background_information.md)
- [Trait objects and fat pointers](./0_1_1_trait_objects.md)
- [Generators and Pin](./0_1_2_generators_pin.md)
- [Naive example](./0_2_naive_implementation.md) - [Naive example](./0_2_naive_implementation.md)
- [Proper Waker](./0_3_proper_waker.md) - [Proper Waker](./0_3_proper_waker.md)
- [Proper Future](0_4_proper_future.md) - [Proper Future](0_4_proper_future.md)