version3 start

This commit is contained in:
Carl Fredrik Samson
2020-04-01 22:56:43 +02:00
parent 5c10caf9c5
commit 7f7fe098f3
16 changed files with 1427 additions and 30 deletions

View File

@@ -78,7 +78,7 @@
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_background_information.html" class="active"><strong aria-hidden="true">1.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">2.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">3.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">4.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">5.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">6.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_why_futures.html"><strong aria-hidden="true">1.</strong> Why Futures</a></li><li><a href="1_background_information.html" class="active"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
@@ -155,7 +155,7 @@
<ul>
<li>High level introduction to concurrency in Rust</li>
<li>Knowing what Rust provides and not when working with async code</li>
<li>Understanding why we need runtimes </li>
<li>Understanding why we need a runtime-library in Rust</li>
<li>Getting pointers to further reading on concurrency in general</li>
</ul>
</blockquote>
@@ -279,7 +279,7 @@ it needs to be, so go on and read these chapters if you feel a bit unsure. </p>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="introduction.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<a rel="prev" href="1_why_futures.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
@@ -297,7 +297,7 @@ it needs to be, so go on and read these chapters if you feel a bit unsure. </p>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a href="introduction.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<a href="1_why_futures.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>

629
book/1_why_futures.html Normal file
View File

