finished background information
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Why Futures - Futures Explained in 200 Lines of Rust</title>
|
||||
<title>Background information - Futures Explained in 200 Lines of Rust</title>
|
||||
|
||||
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
@@ -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_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>
|
||||
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html" class="active"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</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>
|
||||
@@ -149,14 +149,22 @@
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1><a class="header" href="#why-futures" id="why-futures">Why Futures</a></h1>
|
||||
<h1><a class="header" href="#some-background-information" id="some-background-information">Some Background Information</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>
|
||||
<p>While we do that we'll get some information on concurrency which will make it
|
||||
easier for us when we dive in to Futures specifically.</p>
|
||||
<blockquote>
|
||||
<p>For fun, I've added a small snipped of runnable code with most of the examples.
|
||||
If you're like me, things get way more interesting then and maybe you'll se some
|
||||
things you haven't seen before along the way.</p>
|
||||
</blockquote>
|
||||
<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
|
||||
<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>The runtime we use to handle concurrency for us is the operating system itself.</p>
|
||||
<p><strong>Advantages:</strong></p>
|
||||
<ul>
|
||||
<li>Simple</li>
|
||||
@@ -168,14 +176,14 @@ accomplish and write code like we normally would.</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>
|
||||
run out of memory pretty fast.</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>
|
||||
<p><strong>Using OS threads in Rust looks like this:</strong></p>
|
||||
<pre><pre class="playpen"><code class="language-rust">use std::thread;
|
||||
|
||||
fn main() {
|
||||
@@ -203,18 +211,20 @@ fn main() {
|
||||
<p>OS threads sure has some pretty big advantages. So why all this talk about
|
||||
"async" and concurrency in the first place?</p>
|
||||
<p>First of all. For computers to be <a href="https://en.wikipedia.org/wiki/Efficiency"><em>efficient</em></a> it needs to multitask. Once you
|
||||
start to look under the covers (like <a href="https://os.phil-opp.com/async-await/">how an operating system works</a>)
|
||||
start to look under the covers (like <a href="https://os.phil-opp.com/async-await/">how an operating system works</a>)
|
||||
you'll see concurrency everywhere. It's very fundamental in everything we do.</p>
|
||||
<p>Secondly, we have the web. Webservers is all about I/O and handling small tasks
|
||||
(requests). When the number of small tasks is large it's not a good fit for OS
|
||||
threads as of today because of the memory they require and the overhead involved
|
||||
when creating new threads. That's why you'll see so many async web frameworks
|
||||
and database drivers today.</p>
|
||||
<p>However, for a huge number of tasks, the standard OS threads will often be the
|
||||
when creating new threads. This gets even more relevant when the load is variable
|
||||
which means the current number of tasks a program has at any point in time is
|
||||
unpredictable. That's why you'll see so many async web frameworks and database
|
||||
drivers today.</p>
|
||||
<p>However, for a huge number of problems, the standard OS threads will often be the
|
||||
right solution. So, just think twice about your problem before you reach for an
|
||||
async library.</p>
|
||||
<p>Now, let's look at some other options for multitasking. They all have in common
|
||||
that they implement a way to do multitasking by implementing a "userland"
|
||||
that they implement a way to do multitasking by having a "userland"
|
||||
runtime:</p>
|
||||
<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
|
||||
@@ -255,10 +265,12 @@ platforms.</li>
|
||||
<p>If you were to implement green threads in Rust, it could look something like
|
||||
this:</p>
|
||||
<blockquote>
|
||||
<p>The example presented below is from an earlier book I wrote about green
|
||||
threads called <a href="https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/">Green Threads Explained in 200 lines of Rust.</a>
|
||||
<p>The example presented below is an adapted example from an earlier gitbook I
|
||||
wrote about green threads called <a href="https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/">Green Threads Explained in 200 lines of Rust.</a>
|
||||
If you want to know what's going on you'll find everything explained in detail
|
||||
in that book.</p>
|
||||
in that book. The code below is wildly unsafe and it's just to show a real example.
|
||||
It's not in any way meant to showcase "best practice". Just so we're on
|
||||
the same page.</p>
|
||||
</blockquote>
|
||||
<pre><pre class="playpen"><code class="language-rust">#![feature(asm)]
|
||||
#![feature(naked_functions)]
|
||||
@@ -285,6 +297,7 @@ struct Thread {
|
||||
stack: Vec<u8>,
|
||||
ctx: ThreadContext,
|
||||
state: State,
|
||||
task: Option<Box<dyn Fn()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@@ -297,6 +310,7 @@ struct ThreadContext {
|
||||
r12: u64,
|
||||
rbx: u64,
|
||||
rbp: u64,
|
||||
thread_ptr: u64,
|
||||
}
|
||||
|
||||
impl Thread {
|
||||
@@ -306,6 +320,7 @@ impl Thread {
|
||||
stack: vec![0_u8; DEFAULT_STACK_SIZE],
|
||||
ctx: ThreadContext::default(),
|
||||
state: State::Available,
|
||||
task: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -317,11 +332,14 @@ impl Runtime {
|
||||
stack: vec![0_u8; DEFAULT_STACK_SIZE],
|
||||
ctx: ThreadContext::default(),
|
||||
state: State::Running,
|
||||
task: None,
|
||||
};
|
||||
|
||||
let mut threads = vec![base_thread];
|
||||
threads[0].ctx.thread_ptr = &threads[0] as *const Thread as u64;
|
||||
let mut available_threads: Vec<Thread> = (1..MAX_THREADS).map(|i| Thread::new(i)).collect();
|
||||
threads.append(&mut available_threads);
|
||||
|
||||
Runtime {
|
||||
threads,
|
||||
current: 0,
|
||||
@@ -358,40 +376,56 @@ impl Runtime {
|
||||
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
|
||||
true
|
||||
}
|
||||
|
||||
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();
|
||||
pub fn spawn<F: Fn() + 'static>(f: F){
|
||||
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;
|
||||
let rt_ptr = RUNTIME as *mut Runtime;
|
||||
let available = (*rt_ptr)
|
||||
.threads
|
||||
.iter_mut()
|
||||
.find(|t| t.state == State::Available)
|
||||
.expect("no available thread.");
|
||||
|
||||
let size = available.stack.len();
|
||||
let s_ptr = available.stack.as_mut_ptr();
|
||||
available.task = Some(Box::new(f));
|
||||
available.ctx.thread_ptr = available as *const Thread as u64;
|
||||
ptr::write(s_ptr.offset((size - 8) as isize) as *mut u64, guard as u64);
|
||||
ptr::write(s_ptr.offset((size - 16) as isize) as *mut u64, call as u64);
|
||||
available.ctx.rsp = s_ptr.offset((size - 16) as isize) as u64;
|
||||
available.state = State::Ready;
|
||||
}
|
||||
available.state = State::Ready;
|
||||
}
|
||||
}
|
||||
|
||||
fn call(thread: u64) {
|
||||
let thread = unsafe { &*(thread as *const Thread) };
|
||||
if let Some(f) = &thread.task {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
#[naked]
|
||||
fn guard() {
|
||||
unsafe {
|
||||
let rt_ptr = RUNTIME as *mut Runtime;
|
||||
(*rt_ptr).t_return();
|
||||
let rt = &mut *rt_ptr;
|
||||
println!("THREAD {} FINISHED.", rt.threads[rt.current].id);
|
||||
rt.t_return();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -413,7 +447,7 @@ unsafe fn switch(old: *mut ThreadContext, new: *const ThreadContext) {
|
||||
mov %r12, 0x20($0)
|
||||
mov %rbx, 0x28($0)
|
||||
mov %rbp, 0x30($0)
|
||||
|
||||
|
||||
mov 0x00($1), %rsp
|
||||
mov 0x08($1), %r15
|
||||
mov 0x10($1), %r14
|
||||
@@ -421,43 +455,45 @@ unsafe fn switch(old: *mut ThreadContext, new: *const ThreadContext) {
|
||||
mov 0x20($1), %r12
|
||||
mov 0x28($1), %rbx
|
||||
mov 0x30($1), %rbp
|
||||
mov 0x38($1), %rdi
|
||||
ret
|
||||
"
|
||||
:
|
||||
:"r"(old), "r"(new)
|
||||
: "r"(old), "r"(new)
|
||||
:
|
||||
: "volatile", "alignstack"
|
||||
: "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!("I haven't implemented a timer in this example.");
|
||||
yield_thread();
|
||||
println!("Finally, notice how the tasks are executed concurrently.");
|
||||
});
|
||||
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::spawn(|| {
|
||||
println!("But we can still nest tasks...");
|
||||
Runtime::spawn(|| {
|
||||
println!("...like this!");
|
||||
})
|
||||
});
|
||||
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>Still hanging in there? Good. Don't get frustrated if the code above is
|
||||
difficult to understand. If I hadn't written it myself I would probably feel
|
||||
the same. You can always go back and read the book which explains it later.</p>
|
||||
<h3><a class="header" href="#callback-based-approaches" id="callback-based-approaches">Callback based approaches</a></h3>
|
||||
<p>You probably already know what we're going to talk about in the next paragraphs
|
||||
from Javascript which I assume most know. If your exposure to Javascript has
|
||||
given you any sorts of PTSD earlier in life, close your eyes now and scroll down
|
||||
for 2-3 seconds. You'll find a link there that takes you to safety.</p>
|
||||
<p>The whole idea behind a callback based approach is to save a pointer to a set of
|
||||
instructions we want to run later. We can save that pointer on the stack before
|
||||
we yield control to the runtime, or in some sort of collection as we do below.</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>
|
||||
@@ -470,8 +506,10 @@ Rust uses today which we'll soon get to.</p>
|
||||
<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 callbacks in a task.</li>
|
||||
linearly with the number of callbacks in a chain of computations.</li>
|
||||
<li>Can be hard to reason about, many people already know this as as "callback hell".</li>
|
||||
<li>It's a very different way of writing a program, and it can be difficult to
|
||||
get an understanding of the program flow.</li>
|
||||
<li>Sharing state between tasks is a hard problem in Rust using this approach due
|
||||
to it's ownership model.</li>
|
||||
</ul>
|
||||
@@ -552,22 +590,23 @@ same thread using this example. The OS threads we create are basically just used
|
||||
as timers.</p>
|
||||
<h2><a class="header" href="#from-callbacks-to-promises" id="from-callbacks-to-promises">From callbacks to promises</a></h2>
|
||||
<p>You might start to wonder by now, when are we going to talk about Futures?</p>
|
||||
<p>Well, we're getting there. You see <code>promises</code>, <code>futures</code> and <code>deferreds</code> are
|
||||
often used interchangeably in day to day jargon. There are some formal
|
||||
differences between which is used which we'll not cover here but it's worth
|
||||
explaining promises a bit as a segway to Rusts Futures.</p>
|
||||
<p>Well, we're getting there. You see <code>promises</code>, <code>futures</code> and other names for
|
||||
deferred computations are often used interchangeably. There are formal
|
||||
differences between them but we'll not cover that here but it's worth
|
||||
explaining <code>promises</code> a bit since they're widely known due to beeing used in
|
||||
Javascript and will serve as segway to Rusts Futures.</p>
|
||||
<p>First of all, many languages has a concept of promises but I'll use the ones
|
||||
from Javascript as an example.</p>
|
||||
from Javascript in the examples below.</p>
|
||||
<p>Promises is one way to deal with the complexity which comes with a callback
|
||||
based approach.</p>
|
||||
<p>Instead of:</p>
|
||||
<pre><code class="language-js ignore">setTimer(200, () => {
|
||||
setTimer(100, () => {
|
||||
setTimer(50, () => {
|
||||
console.log("I'm the last one");
|
||||
})
|
||||
})
|
||||
})
|
||||
setTimer(100, () => {
|
||||
setTimer(50, () => {
|
||||
console.log("I'm the last one");
|
||||
});
|
||||
});
|
||||
});
|
||||
</code></pre>
|
||||
<p>We can to this:</p>
|
||||
<pre><code class="language-js ignore">function timer(ms) {
|
||||
@@ -580,12 +619,11 @@ timer(200)
|
||||
.then(() => console.log("I'm the last one));
|
||||
</code></pre>
|
||||
<p>The change is even more substantial under the hood. You see, promises return
|
||||
a state which is either <code>pending</code>, <code>fulfilled</code> or <code>rejected</code>. So when we call
|
||||
<code>timer(200)</code> in the sample above, we get back a promise in the state <code>pending</code>.</p>
|
||||
<p>A <code>promise</code> is a state machine which makes one <code>step</code> when the I/O operation
|
||||
is finished.</p>
|
||||
<p>This allows for an even better syntax where we now can write our last example
|
||||
like this:</p>
|
||||
a state machine which can be in one of three states: <code>pending</code>, <code>fulfilled</code> or
|
||||
<code>rejected</code>. So when we call <code>timer(200)</code> in the sample above, we get back a
|
||||
promise in the state <code>pending</code>.</p>
|
||||
<p>Since promises are re-written as state machines they also enable an even better
|
||||
syntax where we now can write our last example like this:</p>
|
||||
<pre><code>async function run() {
|
||||
await timer(200);
|
||||
await timer(100);
|
||||
@@ -593,12 +631,16 @@ like this:</p>
|
||||
console.log("I'm the last one");
|
||||
}
|
||||
</code></pre>
|
||||
<p>Now this is also where the similarities stop. The reason we went through all
|
||||
this is to get an introduction and get into the right mindset for exploring
|
||||
Rusts Futures.</p>
|
||||
<p>Syntactically though, this is relevant. Rusts Futures 1.0 was a lot like the
|
||||
promises example above, and Rusts Futures 3.0 is a lot like async/await
|
||||
in our last example.</p>
|
||||
<p>You can consider the <code>run</code> function a <em>pausable</em> task consisting of several
|
||||
sub-tasks. On each "await" point it yields control to the scheduler (in this
|
||||
case it's the well known Javascript event loop). Once one of the sub-tasks changes
|
||||
state to either <code>fulfilled</code> or <code>rejected</code> the task is sheduled to continue to
|
||||
the next step.</p>
|
||||
<p>Syntactically, Rusts Futures 1.0 was a lot like the promises example above and
|
||||
Rusts Futures 3.0 is a lot like async/await in our last example.</p>
|
||||
<p>Now this is also where the similarities with Rusts Futures stop. The reason we
|
||||
go through all this is to get an introduction and get into the right mindset for
|
||||
exploring Rusts Futures.</p>
|
||||
<blockquote>
|
||||
<p>To avoid confusion later on: There is one difference you should know. Javascript
|
||||
promises are <em>eagerly</em> evaluated. That means that once it's created, it starts
|
||||
@@ -617,7 +659,7 @@ need to be polled once before they do any work. You'll see in a moment.</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_futures_in_rust.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
@@ -635,7 +677,7 @@ need to be polled once before they do any work. You'll see in a moment.</p>
|
||||
|
||||
|
||||
|
||||
<a href="1_background_information.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a href="1_futures_in_rust.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Some background information - Futures Explained in 200 Lines of Rust</title>
|
||||
<title>Futures in Rust - Futures Explained in 200 Lines of Rust</title>
|
||||
|
||||
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
@@ -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_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>
|
||||
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html" class="active"><strong aria-hidden="true">2.</strong> Futures in Rust</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>
|
||||
@@ -149,7 +149,7 @@
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1><a class="header" href="#some-background-information" id="some-background-information">Some background information</a></h1>
|
||||
<h1><a class="header" href="#futures-in-rust" id="futures-in-rust">Futures in Rust</a></h1>
|
||||
<blockquote>
|
||||
<p><strong>Relevant for:</strong></p>
|
||||
<ul>
|
||||
@@ -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="1_why_futures.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="0_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>
|
||||
|
||||
@@ -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="1_why_futures.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a href="0_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>
|
||||
|
||||
@@ -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_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>
|
||||
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</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>
|
||||
@@ -315,7 +315,7 @@ use purely global functions and state, or any other way you wish.</p>
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
|
||||
<a rel="prev" href="1_background_information.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="1_futures_in_rust.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
@@ -333,7 +333,7 @@ use purely global functions and state, or any other way you wish.</p>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
|
||||
<a href="1_background_information.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a href="1_futures_in_rust.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li 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>
|
||||
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</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>
|
||||
|
||||
@@ -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_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>
|
||||
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</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>
|
||||
|
||||
@@ -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_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>
|
||||
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</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>
|
||||
|
||||
@@ -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_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>
|
||||
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</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>
|
||||
|
||||
@@ -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_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>
|
||||
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</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>
|
||||
|
||||
@@ -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_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>
|
||||
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</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>
|
||||
|
||||
@@ -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_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>
|
||||
<ol class="chapter"><li class="affix"><a href="introduction.html" class="active">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</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_why_futures.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next" href="0_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>
|
||||
|
||||
@@ -211,7 +211,7 @@ very well written and very helpful. So thanks!</p>
|
||||
|
||||
|
||||
|
||||
<a href="1_why_futures.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a href="0_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>
|
||||
|
||||
|
||||
196
book/print.html
196
book/print.html
@@ -80,7 +80,7 @@
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li 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>
|
||||
<ol class="chapter"><li class="affix"><a href="introduction.html">Introduction</a></li><li><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li><a href="1_futures_in_rust.html"><strong aria-hidden="true">2.</strong> Futures in Rust</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,14 +191,22 @@ 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>
|
||||
<h1><a class="header" href="#some-background-information" id="some-background-information">Some Background Information</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>
|
||||
<p>While we do that we'll get some information on concurrency which will make it
|
||||
easier for us when we dive in to Futures specifically.</p>
|
||||
<blockquote>
|
||||
<p>For fun, I've added a small snipped of runnable code with most of the examples.
|
||||
If you're like me, things get way more interesting then and maybe you'll se some
|
||||
things you haven't seen before along the way.</p>
|
||||
</blockquote>
|
||||
<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
|
||||
<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>The runtime we use to handle concurrency for us is the operating system itself.</p>
|
||||
<p><strong>Advantages:</strong></p>
|
||||
<ul>
|
||||
<li>Simple</li>
|
||||
@@ -210,14 +218,14 @@ accomplish and write code like we normally would.</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>
|
||||
run out of memory pretty fast.</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>
|
||||
<p><strong>Using OS threads in Rust looks like this:</strong></p>
|
||||
<pre><pre class="playpen"><code class="language-rust">use std::thread;
|
||||
|
||||
fn main() {
|
||||
@@ -245,18 +253,20 @@ fn main() {
|
||||
<p>OS threads sure has some pretty big advantages. So why all this talk about
|
||||
"async" and concurrency in the first place?</p>
|
||||
<p>First of all. For computers to be <a href="https://en.wikipedia.org/wiki/Efficiency"><em>efficient</em></a> it needs to multitask. Once you
|
||||
start to look under the covers (like <a href="https://os.phil-opp.com/async-await/">how an operating system works</a>)
|
||||
start to look under the covers (like <a href="https://os.phil-opp.com/async-await/">how an operating system works</a>)
|
||||
you'll see concurrency everywhere. It's very fundamental in everything we do.</p>
|
||||
<p>Secondly, we have the web. Webservers is all about I/O and handling small tasks
|
||||
(requests). When the number of small tasks is large it's not a good fit for OS
|
||||
threads as of today because of the memory they require and the overhead involved
|
||||
when creating new threads. That's why you'll see so many async web frameworks
|
||||
and database drivers today.</p>
|
||||
<p>However, for a huge number of tasks, the standard OS threads will often be the
|
||||
when creating new threads. This gets even more relevant when the load is variable
|
||||
which means the current number of tasks a program has at any point in time is
|
||||
unpredictable. That's why you'll see so many async web frameworks and database
|
||||
drivers today.</p>
|
||||
<p>However, for a huge number of problems, the standard OS threads will often be the
|
||||
right solution. So, just think twice about your problem before you reach for an
|
||||
async library.</p>
|
||||
<p>Now, let's look at some other options for multitasking. They all have in common
|
||||
that they implement a way to do multitasking by implementing a "userland"
|
||||
that they implement a way to do multitasking by having a "userland"
|
||||
runtime:</p>
|
||||
<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
|
||||
@@ -297,10 +307,12 @@ platforms.</li>
|
||||
<p>If you were to implement green threads in Rust, it could look something like
|
||||
this:</p>
|
||||
<blockquote>
|
||||
<p>The example presented below is from an earlier book I wrote about green
|
||||
threads called <a href="https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/">Green Threads Explained in 200 lines of Rust.</a>
|
||||
<p>The example presented below is an adapted example from an earlier gitbook I
|
||||
wrote about green threads called <a href="https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/">Green Threads Explained in 200 lines of Rust.</a>
|
||||
If you want to know what's going on you'll find everything explained in detail
|
||||
in that book.</p>
|
||||
in that book. The code below is wildly unsafe and it's just to show a real example.
|
||||
It's not in any way meant to showcase "best practice". Just so we're on
|
||||
the same page.</p>
|
||||
</blockquote>
|
||||
<pre><pre class="playpen"><code class="language-rust">#![feature(asm)]
|
||||
#![feature(naked_functions)]
|
||||
@@ -327,6 +339,7 @@ struct Thread {
|
||||
stack: Vec<u8>,
|
||||
ctx: ThreadContext,
|
||||
state: State,
|
||||
task: Option<Box<dyn Fn()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@@ -339,6 +352,7 @@ struct ThreadContext {
|
||||
r12: u64,
|
||||
rbx: u64,
|
||||
rbp: u64,
|
||||
thread_ptr: u64,
|
||||
}
|
||||
|
||||
impl Thread {
|
||||
@@ -348,6 +362,7 @@ impl Thread {
|
||||
stack: vec![0_u8; DEFAULT_STACK_SIZE],
|
||||
ctx: ThreadContext::default(),
|
||||
state: State::Available,
|
||||
task: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -359,11 +374,14 @@ impl Runtime {
|
||||
stack: vec![0_u8; DEFAULT_STACK_SIZE],
|
||||
ctx: ThreadContext::default(),
|
||||
state: State::Running,
|
||||
task: None,
|
||||
};
|
||||
|
||||
let mut threads = vec![base_thread];
|
||||
threads[0].ctx.thread_ptr = &threads[0] as *const Thread as u64;
|
||||
let mut available_threads: Vec<Thread> = (1..MAX_THREADS).map(|i| Thread::new(i)).collect();
|
||||
threads.append(&mut available_threads);
|
||||
|
||||
Runtime {
|
||||
threads,
|
||||
current: 0,
|
||||
@@ -400,40 +418,56 @@ impl Runtime {
|
||||
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
|
||||
true
|
||||
}
|
||||
|
||||
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();
|
||||
pub fn spawn<F: Fn() + 'static>(f: F){
|
||||
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;
|
||||
let rt_ptr = RUNTIME as *mut Runtime;
|
||||
let available = (*rt_ptr)
|
||||
.threads
|
||||
.iter_mut()
|
||||
.find(|t| t.state == State::Available)
|
||||
.expect("no available thread.");
|
||||
|
||||
let size = available.stack.len();
|
||||
let s_ptr = available.stack.as_mut_ptr();
|
||||
available.task = Some(Box::new(f));
|
||||
available.ctx.thread_ptr = available as *const Thread as u64;
|
||||
ptr::write(s_ptr.offset((size - 8) as isize) as *mut u64, guard as u64);
|
||||
ptr::write(s_ptr.offset((size - 16) as isize) as *mut u64, call as u64);
|
||||
available.ctx.rsp = s_ptr.offset((size - 16) as isize) as u64;
|
||||
available.state = State::Ready;
|
||||
}
|
||||
available.state = State::Ready;
|
||||
}
|
||||
}
|
||||
|
||||
fn call(thread: u64) {
|
||||
let thread = unsafe { &*(thread as *const Thread) };
|
||||
if let Some(f) = &thread.task {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
#[naked]
|
||||
fn guard() {
|
||||
unsafe {
|
||||
let rt_ptr = RUNTIME as *mut Runtime;
|
||||
(*rt_ptr).t_return();
|
||||
let rt = &mut *rt_ptr;
|
||||
println!("THREAD {} FINISHED.", rt.threads[rt.current].id);
|
||||
rt.t_return();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -455,7 +489,7 @@ unsafe fn switch(old: *mut ThreadContext, new: *const ThreadContext) {
|
||||
mov %r12, 0x20($0)
|
||||
mov %rbx, 0x28($0)
|
||||
mov %rbp, 0x30($0)
|
||||
|
||||
|
||||
mov 0x00($1), %rsp
|
||||
mov 0x08($1), %r15
|
||||
mov 0x10($1), %r14
|
||||
@@ -463,43 +497,45 @@ unsafe fn switch(old: *mut ThreadContext, new: *const ThreadContext) {
|
||||
mov 0x20($1), %r12
|
||||
mov 0x28($1), %rbx
|
||||
mov 0x30($1), %rbp
|
||||
mov 0x38($1), %rdi
|
||||
ret
|
||||
"
|
||||
:
|
||||
:"r"(old), "r"(new)
|
||||
: "r"(old), "r"(new)
|
||||
:
|
||||
: "volatile", "alignstack"
|
||||
: "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!("I haven't implemented a timer in this example.");
|
||||
yield_thread();
|
||||
println!("Finally, notice how the tasks are executed concurrently.");
|
||||
});
|
||||
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::spawn(|| {
|
||||
println!("But we can still nest tasks...");
|
||||
Runtime::spawn(|| {
|
||||
println!("...like this!");
|
||||
})
|
||||
});
|
||||
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>Still hanging in there? Good. Don't get frustrated if the code above is
|
||||
difficult to understand. If I hadn't written it myself I would probably feel
|
||||
the same. You can always go back and read the book which explains it later.</p>
|
||||
<h3><a class="header" href="#callback-based-approaches" id="callback-based-approaches">Callback based approaches</a></h3>
|
||||
<p>You probably already know what we're going to talk about in the next paragraphs
|
||||
from Javascript which I assume most know. If your exposure to Javascript has
|
||||
given you any sorts of PTSD earlier in life, close your eyes now and scroll down
|
||||
for 2-3 seconds. You'll find a link there that takes you to safety.</p>
|
||||
<p>The whole idea behind a callback based approach is to save a pointer to a set of
|
||||
instructions we want to run later. We can save that pointer on the stack before
|
||||
we yield control to the runtime, or in some sort of collection as we do below.</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>
|
||||
@@ -512,8 +548,10 @@ Rust uses today which we'll soon get to.</p>
|
||||
<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 callbacks in a task.</li>
|
||||
linearly with the number of callbacks in a chain of computations.</li>
|
||||
<li>Can be hard to reason about, many people already know this as as "callback hell".</li>
|
||||
<li>It's a very different way of writing a program, and it can be difficult to
|
||||
get an understanding of the program flow.</li>
|
||||
<li>Sharing state between tasks is a hard problem in Rust using this approach due
|
||||
to it's ownership model.</li>
|
||||
</ul>
|
||||
@@ -594,22 +632,23 @@ same thread using this example. The OS threads we create are basically just used
|
||||
as timers.</p>
|
||||
<h2><a class="header" href="#from-callbacks-to-promises" id="from-callbacks-to-promises">From callbacks to promises</a></h2>
|
||||
<p>You might start to wonder by now, when are we going to talk about Futures?</p>
|
||||
<p>Well, we're getting there. You see <code>promises</code>, <code>futures</code> and <code>deferreds</code> are
|
||||
often used interchangeably in day to day jargon. There are some formal
|
||||
differences between which is used which we'll not cover here but it's worth
|
||||
explaining promises a bit as a segway to Rusts Futures.</p>
|
||||
<p>Well, we're getting there. You see <code>promises</code>, <code>futures</code> and other names for
|
||||
deferred computations are often used interchangeably. There are formal
|
||||
differences between them but we'll not cover that here but it's worth
|
||||
explaining <code>promises</code> a bit since they're widely known due to beeing used in
|
||||
Javascript and will serve as segway to Rusts Futures.</p>
|
||||
<p>First of all, many languages has a concept of promises but I'll use the ones
|
||||
from Javascript as an example.</p>
|
||||
from Javascript in the examples below.</p>
|
||||
<p>Promises is one way to deal with the complexity which comes with a callback
|
||||
based approach.</p>
|
||||
<p>Instead of:</p>
|
||||
<pre><code class="language-js ignore">setTimer(200, () => {
|
||||
setTimer(100, () => {
|
||||
setTimer(50, () => {
|
||||
console.log("I'm the last one");
|
||||
})
|
||||
})
|
||||
})
|
||||
setTimer(100, () => {
|
||||
setTimer(50, () => {
|
||||
console.log("I'm the last one");
|
||||
});
|
||||
});
|
||||
});
|
||||
</code></pre>
|
||||
<p>We can to this:</p>
|
||||
<pre><code class="language-js ignore">function timer(ms) {
|
||||
@@ -622,12 +661,11 @@ timer(200)
|
||||
.then(() => console.log("I'm the last one));
|
||||
</code></pre>
|
||||
<p>The change is even more substantial under the hood. You see, promises return
|
||||
a state which is either <code>pending</code>, <code>fulfilled</code> or <code>rejected</code>. So when we call
|
||||
<code>timer(200)</code> in the sample above, we get back a promise in the state <code>pending</code>.</p>
|
||||
<p>A <code>promise</code> is a state machine which makes one <code>step</code> when the I/O operation
|
||||
is finished.</p>
|
||||
<p>This allows for an even better syntax where we now can write our last example
|
||||
like this:</p>
|
||||
a state machine which can be in one of three states: <code>pending</code>, <code>fulfilled</code> or
|
||||
<code>rejected</code>. So when we call <code>timer(200)</code> in the sample above, we get back a
|
||||
promise in the state <code>pending</code>.</p>
|
||||
<p>Since promises are re-written as state machines they also enable an even better
|
||||
syntax where we now can write our last example like this:</p>
|
||||
<pre><code>async function run() {
|
||||
await timer(200);
|
||||
await timer(100);
|
||||
@@ -635,19 +673,23 @@ like this:</p>
|
||||
console.log("I'm the last one");
|
||||
}
|
||||
</code></pre>
|
||||
<p>Now this is also where the similarities stop. The reason we went through all
|
||||
this is to get an introduction and get into the right mindset for exploring
|
||||
Rusts Futures.</p>
|
||||
<p>Syntactically though, this is relevant. Rusts Futures 1.0 was a lot like the
|
||||
promises example above, and Rusts Futures 3.0 is a lot like async/await
|
||||
in our last example.</p>
|
||||
<p>You can consider the <code>run</code> function a <em>pausable</em> task consisting of several
|
||||
sub-tasks. On each "await" point it yields control to the scheduler (in this
|
||||
case it's the well known Javascript event loop). Once one of the sub-tasks changes
|
||||
state to either <code>fulfilled</code> or <code>rejected</code> the task is sheduled to continue to
|
||||
the next step.</p>
|
||||
<p>Syntactically, Rusts Futures 1.0 was a lot like the promises example above and
|
||||
Rusts Futures 3.0 is a lot like async/await in our last example.</p>
|
||||
<p>Now this is also where the similarities with Rusts Futures stop. The reason we
|
||||
go through all this is to get an introduction and get into the right mindset for
|
||||
exploring Rusts Futures.</p>
|
||||
<blockquote>
|
||||
<p>To avoid confusion later on: There is one difference you should know. Javascript
|
||||
promises are <em>eagerly</em> evaluated. That means that once it's created, it starts
|
||||
running a task. Rusts Futures on the other hand is <em>lazily</em> evaluated. They
|
||||
need to be polled once before they do any work. You'll see in a moment.</p>
|
||||
</blockquote>
|
||||
<h1><a class="header" href="#some-background-information" id="some-background-information">Some background information</a></h1>
|
||||
<h1><a class="header" href="#futures-in-rust" id="futures-in-rust">Futures in Rust</a></h1>
|
||||
<blockquote>
|
||||
<p><strong>Relevant for:</strong></p>
|
||||
<ul>
|
||||
@@ -1724,7 +1766,7 @@ extra care must be taken when implementing <code>Drop</code> for pinned types.</
|
||||
<h2><a class="header" href="#putting-it-all-together" id="putting-it-all-together">Putting it all together</a></h2>
|
||||
<p>This is exactly what we'll do when we implement our own <code>Futures</code> stay tuned,
|
||||
we're soon finished.</p>
|
||||
<h1><a class="header" href="#futures-in-rust" id="futures-in-rust">Futures in Rust</a></h1>
|
||||
<h1><a class="header" href="#futures-in-rust-1" id="futures-in-rust-1">Futures in Rust</a></h1>
|
||||
<p>We'll create our own <code>Futures</code> together with a fake reactor and a simple
|
||||
executor which allows you to edit, run an play around with the code right here
|
||||
in your browser.</p>
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,15 +1,24 @@
|
||||
# Why Futures
|
||||
# Some Background Information
|
||||
|
||||
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.
|
||||
|
||||
While we do that we'll get some information on concurrency which will make it
|
||||
easier for us when we dive in to Futures specifically.
|
||||
|
||||
> For fun, I've added a small snipped of runnable code with most of the examples.
|
||||
> If you're like me, things get way more interesting then and maybe you'll se some
|
||||
> things you haven't seen before along the way.
|
||||
|
||||
## Threads provided by the operating system
|
||||
|
||||
Now one way of accomplishing this is letting the OS take care of everything for
|
||||
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.
|
||||
|
||||
The runtime we use to handle concurrency for us is the operating system itself.
|
||||
|
||||
**Advantages:**
|
||||
|
||||
- Simple
|
||||
@@ -20,15 +29,15 @@ accomplish and write code like we normally would.
|
||||
**Drawbacks:**
|
||||
|
||||
- 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.
|
||||
waiting simultaneously (like you would in a web-server under heavy load) you'll
|
||||
run out of memory pretty fast.
|
||||
- There are a lot of syscalls involved. This can be pretty costly when the number
|
||||
of tasks is high.
|
||||
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.
|
||||
thread as fast as you'd wish.
|
||||
- Might not be an option on some systems
|
||||
|
||||
Using OS threads in Rust looks like this:
|
||||
**Using OS threads in Rust looks like this:**
|
||||
|
||||
```rust
|
||||
use std::thread;
|
||||
@@ -60,21 +69,23 @@ OS threads sure has some pretty big advantages. So why all this talk about
|
||||
"async" and concurrency in the first place?
|
||||
|
||||
First of all. For computers to be [_efficient_](https://en.wikipedia.org/wiki/Efficiency) it needs to multitask. Once you
|
||||
start to look under the covers (like [how an operating system works](https://os.phil-opp.com/async-await/))
|
||||
start to look under the covers (like [how an operating system works](https://os.phil-opp.com/async-await/))
|
||||
you'll see concurrency everywhere. It's very fundamental in everything we do.
|
||||
|
||||
Secondly, we have the web. Webservers is all about I/O and handling small tasks
|
||||
(requests). When the number of small tasks is large it's not a good fit for OS
|
||||
threads as of today because of the memory they require and the overhead involved
|
||||
when creating new threads. That's why you'll see so many async web frameworks
|
||||
and database drivers today.
|
||||
when creating new threads. This gets even more relevant when the load is variable
|
||||
which means the current number of tasks a program has at any point in time is
|
||||
unpredictable. That's why you'll see so many async web frameworks and database
|
||||
drivers today.
|
||||
|
||||
However, for a huge number of tasks, the standard OS threads will often be the
|
||||
However, for a huge number of problems, the standard OS threads will often be the
|
||||
right solution. So, just think twice about your problem before you reach for an
|
||||
async library.
|
||||
|
||||
Now, let's look at some other options for multitasking. They all have in common
|
||||
that they implement a way to do multitasking by implementing a "userland"
|
||||
that they implement a way to do multitasking by having a "userland"
|
||||
runtime:
|
||||
|
||||
## Green threads
|
||||
@@ -91,9 +102,9 @@ 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
|
||||
"jumps" to that stack
|
||||
4. Run some non-blocking code on the new thread until a new blocking call or the
|
||||
task is finished
|
||||
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
|
||||
@@ -104,26 +115,28 @@ second as you read this.
|
||||
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.
|
||||
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.
|
||||
which puts a lot of control in the hands of the runtime implementors.
|
||||
|
||||
**Drawbacks:**
|
||||
|
||||
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_ (Rust had green threads early on and this
|
||||
was one of the reasons they were removed).
|
||||
1. Complicated to implement correctly if you want to support many different
|
||||
platforms.
|
||||
was one of the reasons they were removed).
|
||||
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 you'll find everything explained in detail
|
||||
>in that book.
|
||||
> The example presented below is an adapted example from an earlier gitbook 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 you'll find everything explained in detail
|
||||
> in that book. The code below is wildly unsafe and it's just to show a real example.
|
||||
> It's not in any way meant to showcase "best practice". Just so we're on
|
||||
> the same page.
|
||||
|
||||
```rust
|
||||
#![feature(asm)]
|
||||
@@ -151,6 +164,7 @@ struct Thread {
|
||||
stack: Vec<u8>,
|
||||
ctx: ThreadContext,
|
||||
state: State,
|
||||
task: Option<Box<dyn Fn()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@@ -163,6 +177,7 @@ struct ThreadContext {
|
||||
r12: u64,
|
||||
rbx: u64,
|
||||
rbp: u64,
|
||||
thread_ptr: u64,
|
||||
}
|
||||
|
||||
impl Thread {
|
||||
@@ -172,6 +187,7 @@ impl Thread {
|
||||
stack: vec![0_u8; DEFAULT_STACK_SIZE],
|
||||
ctx: ThreadContext::default(),
|
||||
state: State::Available,
|
||||
task: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,11 +199,14 @@ impl Runtime {
|
||||
stack: vec![0_u8; DEFAULT_STACK_SIZE],
|
||||
ctx: ThreadContext::default(),
|
||||
state: State::Running,
|
||||
task: None,
|
||||
};
|
||||
|
||||
let mut threads = vec![base_thread];
|
||||
threads[0].ctx.thread_ptr = &threads[0] as *const Thread as u64;
|
||||
let mut available_threads: Vec<Thread> = (1..MAX_THREADS).map(|i| Thread::new(i)).collect();
|
||||
threads.append(&mut available_threads);
|
||||
|
||||
Runtime {
|
||||
threads,
|
||||
current: 0,
|
||||
@@ -224,40 +243,56 @@ impl Runtime {
|
||||
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
|
||||
true
|
||||
}
|
||||
|
||||
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();
|
||||
pub fn spawn<F: Fn() + 'static>(f: F){
|
||||
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;
|
||||
let rt_ptr = RUNTIME as *mut Runtime;
|
||||
let available = (*rt_ptr)
|
||||
.threads
|
||||
.iter_mut()
|
||||
.find(|t| t.state == State::Available)
|
||||
.expect("no available thread.");
|
||||
|
||||
let size = available.stack.len();
|
||||
let s_ptr = available.stack.as_mut_ptr();
|
||||
available.task = Some(Box::new(f));
|
||||
available.ctx.thread_ptr = available as *const Thread as u64;
|
||||
ptr::write(s_ptr.offset((size - 8) as isize) as *mut u64, guard as u64);
|
||||
ptr::write(s_ptr.offset((size - 16) as isize) as *mut u64, call as u64);
|
||||
available.ctx.rsp = s_ptr.offset((size - 16) as isize) as u64;
|
||||
available.state = State::Ready;
|
||||
}
|
||||
available.state = State::Ready;
|
||||
}
|
||||
}
|
||||
|
||||
fn call(thread: u64) {
|
||||
let thread = unsafe { &*(thread as *const Thread) };
|
||||
if let Some(f) = &thread.task {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
#[naked]
|
||||
fn guard() {
|
||||
unsafe {
|
||||
let rt_ptr = RUNTIME as *mut Runtime;
|
||||
(*rt_ptr).t_return();
|
||||
let rt = &mut *rt_ptr;
|
||||
println!("THREAD {} FINISHED.", rt.threads[rt.current].id);
|
||||
rt.t_return();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -279,7 +314,7 @@ unsafe fn switch(old: *mut ThreadContext, new: *const ThreadContext) {
|
||||
mov %r12, 0x20($0)
|
||||
mov %rbx, 0x28($0)
|
||||
mov %rbp, 0x30($0)
|
||||
|
||||
|
||||
mov 0x00($1), %rsp
|
||||
mov 0x08($1), %r15
|
||||
mov 0x10($1), %r14
|
||||
@@ -287,45 +322,49 @@ unsafe fn switch(old: *mut ThreadContext, new: *const ThreadContext) {
|
||||
mov 0x20($1), %r12
|
||||
mov 0x28($1), %rbx
|
||||
mov 0x30($1), %rbp
|
||||
mov 0x38($1), %rdi
|
||||
ret
|
||||
"
|
||||
:
|
||||
:"r"(old), "r"(new)
|
||||
: "r"(old), "r"(new)
|
||||
:
|
||||
: "volatile", "alignstack"
|
||||
: "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!("I haven't implemented a timer in this example.");
|
||||
yield_thread();
|
||||
println!("Finally, notice how the tasks are executed concurrently.");
|
||||
});
|
||||
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::spawn(|| {
|
||||
println!("But we can still nest tasks...");
|
||||
Runtime::spawn(|| {
|
||||
println!("...like this!");
|
||||
})
|
||||
});
|
||||
runtime.run();
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Callback based approach
|
||||
Still hanging in there? Good. Don't get frustrated if the code above is
|
||||
difficult to understand. If I hadn't written it myself I would probably feel
|
||||
the same. You can always go back and read the book which explains it later.
|
||||
|
||||
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.
|
||||
### Callback based approaches
|
||||
|
||||
You probably already know what we're going to talk about in the next paragraphs
|
||||
from Javascript which I assume most know. If your exposure to Javascript has
|
||||
given you any sorts of PTSD earlier in life, close your eyes now and scroll down
|
||||
for 2-3 seconds. You'll find a link there that takes you to safety.
|
||||
|
||||
The whole idea behind a callback based approach is to save a pointer to a set of
|
||||
instructions we want to run later. We can save that pointer on the stack before
|
||||
we yield control to the runtime, or in some sort of collection as we do below.
|
||||
|
||||
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
|
||||
@@ -340,10 +379,12 @@ Rust uses today which we'll soon get to.
|
||||
**Drawbacks:**
|
||||
|
||||
- Each task must save the state it needs for later, the memory usage will grow
|
||||
linearly with the number of callbacks in a task.
|
||||
linearly with the number of callbacks in a chain of computations.
|
||||
- Can be hard to reason about, many people already know this as as "callback hell".
|
||||
- It's a very different way of writing a program, and it can be difficult to
|
||||
get an understanding of the program flow.
|
||||
- Sharing state between tasks is a hard problem in Rust using this approach due
|
||||
to it's ownership model.
|
||||
to it's ownership model.
|
||||
|
||||
An extremely simplified example of a how a callback based approach could look
|
||||
like is:
|
||||
@@ -428,13 +469,14 @@ as timers.
|
||||
|
||||
You might start to wonder by now, when are we going to talk about Futures?
|
||||
|
||||
Well, we're getting there. You see `promises`, `futures` and `deferreds` are
|
||||
often used interchangeably in day to day jargon. There are some formal
|
||||
differences between which is used which we'll not cover here but it's worth
|
||||
explaining promises a bit as a segway to Rusts Futures.
|
||||
Well, we're getting there. You see `promises`, `futures` and other names for
|
||||
deferred computations are often used interchangeably. There are formal
|
||||
differences between them but we'll not cover that here but it's worth
|
||||
explaining `promises` a bit since they're widely known due to beeing used in
|
||||
Javascript and will serve as segway to Rusts Futures.
|
||||
|
||||
First of all, many languages has a concept of promises but I'll use the ones
|
||||
from Javascript as an example.
|
||||
from Javascript in the examples below.
|
||||
|
||||
Promises is one way to deal with the complexity which comes with a callback
|
||||
based approach.
|
||||
@@ -443,12 +485,12 @@ Instead of:
|
||||
|
||||
```js, ignore
|
||||
setTimer(200, () => {
|
||||
setTimer(100, () => {
|
||||
setTimer(50, () => {
|
||||
console.log("I'm the last one");
|
||||
})
|
||||
})
|
||||
})
|
||||
setTimer(100, () => {
|
||||
setTimer(50, () => {
|
||||
console.log("I'm the last one");
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
We can to this:
|
||||
@@ -465,14 +507,12 @@ timer(200)
|
||||
```
|
||||
|
||||
The change is even more substantial under the hood. You see, promises return
|
||||
a state which is either `pending`, `fulfilled` or `rejected`. So when we call
|
||||
`timer(200)` in the sample above, we get back a promise in the state `pending`.
|
||||
a state machine which can be in one of three states: `pending`, `fulfilled` or
|
||||
`rejected`. So when we call `timer(200)` in the sample above, we get back a
|
||||
promise in the state `pending`.
|
||||
|
||||
A `promise` is a state machine which makes one `step` when the I/O operation
|
||||
is finished.
|
||||
|
||||
This allows for an even better syntax where we now can write our last example
|
||||
like this:
|
||||
Since promises are re-written as state machines they also enable an even better
|
||||
syntax where we now can write our last example like this:
|
||||
|
||||
```
|
||||
async function run() {
|
||||
@@ -483,15 +523,22 @@ async function run() {
|
||||
}
|
||||
```
|
||||
|
||||
Now this is also where the similarities stop. The reason we went through all
|
||||
this is to get an introduction and get into the right mindset for exploring
|
||||
Rusts Futures.
|
||||
You can consider the `run` function a _pausable_ task consisting of several
|
||||
sub-tasks. On each "await" point it yields control to the scheduler (in this
|
||||
case it's the well known Javascript event loop). Once one of the sub-tasks changes
|
||||
state to either `fulfilled` or `rejected` the task is sheduled to continue to
|
||||
the next step.
|
||||
|
||||
Syntactically though, this is relevant. Rusts Futures 1.0 was a lot like the
|
||||
promises example above, and Rusts Futures 3.0 is a lot like async/await
|
||||
in our last example.
|
||||
Syntactically, Rusts Futures 1.0 was a lot like the promises example above and
|
||||
Rusts Futures 3.0 is a lot like async/await in our last example.
|
||||
|
||||
>To avoid confusion later on: There is one difference you should know. Javascript
|
||||
>promises are _eagerly_ evaluated. That means that once it's created, it starts
|
||||
>running a task. Rusts Futures on the other hand is _lazily_ evaluated. They
|
||||
>need to be polled once before they do any work. You'll see in a moment.
|
||||
Now this is also where the similarities with Rusts Futures stop. The reason we
|
||||
go through all this is to get an introduction and get into the right mindset for
|
||||
exploring Rusts Futures.
|
||||
|
||||
|
||||
|
||||
> To avoid confusion later on: There is one difference you should know. Javascript
|
||||
> promises are _eagerly_ evaluated. That means that once it's created, it starts
|
||||
> running a task. Rusts Futures on the other hand is _lazily_ evaluated. They
|
||||
> need to be polled once before they do any work. You'll see in a moment.
|
||||
@@ -1,4 +1,4 @@
|
||||
# Some background information
|
||||
# Futures in Rust
|
||||
|
||||
> **Relevant for:**
|
||||
>
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
[Introduction](./introduction.md)
|
||||
|
||||
- [Why Futures](./1_why_futures.md)
|
||||
- [Some background information](./1_background_information.md)
|
||||
- [Background information](./0_background_information.md)
|
||||
- [Futures in Rust](./1_futures_in_rust.md)
|
||||
- [Waker and Context](./2_waker_context.md)
|
||||
- [Generators](./3_generators_pin.md)
|
||||
- [Pin](./4_pin.md)
|
||||
|
||||
Reference in New Issue
Block a user