@@ -0,0 +1,629 @@
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Why Futures - Futures Explained in 200 Lines of Rust</title>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="This book aims to explain Futures in Rust using an example driven approach.">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff" />
<link rel="shortcut icon" href="favicon.png">
<link rel="stylesheet" href="css/variables.css">
<link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" href="css/chrome.css">
<link rel="stylesheet" href="css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="highlight.css">
<link rel="stylesheet" href="tomorrow-night.css">
<link rel="stylesheet" href="ayu-highlight.css">
<!-- Custom theme stylesheets -->
</head>
<body class="light">
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script type="text/javascript">
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script type="text/javascript">
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
document.body.className = theme;
document.querySelector('html').className = theme + ' js';
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_why_futures.html" class="active"><strong aria-hidden="true">1.</strong> Why Futures</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar" class="menu-bar">
<div id="menu-bar-sticky-container">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Futures Explained in 200 Lines of Rust</h1>
<div class="right-buttons">
<a href="print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/cfsamson/books-futures-explained" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
</div>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script type="text/javascript">
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1><a class="header" href="#why-futures" id="why-futures">Why Futures</a></h1>
<p>Before we go into the details about Futures in Rust, let's take a quick look
at the alternatives for handling concurrent programming in general and some
pros and cons for each of them.</p>
<h2><a class="header" href="#threads-provided-by-the-operating-system" id="threads-provided-by-the-operating-system">Threads provided by the operating system</a></h2>
<p>Now one way of accomplishing this is letting the OS take care of everything for
us. We do this by simply spawning a new OS thread for each task we want to
accomplish and write code like we normally would.</p>
<p><strong>Pros:</strong></p>
<ul>
<li>Simple</li>
<li>Easy to use</li>
<li>Switching between tasks is reasonably fast</li>
<li>You get parallelism for free</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li>OS level threads come with a rather large stack. If you have many tasks
waiting simultaneously (like you would in a web-server under heavy load) you'll
run out of memory pretty soon.</li>
<li>There are a lot of syscalls involved. This can be pretty costly when the number
of tasks is high.</li>
<li>The OS has many things it needs to handle. It might not switch back to your
thread as fast as you'd wish.</li>
<li>Might not be an option on some systems</li>
</ul>
<p>Using OS threads in Rust looks like this:</p>
<pre><pre class="playpen"><code class="language-rust">use std::thread;
fn main() {
println!(&quot;So we start the program here!&quot;);
let t1 = thread::spawn(move || {
thread::sleep(std::time::Duration::from_millis(200));
println!(&quot;We create tasks which gets run when they're finished!&quot;);
});
let t2 = thread::spawn(move || {
thread::sleep(std::time::Duration::from_millis(100));
println!(&quot;We can even chain callbacks...&quot;);
let t3 = thread::spawn(move || {
thread::sleep(std::time::Duration::from_millis(50));
println!(&quot;...like this!&quot;);
});
t3.join().unwrap();
});
println!(&quot;While our tasks are executing we can do other stuff here.&quot;);
t1.join().unwrap();
t2.join().unwrap();
}
</code></pre></pre>
<h2><a class="header" href="#green-threads" id="green-threads">Green threads</a></h2>
<p>Green threads has been popularized by GO in the recent years. Green threads
uses the same basic technique as operating systems does to handle concurrency.</p>
<p>Green threads are implemented by setting up a stack for each task you want to
execute and make the CPU &quot;jump&quot; from one stack to another to switch between
tasks.</p>
<p>The typical flow will be like this:</p>
<ol>
<li>Run som non-blocking code</li>
<li>Make a blocking call to some external resource</li>
<li>CPU jumps to the &quot;main&quot; thread which schedules a different thread to run and
&quot;jumps&quot; to that stack</li>
<li>Run some non-blocking code on the new thread until a new blocking call or the
task is finished</li>
<li>&quot;jumps&quot; back to the &quot;main&quot; thread and so on</li>
</ol>
<p>These &quot;jumps&quot; are know as context switches. Your OS is doing it many times each
second as you read this.</p>
<p>The main advantages are:</p>
<ol>
<li>Simple to use. The code will look like it does when using OS threads.</li>
<li>A &quot;context switch&quot; is reasonably fast</li>
<li>Each stack only gets a little memory to start with so you can have hundred of
thousands of green threads running.</li>
<li>It's easy to incorporate <a href="https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/green-threads#preemptive-multitasking"><em>preemtion</em></a>
which puts a lot of control in the hands of the runtime implementors.</li>
</ol>
<p>The main cons are:</p>
<ol>
<li>The stacks might need to grow. Solving this is not easy and will have a cost.</li>
<li>You need to save all the CPU state on every switch</li>
<li>It's not a <em>zero cost abstraction</em> (which is one of the reasons Rust removed
them early on).</li>
<li>Complicated to implement correctly if you want to support many different
platforms.</li>
</ol>
<p>If you were to implement green threads in Rust, it could look something like
this:</p>
<pre><code>The example presented below is from an earlier book I wrote about green
threads called [Green Threads Explained in 200 lines of Rust.](https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/)
If you want to know what's going on everything is explained in detail
in that book.
</code></pre>
<pre><pre class="playpen"><code class="language-rust">#![feature(asm)]
#![feature(naked_functions)]
use std::ptr;
const DEFAULT_STACK_SIZE: usize = 1024 * 1024 * 2;
const MAX_THREADS: usize = 4;
static mut RUNTIME: usize = 0;
pub struct Runtime {
threads: Vec&lt;Thread&gt;,
current: usize,
}
#[derive(PartialEq, Eq, Debug)]
enum State {
Available,
Running,
Ready,
}
struct Thread {
id: usize,
stack: Vec&lt;u8&gt;,
ctx: ThreadContext,
state: State,
}
#[derive(Debug, Default)]
#[repr(C)]
struct ThreadContext {
rsp: u64,
r15: u64,
r14: u64,
r13: u64,
r12: u64,
rbx: u64,
rbp: u64,
}
impl Thread {
fn new(id: usize) -&gt; Self {
Thread {
id,
stack: vec![0_u8; DEFAULT_STACK_SIZE],
ctx: ThreadContext::default(),
state: State::Available,
}
}
}
impl Runtime {
pub fn new() -&gt; Self {
let base_thread = Thread {
id: 0,
stack: vec![0_u8; DEFAULT_STACK_SIZE],
ctx: ThreadContext::default(),
state: State::Running,
};
let mut threads = vec![base_thread];
let mut available_threads: Vec&lt;Thread&gt; = (1..MAX_THREADS).map(|i| Thread::new(i)).collect();
threads.append(&amp;mut available_threads);
Runtime {
threads,
current: 0,
}
}
pub fn init(&amp;self) {
unsafe {
let r_ptr: *const Runtime = self;
RUNTIME = r_ptr as usize;
}
}
pub fn run(&amp;mut self) -&gt; ! {
while self.t_yield() {}
std::process::exit(0);
}
fn t_return(&amp;mut self) {
if self.current != 0 {
self.threads[self.current].state = State::Available;
self.t_yield();
}
}
fn t_yield(&amp;mut self) -&gt; bool {
let mut pos = self.current;
while self.threads[pos].state != State::Ready {
pos += 1;
if pos == self.threads.len() {
pos = 0;
}
if pos == self.current {
return false;
}
}
if self.threads[self.current].state != State::Available {
self.threads[self.current].state = State::Ready;
}
self.threads[pos].state = State::Running;
let old_pos = self.current;
self.current = pos;
unsafe {
switch(&amp;mut self.threads[old_pos].ctx, &amp;self.threads[pos].ctx);
}
self.threads.len() &gt; 0
}
pub fn spawn(&amp;mut self, f: fn()) {
let available = self
.threads
.iter_mut()
.find(|t| t.state == State::Available)
.expect(&quot;no available thread.&quot;);
let size = available.stack.len();
unsafe {
let s_ptr = available.stack.as_mut_ptr().offset(size as isize);
let s_ptr = (s_ptr as usize &amp; !15) as *mut u8;
ptr::write(s_ptr.offset(-24) as *mut u64, guard as u64);
ptr::write(s_ptr.offset(-32) as *mut u64, f as u64);
available.ctx.rsp = s_ptr.offset(-32) as u64;
}
available.state = State::Ready;
}
}
fn guard() {
unsafe {
let rt_ptr = RUNTIME as *mut Runtime;
(*rt_ptr).t_return();
};
}
pub fn yield_thread() {
unsafe {
let rt_ptr = RUNTIME as *mut Runtime;
(*rt_ptr).t_yield();
};
}
#[naked]
#[inline(never)]
unsafe fn switch(old: *mut ThreadContext, new: *const ThreadContext) {
asm!(&quot;
mov %rsp, 0x00($0)
mov %r15, 0x08($0)
mov %r14, 0x10($0)
mov %r13, 0x18($0)
mov %r12, 0x20($0)
mov %rbx, 0x28($0)
mov %rbp, 0x30($0)
mov 0x00($1), %rsp
mov 0x08($1), %r15
mov 0x10($1), %r14
mov 0x18($1), %r13
mov 0x20($1), %r12
mov 0x28($1), %rbx
mov 0x30($1), %rbp
ret
&quot;
:
:&quot;r&quot;(old), &quot;r&quot;(new)
:
: &quot;volatile&quot;, &quot;alignstack&quot;
);
}
fn main() {
let mut runtime = Runtime::new();
runtime.init();
runtime.spawn(|| {
println!(&quot;THREAD 1 STARTING&quot;);
let id = 1;
for i in 0..10 {
println!(&quot;thread: {} counter: {}&quot;, id, i);
yield_thread();
}
println!(&quot;THREAD 1 FINISHED&quot;);
});
runtime.spawn(|| {
println!(&quot;THREAD 2 STARTING&quot;);
let id = 2;
for i in 0..15 {
println!(&quot;thread: {} counter: {}&quot;, id, i);
yield_thread();
}
println!(&quot;THREAD 2 FINISHED&quot;);
});
runtime.run();
}
</code></pre></pre>
<h3><a class="header" href="#callback-based-approach" id="callback-based-approach">Callback based approach</a></h3>
<p>You probably already know this from Javascript since it's extremely common.
The whole idea behind a callback based approach is to save a pointer to a
set of instructions we want to run later on.</p>
<p>The basic idea of not involving threads as a primary way to achieve concurrency
is the common denominator for the rest of the approaches. Including the one
Rust uses today which we'll soon get to.</p>
<p><strong>Advantages:</strong></p>
<ul>
<li>Easy to implement in most languages</li>
<li>No context switching</li>
<li>Low memory overhead (in most cases)</li>
</ul>
<p><strong>Drawbacks:</strong></p>
<ul>
<li>Each task must save the state it needs for later, the memory usage will grow
linearly with the number of tasks i .</li>
<li>Can be hard to reason about, also known as &quot;callback hell&quot;.</li>
<li>Sharing state between tasks is a hard problem in Rust using this approach due
to it's ownership model.</li>
</ul>
<p>The</p>
<p>If we did that in Rust it could look something like this:</p>
<pre><pre class="playpen"><code class="language-rust">fn program_main() {
println!(&quot;So we start the program here!&quot;);
set_timeout(200, || {
println!(&quot;We create tasks which gets run when they're finished!&quot;);
});
set_timeout(100, || {
println!(&quot;We can even chain callbacks...&quot;);
set_timeout(50, || {
println!(&quot;...like this!&quot;);
})
});
println!(&quot;While our tasks are executing we can do other stuff here.&quot;);
}
fn main() {
RT.with(|rt| rt.run(program_main));
}
use std::sync::mpsc::{channel, Receiver, Sender};
use std::{cell::RefCell, collections::HashMap, thread};
thread_local! {
static RT: Runtime = Runtime::new();
}
struct Runtime {
callbacks: RefCell&lt;HashMap&lt;usize, Box&lt;dyn FnOnce() -&gt; ()&gt;&gt;&gt;,
next_id: RefCell&lt;usize&gt;,
evt_sender: Sender&lt;usize&gt;,
evt_reciever: Receiver&lt;usize&gt;,
}
fn set_timeout(ms: u64, cb: impl FnOnce() + 'static) {
RT.with(|rt| {
let id = *rt.next_id.borrow();
*rt.next_id.borrow_mut() += 1;
rt.callbacks.borrow_mut().insert(id, Box::new(cb));
let evt_sender = rt.evt_sender.clone();
thread::spawn(move || {
thread::sleep(std::time::Duration::from_millis(ms));
evt_sender.send(id).unwrap();
});
});
}
impl Runtime {
fn new() -&gt; Self {
let (evt_sender, evt_reciever) = channel();
Runtime {
callbacks: RefCell::new(HashMap::new()),
next_id: RefCell::new(1),
evt_sender,
evt_reciever,
}
}
fn run(&amp;self, program: fn()) {
program();
for evt_id in &amp;self.evt_reciever {
let cb = self.callbacks.borrow_mut().remove(&amp;evt_id).unwrap();
cb();
if self.callbacks.borrow().is_empty() {
break;
}
}
}
}
</code></pre></pre>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="introduction.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="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>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a href="introduction.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="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>
</div>
<!-- Livereload script (if served using the cli tool) -->
<script type="text/javascript">
var socket = new WebSocket("ws://localhost:3001");
socket.onmessage = function (event) {
if (event.data === "reload") {
socket.close();
location.reload(true); // force reload from server (not from cache)
}
};
window.onbeforeunload = function() {
socket.close();
}
</script>
<!-- Google Analytics Tag -->
<script type="text/javascript">
var localAddrs = ["localhost", "127.0.0.1", ""];
// make sure we don't activate google analytics if the developer is
// inspecting the book locally...
if (localAddrs.indexOf(document.location.hostname) === -1) {
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-157536992-1', 'auto');
ga('send', 'pageview');
}
</script>
<script src="ace.js" type="text/javascript" charset="utf-8"></script>
<script src="editor.js" type="text/javascript" charset="utf-8"></script>
<script src="mode-rust.js" type="text/javascript" charset="utf-8"></script>
<script src="theme-dawn.js" type="text/javascript" charset="utf-8"></script>
<script src="theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="book.js" type="text/javascript" charset="utf-8"></script>
<!-- Custom JS scripts -->
</body>
</html>

View File

@@ -78,7 +78,7 @@
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">1.</strong> Some background information</a></li><li><a href="2_waker_context.html" class="active"><strong aria-hidden="true">2.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">3.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">4.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">5.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">6.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_why_futures.html"><strong aria-hidden="true">1.</strong> Why Futures</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="2_waker_context.html" class="active"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>

View File

@@ -78,7 +78,7 @@
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">1.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">2.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html" class="active"><strong aria-hidden="true">3.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">4.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">5.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">6.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_why_futures.html"><strong aria-hidden="true">1.</strong> Why Futures</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html" class="active"><strong aria-hidden="true">4.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>

View File

@@ -78,7 +78,7 @@
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">1.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">2.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">3.</strong> Generators</a></li><li><a href="4_pin.html" class="active"><strong aria-hidden="true">4.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">5.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">6.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_why_futures.html"><strong aria-hidden="true">1.</strong> Why Futures</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators</a></li><li><a href="4_pin.html" class="active"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>

View File

@@ -78,7 +78,7 @@
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">1.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">2.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">3.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">4.</strong> Pin</a></li><li><a href="6_future_example.html" class="active"><strong aria-hidden="true">5.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">6.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_why_futures.html"><strong aria-hidden="true">1.</strong> Why Futures</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html" class="active"><strong aria-hidden="true">6.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>

View File

@@ -78,7 +78,7 @@
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">1.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">2.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">3.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">4.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">5.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html" class="active"><strong aria-hidden="true">6.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_why_futures.html"><strong aria-hidden="true">1.</strong> Why Futures</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html" class="active"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>

View File

@@ -78,7 +78,7 @@
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">1.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">2.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">3.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">4.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">5.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">6.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html" class="active">Conclusion and exercises</a></li></ol>
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_why_futures.html"><strong aria-hidden="true">1.</strong> Why Futures</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html" class="active">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>

View File

@@ -78,7 +78,7 @@
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">1.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">2.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">3.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">4.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">5.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">6.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_why_futures.html"><strong aria-hidden="true">1.</strong> Why Futures</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>

View File

@@ -78,7 +78,7 @@
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html" class="active">Introduction</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">1.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">2.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">3.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">4.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">5.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">6.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
<ol class="chapter"><li class="affix"><a href="introduction.html" class="active">Introduction</a></li><li><a href="1_why_futures.html"><strong aria-hidden="true">1.</strong> Why Futures</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
@@ -197,7 +197,7 @@ very well written and very helpful. So thanks!</p>
<a rel="next" href="1_background_information.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<a rel="next" href="1_why_futures.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
@@ -211,7 +211,7 @@ very well written and very helpful. So thanks!</p>
<a href="1_background_information.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<a href="1_why_futures.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>

View File

@@ -80,7 +80,7 @@
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">1.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">2.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">3.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">4.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">5.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">6.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="1_why_futures.html"><strong aria-hidden="true">1.</strong> Why Futures</a></li><li><a href="1_background_information.html"><strong aria-hidden="true">2.</strong> Some background information</a></li><li><a href="2_waker_context.html"><strong aria-hidden="true">3.</strong> Waker and Context</a></li><li><a href="3_generators_pin.html"><strong aria-hidden="true">4.</strong> Generators</a></li><li><a href="4_pin.html"><strong aria-hidden="true">5.</strong> Pin</a></li><li><a href="6_future_example.html"><strong aria-hidden="true">6.</strong> Futures - our main example</a></li><li><a href="8_finished_example.html"><strong aria-hidden="true">7.</strong> Finished example (editable)</a></li><li class="affix"><a href="conclusion.html">Conclusion and exercises</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
@@ -191,13 +191,392 @@ explore further and try your own ideas.</p>
<code>async_std</code>, <code>Futures</code>, <code>libc</code>, <code>crossbeam</code> and many other libraries which so
much is built upon. Even the RFCs that much of the design is built upon is
very well written and very helpful. So thanks!</p>
<h1><a class="header" href="#why-futures" id="why-futures">Why Futures</a></h1>
<p>Before we go into the details about Futures in Rust, let's take a quick look
at the alternatives for handling concurrent programming in general and some
pros and cons for each of them.</p>
<h2><a class="header" href="#threads-provided-by-the-operating-system" id="threads-provided-by-the-operating-system">Threads provided by the operating system</a></h2>
<p>Now one way of accomplishing this is letting the OS take care of everything for
us. We do this by simply spawning a new OS thread for each task we want to
accomplish and write code like we normally would.</p>
<p><strong>Pros:</strong></p>
<ul>
<li>Simple</li>
<li>Easy to use</li>
<li>Switching between tasks is reasonably fast</li>
<li>You get parallelism for free</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li>OS level threads come with a rather large stack. If you have many tasks
waiting simultaneously (like you would in a web-server under heavy load) you'll
run out of memory pretty soon.</li>
<li>There are a lot of syscalls involved. This can be pretty costly when the number
of tasks is high.</li>
<li>The OS has many things it needs to handle. It might not switch back to your
thread as fast as you'd wish.</li>
<li>Might not be an option on some systems</li>
</ul>
<p>Using OS threads in Rust looks like this:</p>
<pre><pre class="playpen"><code class="language-rust">use std::thread;
fn main() {
println!(&quot;So we start the program here!&quot;);
let t1 = thread::spawn(move || {
thread::sleep(std::time::Duration::from_millis(200));
println!(&quot;We create tasks which gets run when they're finished!&quot;);
});
let t2 = thread::spawn(move || {
thread::sleep(std::time::Duration::from_millis(100));
println!(&quot;We can even chain callbacks...&quot;);
let t3 = thread::spawn(move || {
thread::sleep(std::time::Duration::from_millis(50));
println!(&quot;...like this!&quot;);
});
t3.join().unwrap();
});
println!(&quot;While our tasks are executing we can do other stuff here.&quot;);
t1.join().unwrap();
t2.join().unwrap();
}
</code></pre></pre>
<h2><a class="header" href="#green-threads" id="green-threads">Green threads</a></h2>
<p>Green threads has been popularized by GO in the recent years. Green threads
uses the same basic technique as operating systems does to handle concurrency.</p>
<p>Green threads are implemented by setting up a stack for each task you want to
execute and make the CPU &quot;jump&quot; from one stack to another to switch between
tasks.</p>
<p>The typical flow will be like this:</p>
<ol>
<li>Run som non-blocking code</li>
<li>Make a blocking call to some external resource</li>
<li>CPU jumps to the &quot;main&quot; thread which schedules a different thread to run and
&quot;jumps&quot; to that stack</li>
<li>Run some non-blocking code on the new thread until a new blocking call or the
task is finished</li>
<li>&quot;jumps&quot; back to the &quot;main&quot; thread and so on</li>
</ol>
<p>These &quot;jumps&quot; are know as context switches. Your OS is doing it many times each
second as you read this.</p>
<p>The main advantages are:</p>
<ol>
<li>Simple to use. The code will look like it does when using OS threads.</li>
<li>A &quot;context switch&quot; is reasonably fast</li>
<li>Each stack only gets a little memory to start with so you can have hundred of
thousands of green threads running.</li>
<li>It's easy to incorporate <a href="https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/green-threads#preemptive-multitasking"><em>preemtion</em></a>
which puts a lot of control in the hands of the runtime implementors.</li>
</ol>
<p>The main cons are:</p>
<ol>
<li>The stacks might need to grow. Solving this is not easy and will have a cost.</li>
<li>You need to save all the CPU state on every switch</li>
<li>It's not a <em>zero cost abstraction</em> (which is one of the reasons Rust removed
them early on).</li>
<li>Complicated to implement correctly if you want to support many different
platforms.</li>
</ol>
<p>If you were to implement green threads in Rust, it could look something like
this:</p>
<pre><code>The example presented below is from an earlier book I wrote about green
threads called [Green Threads Explained in 200 lines of Rust.](https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/)
If you want to know what's going on everything is explained in detail
in that book.
</code></pre>
<pre><pre class="playpen"><code class="language-rust">#![feature(asm)]
#![feature(naked_functions)]
use std::ptr;
const DEFAULT_STACK_SIZE: usize = 1024 * 1024 * 2;
const MAX_THREADS: usize = 4;
static mut RUNTIME: usize = 0;
pub struct Runtime {
threads: Vec&lt;Thread&gt;,
current: usize,
}
#[derive(PartialEq, Eq, Debug)]
enum State {
Available,
Running,
Ready,
}
struct Thread {
id: usize,
stack: Vec&lt;u8&gt;,
ctx: ThreadContext,
state: State,
}
#[derive(Debug, Default)]
#[repr(C)]
struct ThreadContext {
rsp: u64,
r15: u64,
r14: u64,
r13: u64,
r12: u64,
rbx: u64,
rbp: u64,
}
impl Thread {
fn new(id: usize) -&gt; Self {
Thread {
id,
stack: vec![0_u8; DEFAULT_STACK_SIZE],
ctx: ThreadContext::default(),
state: State::Available,
}
}
}
impl Runtime {
pub fn new() -&gt; Self {
let base_thread = Thread {
id: 0,
stack: vec![0_u8; DEFAULT_STACK_SIZE],
ctx: ThreadContext::default(),
state: State::Running,
};
let mut threads = vec![base_thread];
let mut available_threads: Vec&lt;Thread&gt; = (1..MAX_THREADS).map(|i| Thread::new(i)).collect();
threads.append(&amp;mut available_threads);
Runtime {
threads,
current: 0,
}
}
pub fn init(&amp;self) {
unsafe {
let r_ptr: *const Runtime = self;
RUNTIME = r_ptr as usize;
}
}
pub fn run(&amp;mut self) -&gt; ! {
while self.t_yield() {}
std::process::exit(0);
}
fn t_return(&amp;mut self) {
if self.current != 0 {
self.threads[self.current].state = State::Available;
self.t_yield();
}
}
fn t_yield(&amp;mut self) -&gt; bool {
let mut pos = self.current;
while self.threads[pos].state != State::Ready {
pos += 1;
if pos == self.threads.len() {
pos = 0;
}
if pos == self.current {
return false;
}
}
if self.threads[self.current].state != State::Available {
self.threads[self.current].state = State::Ready;
}
self.threads[pos].state = State::Running;
let old_pos = self.current;
self.current = pos;
unsafe {
switch(&amp;mut self.threads[old_pos].ctx, &amp;self.threads[pos].ctx);
}
self.threads.len() &gt; 0
}
pub fn spawn(&amp;mut self, f: fn()) {
let available = self
.threads
.iter_mut()
.find(|t| t.state == State::Available)
.expect(&quot;no available thread.&quot;);
let size = available.stack.len();
unsafe {
let s_ptr = available.stack.as_mut_ptr().offset(size as isize);
let s_ptr = (s_ptr as usize &amp; !15) as *mut u8;
ptr::write(s_ptr.offset(-24) as *mut u64, guard as u64);
ptr::write(s_ptr.offset(-32) as *mut u64, f as u64);
available.ctx.rsp = s_ptr.offset(-32) as u64;
}
available.state = State::Ready;
}
}
fn guard() {
unsafe {
let rt_ptr = RUNTIME as *mut Runtime;
(*rt_ptr).t_return();
};
}
pub fn yield_thread() {
unsafe {
let rt_ptr = RUNTIME as *mut Runtime;
(*rt_ptr).t_yield();
};
}
#[naked]
#[inline(never)]
unsafe fn switch(old: *mut ThreadContext, new: *const ThreadContext) {
asm!(&quot;
mov %rsp, 0x00($0)
mov %r15, 0x08($0)
mov %r14, 0x10($0)
mov %r13, 0x18($0)
mov %r12, 0x20($0)
mov %rbx, 0x28($0)
mov %rbp, 0x30($0)
mov 0x00($1), %rsp
mov 0x08($1), %r15
mov 0x10($1), %r14
mov 0x18($1), %r13
mov 0x20($1), %r12
mov 0x28($1), %rbx
mov 0x30($1), %rbp
ret
&quot;
:
:&quot;r&quot;(old), &quot;r&quot;(new)
:
: &quot;volatile&quot;, &quot;alignstack&quot;
);
}
fn main() {
let mut runtime = Runtime::new();
runtime.init();
runtime.spawn(|| {
println!(&quot;THREAD 1 STARTING&quot;);
let id = 1;
for i in 0..10 {
println!(&quot;thread: {} counter: {}&quot;, id, i);
yield_thread();
}
println!(&quot;THREAD 1 FINISHED&quot;);
});
runtime.spawn(|| {
println!(&quot;THREAD 2 STARTING&quot;);
let id = 2;
for i in 0..15 {
println!(&quot;thread: {} counter: {}&quot;, id, i);
yield_thread();
}
println!(&quot;THREAD 2 FINISHED&quot;);
});
runtime.run();
}
</code></pre></pre>
<h3><a class="header" href="#callback-based-approach" id="callback-based-approach">Callback based approach</a></h3>
<p>You probably already know this from Javascript since it's extremely common.
The whole idea behind a callback based approach is to save a pointer to a
set of instructions we want to run later on.</p>
<p>The basic idea of not involving threads as a primary way to achieve concurrency
is the common denominator for the rest of the approaches. Including the one
Rust uses today which we'll soon get to.</p>
<p><strong>Advantages:</strong></p>
<ul>
<li>Easy to implement in most languages</li>
<li>No context switching</li>
<li>Low memory overhead (in most cases)</li>
</ul>
<p><strong>Drawbacks:</strong></p>
<ul>
<li>Each task must save the state it needs for later, the memory usage will grow
linearly with the number of tasks i .</li>
<li>Can be hard to reason about, also known as &quot;callback hell&quot;.</li>
<li>Sharing state between tasks is a hard problem in Rust using this approach due
to it's ownership model.</li>
</ul>
<p>The</p>
<p>If we did that in Rust it could look something like this:</p>
<pre><pre class="playpen"><code class="language-rust">fn program_main() {
println!(&quot;So we start the program here!&quot;);
set_timeout(200, || {
println!(&quot;We create tasks which gets run when they're finished!&quot;);
});
set_timeout(100, || {
println!(&quot;We can even chain callbacks...&quot;);
set_timeout(50, || {
println!(&quot;...like this!&quot;);
})
});
println!(&quot;While our tasks are executing we can do other stuff here.&quot;);
}
fn main() {
RT.with(|rt| rt.run(program_main));
}
use std::sync::mpsc::{channel, Receiver, Sender};
use std::{cell::RefCell, collections::HashMap, thread};
thread_local! {
static RT: Runtime = Runtime::new();
}
struct Runtime {
callbacks: RefCell&lt;HashMap&lt;usize, Box&lt;dyn FnOnce() -&gt; ()&gt;&gt;&gt;,
next_id: RefCell&lt;usize&gt;,
evt_sender: Sender&lt;usize&gt;,
evt_reciever: Receiver&lt;usize&gt;,
}
fn set_timeout(ms: u64, cb: impl FnOnce() + 'static) {
RT.with(|rt| {
let id = *rt.next_id.borrow();
*rt.next_id.borrow_mut() += 1;
rt.callbacks.borrow_mut().insert(id, Box::new(cb));
let evt_sender = rt.evt_sender.clone();
thread::spawn(move || {
thread::sleep(std::time::Duration::from_millis(ms));
evt_sender.send(id).unwrap();
});
});
}
impl Runtime {
fn new() -&gt; Self {
let (evt_sender, evt_reciever) = channel();
Runtime {
callbacks: RefCell::new(HashMap::new()),
next_id: RefCell::new(1),
evt_sender,
evt_reciever,
}
}
fn run(&amp;self, program: fn()) {
program();
for evt_id in &amp;self.evt_reciever {
let cb = self.callbacks.borrow_mut().remove(&amp;evt_id).unwrap();
cb();
if self.callbacks.borrow().is_empty() {
break;
}
}
}
}
</code></pre></pre>
<h1><a class="header" href="#some-background-information" id="some-background-information">Some background information</a></h1>
<blockquote>
<p><strong>Relevant for:</strong></p>
<ul>
<li>High level introduction to concurrency in Rust</li>
<li>Knowing what Rust provides and not when working with async code</li>
<li>Understanding why we need runtimes </li>
<li>Understanding why we need a runtime-library in Rust</li>
<li>Getting pointers to further reading on concurrency in general</li>
</ul>
</blockquote>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -13,18 +13,6 @@ information that will help demystify some of the concepts we encounter.
Actually, after going through these concepts, implementing futures will seem
pretty simple. I promise.
## Popular alternatives for writing concurrent programs
So let's kick this off by first taking a brief look into what the popular
options we have for writing concurrent programs
### Callback based approcah
You probably already know this from Javascript since it's extremely common:
## Futures
So what is a future?

400
src/1_why_futures.md Normal file
View File

@@ -0,0 +1,400 @@
# Why Futures
Before we go into the details about Futures in Rust, let's take a quick look
at the alternatives for handling concurrent programming in general and some
pros and cons for each of them.
## Threads provided by the operating system
Now one way of accomplishing this is letting the OS take care of everything for
us. We do this by simply spawning a new OS thread for each task we want to
accomplish and write code like we normally would.
**Pros:**
- Simple
- Easy to use
- Switching between tasks is reasonably fast
- You get parallelism for free
**Cons:**
- OS level threads come with a rather large stack. If you have many tasks
waiting simultaneously (like you would in a web-server under heavy load) you'll
run out of memory pretty soon.
- There are a lot of syscalls involved. This can be pretty costly when the number
of tasks is high.
- The OS has many things it needs to handle. It might not switch back to your
thread as fast as you'd wish.
- Might not be an option on some systems
Using OS threads in Rust looks like this:
```rust
use std::thread;
fn main() {
println!("So we start the program here!");
let t1 = thread::spawn(move || {
thread::sleep(std::time::Duration::from_millis(200));
println!("We create tasks which gets run when they're finished!");
});
let t2 = thread::spawn(move || {
thread::sleep(std::time::Duration::from_millis(100));
println!("We can even chain callbacks...");
let t3 = thread::spawn(move || {
thread::sleep(std::time::Duration::from_millis(50));
println!("...like this!");
});
t3.join().unwrap();
});
println!("While our tasks are executing we can do other stuff here.");
t1.join().unwrap();
t2.join().unwrap();
}
```
## Green threads
Green threads has been popularized by GO in the recent years. Green threads
uses the same basic technique as operating systems does to handle concurrency.
Green threads are implemented by setting up a stack for each task you want to
execute and make the CPU "jump" from one stack to another to switch between
tasks.
The typical flow will be like this:
1. Run som non-blocking code
2. Make a blocking call to some external resource
3. CPU jumps to the "main" thread which schedules a different thread to run and
"jumps" to that stack
4. Run some non-blocking code on the new thread until a new blocking call or the
task is finished
5. "jumps" back to the "main" thread and so on
These "jumps" are know as context switches. Your OS is doing it many times each
second as you read this.
The main advantages are:
1. Simple to use. The code will look like it does when using OS threads.
2. A "context switch" is reasonably fast
3. Each stack only gets a little memory to start with so you can have hundred of
thousands of green threads running.
4. It's easy to incorporate [_preemtion_](https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/green-threads#preemptive-multitasking)
which puts a lot of control in the hands of the runtime implementors.
The main cons are:
1. The stacks might need to grow. Solving this is not easy and will have a cost.
2. You need to save all the CPU state on every switch
3. It's not a _zero cost abstraction_ (which is one of the reasons Rust removed
them early on).
4. Complicated to implement correctly if you want to support many different
platforms.
If you were to implement green threads in Rust, it could look something like
this:
The example presented below is from an earlier book I wrote about green
threads called [Green Threads Explained in 200 lines of Rust.](https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/)
If you want to know what's going on everything is explained in detail
in that book.
```rust
#![feature(asm)]
#![feature(naked_functions)]
use std::ptr;
const DEFAULT_STACK_SIZE: usize = 1024 * 1024 * 2;
const MAX_THREADS: usize = 4;
static mut RUNTIME: usize = 0;
pub struct Runtime {
threads: Vec<Thread>,
current: usize,
}
#[derive(PartialEq, Eq, Debug)]
enum State {
Available,
Running,
Ready,
}
struct Thread {
id: usize,
stack: Vec<u8>,
ctx: ThreadContext,
state: State,
}
#[derive(Debug, Default)]
#[repr(C)]
struct ThreadContext {
rsp: u64,
r15: u64,
r14: u64,
r13: u64,
r12: u64,
rbx: u64,
rbp: u64,
}
impl Thread {
fn new(id: usize) -> Self {
Thread {
id,
stack: vec![0_u8; DEFAULT_STACK_SIZE],
ctx: ThreadContext::default(),
state: State::Available,
}
}
}
impl Runtime {
pub fn new() -> Self {
let base_thread = Thread {
id: 0,
stack: vec![0_u8; DEFAULT_STACK_SIZE],
ctx: ThreadContext::default(),
state: State::Running,
};
let mut threads = vec![base_thread];
let mut available_threads: Vec<Thread> = (1..MAX_THREADS).map(|i| Thread::new(i)).collect();
threads.append(&mut available_threads);
Runtime {
threads,
current: 0,
}
}
pub fn init(&self) {
unsafe {
let r_ptr: *const Runtime = self;
RUNTIME = r_ptr as usize;
}
}
pub fn run(&mut self) -> ! {
while self.t_yield() {}
std::process::exit(0);
}
fn t_return(&mut self) {
if self.current != 0 {
self.threads[self.current].state = State::Available;
self.t_yield();
}
}
fn t_yield(&mut self) -> bool {
let mut pos = self.current;
while self.threads[pos].state != State::Ready {
pos += 1;
if pos == self.threads.len() {
pos = 0;
}
if pos == self.current {
return false;
}
}
if self.threads[self.current].state != State::Available {
self.threads[self.current].state = State::Ready;
}
self.threads[pos].state = State::Running;
let old_pos = self.current;
self.current = pos;
unsafe {
switch(&mut self.threads[old_pos].ctx, &self.threads[pos].ctx);
}
self.threads.len() > 0
}
pub fn spawn(&mut self, f: fn()) {
let available = self
.threads
.iter_mut()
.find(|t| t.state == State::Available)
.expect("no available thread.");
let size = available.stack.len();
unsafe {
let s_ptr = available.stack.as_mut_ptr().offset(size as isize);
let s_ptr = (s_ptr as usize & !15) as *mut u8;
ptr::write(s_ptr.offset(-24) as *mut u64, guard as u64);
ptr::write(s_ptr.offset(-32) as *mut u64, f as u64);
available.ctx.rsp = s_ptr.offset(-32) as u64;
}
available.state = State::Ready;
}
}
fn guard() {
unsafe {
let rt_ptr = RUNTIME as *mut Runtime;
(*rt_ptr).t_return();
};
}
pub fn yield_thread() {
unsafe {
let rt_ptr = RUNTIME as *mut Runtime;
(*rt_ptr).t_yield();
};
}
#[naked]
#[inline(never)]
unsafe fn switch(old: *mut ThreadContext, new: *const ThreadContext) {
asm!("
mov %rsp, 0x00($0)
mov %r15, 0x08($0)
mov %r14, 0x10($0)
mov %r13, 0x18($0)
mov %r12, 0x20($0)
mov %rbx, 0x28($0)
mov %rbp, 0x30($0)
mov 0x00($1), %rsp
mov 0x08($1), %r15
mov 0x10($1), %r14
mov 0x18($1), %r13
mov 0x20($1), %r12
mov 0x28($1), %rbx
mov 0x30($1), %rbp
ret
"
:
:"r"(old), "r"(new)
:
: "volatile", "alignstack"
);
}
fn main() {
let mut runtime = Runtime::new();
runtime.init();
runtime.spawn(|| {
println!("THREAD 1 STARTING");
let id = 1;
for i in 0..10 {
println!("thread: {} counter: {}", id, i);
yield_thread();
}
println!("THREAD 1 FINISHED");
});
runtime.spawn(|| {
println!("THREAD 2 STARTING");
let id = 2;
for i in 0..15 {
println!("thread: {} counter: {}", id, i);
yield_thread();
}
println!("THREAD 2 FINISHED");
});
runtime.run();
}
```
### Callback based approach
You probably already know this from Javascript since it's extremely common.
The whole idea behind a callback based approach is to save a pointer to a
set of instructions we want to run later on.
The basic idea of not involving threads as a primary way to achieve concurrency
is the common denominator for the rest of the approaches. Including the one
Rust uses today which we'll soon get to.
**Advantages:**
- Easy to implement in most languages
- No context switching
- Low memory overhead (in most cases)
**Drawbacks:**
- Each task must save the state it needs for later, the memory usage will grow
linearly with the number of tasks i .
- Can be hard to reason about, also known as "callback hell".
- Sharing state between tasks is a hard problem in Rust using this approach due
to it's ownership model.
The
If we did that in Rust it could look something like this:
```rust
fn program_main() {
println!("So we start the program here!");
set_timeout(200, || {
println!("We create tasks which gets run when they're finished!");
});
set_timeout(100, || {
println!("We can even chain callbacks...");
set_timeout(50, || {
println!("...like this!");
})
});
println!("While our tasks are executing we can do other stuff here.");
}
fn main() {
RT.with(|rt| rt.run(program_main));
}
use std::sync::mpsc::{channel, Receiver, Sender};
use std::{cell::RefCell, collections::HashMap, thread};
thread_local! {
static RT: Runtime = Runtime::new();
}
struct Runtime {
callbacks: RefCell<HashMap<usize, Box<dyn FnOnce() -> ()>>>,
next_id: RefCell<usize>,
evt_sender: Sender<usize>,
evt_reciever: Receiver<usize>,
}
fn set_timeout(ms: u64, cb: impl FnOnce() + 'static) {
RT.with(|rt| {
let id = *rt.next_id.borrow();
*rt.next_id.borrow_mut() += 1;
rt.callbacks.borrow_mut().insert(id, Box::new(cb));
let evt_sender = rt.evt_sender.clone();
thread::spawn(move || {
thread::sleep(std::time::Duration::from_millis(ms));
evt_sender.send(id).unwrap();
});
});
}
impl Runtime {
fn new() -> Self {
let (evt_sender, evt_reciever) = channel();
Runtime {
callbacks: RefCell::new(HashMap::new()),
next_id: RefCell::new(1),
evt_sender,
evt_reciever,
}
}
fn run(&self, program: fn()) {
program();
for evt_id in &self.evt_reciever {
let cb = self.callbacks.borrow_mut().remove(&evt_id).unwrap();
cb();
if self.callbacks.borrow().is_empty() {
break;
}
}
}
}
```

View File

@@ -2,6 +2,7 @@
[Introduction](./introduction.md)
- [Why Futures](./1_why_futures.md)
- [Some background information](./1_background_information.md)
- [Waker and Context](./2_waker_context.md)
- [Generators](./3_generators_pin.md)