merged with latest changes and made some additional corrections
This commit is contained in:
@@ -213,11 +213,11 @@ fn main() {
|
||||
<p>First, for computers to be <a href="https://en.wikipedia.org/wiki/Efficiency"><em>efficient</em></a> they need 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>)
|
||||
you'll see concurrency everywhere. It's very fundamental in everything we do.</p>
|
||||
<p>Second, we have the web. </p>
|
||||
<p>Secondly, we have the web.</p>
|
||||
<p>Web servers are 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. </p>
|
||||
when creating new threads.</p>
|
||||
<p>This gets even more problematic 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>
|
||||
@@ -235,7 +235,7 @@ task(thread) to another by doing a "context switch".</p>
|
||||
such a system) which then continues running a different task.</p>
|
||||
<p>Rust had green threads once, but they were removed before it hit 1.0. The state
|
||||
of execution is stored in each stack so in such a solution there would be no
|
||||
need for <code>async</code>, <code>await</code>, <code>Futures</code> or <code>Pin</code>. </p>
|
||||
need for <code>async</code>, <code>await</code>, <code>Future</code> or <code>Pin</code>.</p>
|
||||
<p><strong>The typical flow looks like this:</strong></p>
|
||||
<ol>
|
||||
<li>Run some non-blocking code.</li>
|
||||
@@ -276,26 +276,26 @@ in that book. The code below is wildly unsafe and it's just to show a real examp
|
||||
It's not in any way meant to showcase "best practice". Just so we're on
|
||||
the same page.</p>
|
||||
</blockquote>
|
||||
<p><em><strong>Press the expand icon in the top right corner to show the example code.</strong></em> </p>
|
||||
<p><em><strong>Press the expand icon in the top right corner to show the example code.</strong></em></p>
|
||||
<pre><pre class="playpen"><code class="language-rust edition2018"># #![feature(asm, 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>,
|
||||
@@ -303,7 +303,7 @@ the same page.</p>
|
||||
# state: State,
|
||||
# task: Option<Box<dyn Fn()>>,
|
||||
# }
|
||||
#
|
||||
#
|
||||
# #[derive(Debug, Default)]
|
||||
# #[repr(C)]
|
||||
# struct ThreadContext {
|
||||
@@ -316,7 +316,7 @@ the same page.</p>
|
||||
# rbp: u64,
|
||||
# thread_ptr: u64,
|
||||
# }
|
||||
#
|
||||
#
|
||||
# impl Thread {
|
||||
# fn new(id: usize) -> Self {
|
||||
# Thread {
|
||||
@@ -328,7 +328,7 @@ the same page.</p>
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
#
|
||||
#
|
||||
# impl Runtime {
|
||||
# pub fn new() -> Self {
|
||||
# let base_thread = Thread {
|
||||
@@ -338,37 +338,37 @@ the same page.</p>
|
||||
# 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,
|
||||
# }
|
||||
# }
|
||||
#
|
||||
#
|
||||
# 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 {
|
||||
@@ -380,21 +380,21 @@ the same page.</p>
|
||||
# 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);
|
||||
# }
|
||||
# true
|
||||
# }
|
||||
#
|
||||
#
|
||||
# pub fn spawn<F: Fn() + 'static>(f: F){
|
||||
# unsafe {
|
||||
# let rt_ptr = RUNTIME as *mut Runtime;
|
||||
@@ -403,7 +403,7 @@ the same page.</p>
|
||||
# .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));
|
||||
@@ -415,14 +415,14 @@ the same page.</p>
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
#
|
||||
#
|
||||
# fn call(thread: u64) {
|
||||
# let thread = unsafe { &*(thread as *const Thread) };
|
||||
# if let Some(f) = &thread.task {
|
||||
# f();
|
||||
# }
|
||||
# }
|
||||
#
|
||||
#
|
||||
# #[naked]
|
||||
# fn guard() {
|
||||
# unsafe {
|
||||
@@ -432,14 +432,14 @@ the same page.</p>
|
||||
# rt.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) {
|
||||
@@ -451,7 +451,7 @@ the same page.</p>
|
||||
# mov %r12, 0x20($0)
|
||||
# mov %rbx, 0x28($0)
|
||||
# mov %rbp, 0x30($0)
|
||||
#
|
||||
#
|
||||
# mov 0x00($1), %rsp
|
||||
# mov 0x08($1), %r15
|
||||
# mov 0x10($1), %r14
|
||||
@@ -493,7 +493,7 @@ 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>
|
||||
<h2><a class="header" href="#callback-based-approaches" id="callback-based-approaches">Callback based approaches</a></h2>
|
||||
<p>You probably already know what we're going to talk about in the next paragraphs
|
||||
from JavaScript which I assume most know. </p>
|
||||
from JavaScript which I assume most know.</p>
|
||||
<blockquote>
|
||||
<p>If your exposure to JavaScript callbacks 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
|
||||
@@ -600,8 +600,8 @@ same thread using this example. The OS threads we create are basically just used
|
||||
as timers but could represent any kind of resource that we'll have to wait for.</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 other names for
|
||||
deferred computations are often used interchangeably. </p>
|
||||
<p>Well, we're getting there. You see Promises, Futures and other names for
|
||||
deferred computations are often used interchangeably.</p>
|
||||
<p>There are formal differences between them, but we won't cover those
|
||||
here. It's worth explaining <code>promises</code> a bit since they're widely known due to
|
||||
their use in JavaScript. Promises also have a lot in common with Rust's Futures.</p>
|
||||
@@ -629,8 +629,8 @@ 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 machine which can be in one of three states: <code>pending</code>, <code>fulfilled</code> or
|
||||
<code>rejected</code>. </p>
|
||||
a state machine which can be in one of three states: <code>pending</code>, <code>fulfilled</code> or
|
||||
<code>rejected</code>.</p>
|
||||
<p>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 which allows us to write our last example like this:</p>
|
||||
@@ -658,9 +658,10 @@ running a task. Rust's Futures on the other hand are <em>lazily</em> evaluated.
|
||||
need to be polled once before they do any work.</p>
|
||||
</blockquote>
|
||||
<br />
|
||||
<div style="text-align: center; padding-top: 2em;">
|
||||
<div style="text-align: center; padding-top: 2em;">
|
||||
<a href="/books-futures-explained/1_futures_in_rust.html" style="background: red; color: white; padding:2em 2em 2em 2em; font-size: 1.2em;"><strong>PANIC BUTTON (next chapter)</strong></a>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
|
||||
@@ -222,7 +222,7 @@ seem a bit strange to you.</p>
|
||||
<p>Rust is different from these languages in the sense that Rust doesn't come with
|
||||
a runtime for handling concurrency, so you need to use a library which provide
|
||||
this for you.</p>
|
||||
<p>Quite a bit of complexity attributed to <code>Futures</code> is actually complexity rooted
|
||||
<p>Quite a bit of complexity attributed to Futures is actually complexity rooted
|
||||
in runtimes. Creating an efficient runtime is hard.</p>
|
||||
<p>Learning how to use one correctly requires quite a bit of effort as well, but
|
||||
you'll see that there are several similarities between these kind of runtimes so
|
||||
@@ -240,7 +240,7 @@ notifying a <code>Future</code> that it can do more work, and actually doing the
|
||||
on the <code>Future</code>.</p>
|
||||
<p>You can think of the former as the reactor's job, and the latter as the
|
||||
executors job. These two parts of a runtime interact with each other using the <code>Waker</code> type.</p>
|
||||
<p>The two most popular runtimes for <code>Futures</code> as of writing this is:</p>
|
||||
<p>The two most popular runtimes for Futures as of writing this is:</p>
|
||||
<ul>
|
||||
<li><a href="https://github.com/async-rs/async-std">async-std</a></li>
|
||||
<li><a href="https://github.com/tokio-rs/tokio">Tokio</a></li>
|
||||
@@ -260,17 +260,17 @@ of non-blocking I/O, how these tasks are created or how they're run.</p>
|
||||
take a look at this async block using pseudo-rust as example:</p>
|
||||
<pre><code class="language-rust ignore">let non_leaf = async {
|
||||
let mut stream = TcpStream::connect("127.0.0.1:3000").await.unwrap(); // <-- yield
|
||||
|
||||
|
||||
// request a large dataset
|
||||
let result = stream.write(get_dataset_request).await.unwrap(); // <-- yield
|
||||
|
||||
|
||||
// wait for the dataset
|
||||
let mut response = vec![];
|
||||
stream.read(&mut response).await.unwrap(); // <-- yield
|
||||
|
||||
// do some CPU-intensive analysis on the dataset
|
||||
let report = analyzer::analyze_data(response).unwrap();
|
||||
|
||||
|
||||
// send the results back
|
||||
stream.write(report).await.unwrap(); // <-- yield
|
||||
};
|
||||
@@ -308,13 +308,13 @@ to the thread-pool most runtimes provide.</p>
|
||||
can either perform CPU-intensive tasks or "blocking" tasks which is not supported
|
||||
by the runtime.</p>
|
||||
<p>Now, armed with this knowledge you are already on a good way for understanding
|
||||
Futures, but we're not gonna stop yet, there is lots of details to cover. </p>
|
||||
Futures, but we're not gonna stop yet, there is lots of details to cover.</p>
|
||||
<p>Take a break or a cup of coffe and get ready as we go for a deep dive in the next chapters.</p>
|
||||
<h2><a class="header" href="#bonus-section" id="bonus-section">Bonus section</a></h2>
|
||||
<p>If you find the concepts of concurrency and async programming confusing in
|
||||
general, I know where you're coming from and I have written some resources to
|
||||
try to give a high level overview that will make it easier to learn Rusts
|
||||
<code>Futures</code> afterwards:</p>
|
||||
general, I know where you're coming from and I have written some resources to
|
||||
try to give a high level overview that will make it easier to learn Rusts
|
||||
Futures afterwards:</p>
|
||||
<ul>
|
||||
<li><a href="https://cfsamson.github.io/book-exploring-async-basics/1_concurrent_vs_parallel.html">Async Basics - The difference between concurrency and parallelism</a></li>
|
||||
<li><a href="https://cfsamson.github.io/book-exploring-async-basics/2_async_history.html">Async Basics - Async history</a></li>
|
||||
@@ -322,7 +322,7 @@ try to give a high level overview that will make it easier to learn Rusts
|
||||
<li><a href="https://cfsamson.github.io/book-exploring-async-basics/6_epoll_kqueue_iocp.html">Async Basics - Epoll, Kqueue and IOCP</a></li>
|
||||
</ul>
|
||||
<p>Learning these concepts by studying futures is making it much harder than
|
||||
it needs to be, so go on and read these chapters if you feel a bit unsure. </p>
|
||||
it needs to be, so go on and read these chapters if you feel a bit unsure.</p>
|
||||
<p>I'll be right here when you're back.</p>
|
||||
<p>However, if you feel that you have the basics covered, then let's get moving!</p>
|
||||
|
||||
|
||||
@@ -177,19 +177,19 @@ recommend <a href="https://boats.gitlab.io/blog/post/wakers-i/">Withoutboats art
|
||||
flexibility for future evolutions of the API in Rust. The context can for example hold
|
||||
task-local storage and provide space for debugging hooks in later iterations.</p>
|
||||
<h2><a class="header" href="#understanding-the-waker" id="understanding-the-waker">Understanding the <code>Waker</code></a></h2>
|
||||
<p>One of the most confusing things we encounter when implementing our own <code>Futures</code>
|
||||
<p>One of the most confusing things we encounter when implementing our own <code>Future</code>s
|
||||
is how we implement a <code>Waker</code> . Creating a <code>Waker</code> involves creating a <code>vtable</code>
|
||||
which allows us to use dynamic dispatch to call methods on a <em>type erased</em> trait
|
||||
object we construct our selves.</p>
|
||||
<blockquote>
|
||||
<p>If you want to know more about dynamic dispatch in Rust I can recommend an
|
||||
<p>If you want to know more about dynamic dispatch in Rust I can recommend an
|
||||
article written by Adam Schwalm called <a href="https://alschwalm.com/blog/static/2017/03/07/exploring-dynamic-dispatch-in-rust/">Exploring Dynamic Dispatch in Rust</a>.</p>
|
||||
</blockquote>
|
||||
<p>Let's explain this a bit more in detail.</p>
|
||||
<h2><a class="header" href="#fat-pointers-in-rust" id="fat-pointers-in-rust">Fat pointers in Rust</a></h2>
|
||||
<p>To get a better understanding of how we implement the <code>Waker</code> in Rust, we need
|
||||
to take a step back and talk about some fundamentals. Let's start by taking a
|
||||
look at the size of some different pointer types in Rust. </p>
|
||||
look at the size of some different pointer types in Rust.</p>
|
||||
<p>Run the following code <em>(You'll have to press "play" to see the output)</em>:</p>
|
||||
<pre><pre class="playpen"><code class="language-rust"># use std::mem::size_of;
|
||||
trait SomeTrait { }
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
<li>See first hand why we need <code>Pin</code></li>
|
||||
<li>Understand what makes Rusts async model very memory efficient</li>
|
||||
</ul>
|
||||
<p>The motivation for <code>Generators</code> can be found in <a href="https://github.com/rust-lang/rfcs/blob/master/text/2033-experimental-coroutines.md">RFC#2033</a>. It's very
|
||||
<p>The motivation for <code>Generator</code>s can be found in <a href="https://github.com/rust-lang/rfcs/blob/master/text/2033-experimental-coroutines.md">RFC#2033</a>. It's very
|
||||
well written and I can recommend reading through it (it talks as much about
|
||||
async/await as it does about generators).</p>
|
||||
</blockquote>
|
||||
@@ -182,7 +182,7 @@ handle concurrency:</p>
|
||||
so we won't repeat that here. We'll concentrate on the variants of stackless
|
||||
coroutines which Rust uses today.</p>
|
||||
<h3><a class="header" href="#combinators" id="combinators">Combinators</a></h3>
|
||||
<p><code>Futures 0.1</code> used combinators. If you've worked with <code>Promises</code> in JavaScript,
|
||||
<p><code>Futures 0.1</code> used combinators. If you've worked with Promises in JavaScript,
|
||||
you already know combinators. In Rust they look like this:</p>
|
||||
<pre><code class="language-rust noplaypen ignore">let future = Connection::connect(conn_str).and_then(|conn| {
|
||||
conn.query("somerequest").map(|row|{
|
||||
@@ -227,7 +227,7 @@ async/await as keywords (it can even be done using a macro).</li>
|
||||
</code></pre>
|
||||
<p>Async in Rust is implemented using Generators. So to understand how async really
|
||||
works we need to understand generators first. Generators in Rust are implemented
|
||||
as state machines. </p>
|
||||
as state machines.</p>
|
||||
<p>The memory footprint of a chain of computations is defined by <em>the largest footprint
|
||||
that a single step requires</em>.</p>
|
||||
<p>That means that adding steps to a chain of computations might not require any
|
||||
@@ -330,7 +330,7 @@ impl Generator for GeneratorA {
|
||||
</blockquote>
|
||||
<p>Now that you know that the <code>yield</code> keyword in reality rewrites your code to become a state machine,
|
||||
you'll also know the basics of how <code>await</code> works. It's very similar.</p>
|
||||
<p>Now, there are some limitations in our naive state machine above. What happens when you have a
|
||||
<p>Now, there are some limitations in our naive state machine above. What happens when you have a
|
||||
<code>borrow</code> across a <code>yield</code> point?</p>
|
||||
<p>We could forbid that, but <strong>one of the major design goals for the async/await syntax has been
|
||||
to allow this</strong>. These kinds of borrows were not possible using <code>Futures 0.1</code> so we can't let this
|
||||
@@ -363,10 +363,10 @@ Just keep this in the back of your head as we move forward.</p>
|
||||
# #![allow(unused_variables)]
|
||||
#fn main() {
|
||||
# enum GeneratorState<Y, R> {
|
||||
# Yielded(Y),
|
||||
# Yielded(Y),
|
||||
# Complete(R),
|
||||
# }
|
||||
#
|
||||
#
|
||||
# trait Generator {
|
||||
# type Yield;
|
||||
# type Return;
|
||||
@@ -426,8 +426,8 @@ into itself.</p>
|
||||
# #![allow(unused_variables)]
|
||||
#fn main() {
|
||||
enum GeneratorState<Y, R> {
|
||||
Yielded(Y),
|
||||
Complete(R),
|
||||
Yielded(Y),
|
||||
Complete(R),
|
||||
}
|
||||
|
||||
trait Generator {
|
||||
@@ -460,12 +460,12 @@ impl Generator for GeneratorA {
|
||||
let borrowed = &to_borrow;
|
||||
let res = borrowed.len();
|
||||
*self = GeneratorA::Yield1 {to_borrow, borrowed: std::ptr::null()};
|
||||
|
||||
|
||||
// NB! And we set the pointer to reference the to_borrow string here
|
||||
if let GeneratorA::Yield1 {to_borrow, borrowed} = self {
|
||||
*borrowed = to_borrow;
|
||||
}
|
||||
|
||||
|
||||
GeneratorState::Yielded(res)
|
||||
}
|
||||
|
||||
@@ -507,16 +507,16 @@ does what we'd expect. But there is still one huge problem with this:</p>
|
||||
};
|
||||
}
|
||||
# enum GeneratorState<Y, R> {
|
||||
# Yielded(Y),
|
||||
# Complete(R),
|
||||
# Yielded(Y),
|
||||
# Complete(R),
|
||||
# }
|
||||
#
|
||||
#
|
||||
# trait Generator {
|
||||
# type Yield;
|
||||
# type Return;
|
||||
# fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return>;
|
||||
# }
|
||||
#
|
||||
#
|
||||
# enum GeneratorA {
|
||||
# Enter,
|
||||
# Yield1 {
|
||||
@@ -525,7 +525,7 @@ does what we'd expect. But there is still one huge problem with this:</p>
|
||||
# },
|
||||
# Exit,
|
||||
# }
|
||||
#
|
||||
#
|
||||
# impl GeneratorA {
|
||||
# fn start() -> Self {
|
||||
# GeneratorA::Enter
|
||||
@@ -541,15 +541,15 @@ does what we'd expect. But there is still one huge problem with this:</p>
|
||||
# let borrowed = &to_borrow;
|
||||
# let res = borrowed.len();
|
||||
# *self = GeneratorA::Yield1 {to_borrow, borrowed: std::ptr::null()};
|
||||
#
|
||||
#
|
||||
# // We set the self-reference here
|
||||
# if let GeneratorA::Yield1 {to_borrow, borrowed} = self {
|
||||
# *borrowed = to_borrow;
|
||||
# }
|
||||
#
|
||||
#
|
||||
# GeneratorState::Yielded(res)
|
||||
# }
|
||||
#
|
||||
#
|
||||
# GeneratorA::Yield1 {borrowed, ..} => {
|
||||
# let borrowed: &String = unsafe {&**borrowed};
|
||||
# println!("{} world", borrowed);
|
||||
@@ -585,16 +585,16 @@ pub fn main() {
|
||||
};
|
||||
}
|
||||
# enum GeneratorState<Y, R> {
|
||||
# Yielded(Y),
|
||||
# Complete(R),
|
||||
# Yielded(Y),
|
||||
# Complete(R),
|
||||
# }
|
||||
#
|
||||
#
|
||||
# trait Generator {
|
||||
# type Yield;
|
||||
# type Return;
|
||||
# fn resume(&mut self) -> GeneratorState<Self::Yield, Self::Return>;
|
||||
# }
|
||||
#
|
||||
#
|
||||
# enum GeneratorA {
|
||||
# Enter,
|
||||
# Yield1 {
|
||||
@@ -603,7 +603,7 @@ pub fn main() {
|
||||
# },
|
||||
# Exit,
|
||||
# }
|
||||
#
|
||||
#
|
||||
# impl GeneratorA {
|
||||
# fn start() -> Self {
|
||||
# GeneratorA::Enter
|
||||
@@ -619,15 +619,15 @@ pub fn main() {
|
||||
# let borrowed = &to_borrow;
|
||||
# let res = borrowed.len();
|
||||
# *self = GeneratorA::Yield1 {to_borrow, borrowed: std::ptr::null()};
|
||||
#
|
||||
#
|
||||
# // We set the self-reference here
|
||||
# if let GeneratorA::Yield1 {to_borrow, borrowed} = self {
|
||||
# *borrowed = to_borrow;
|
||||
# }
|
||||
#
|
||||
#
|
||||
# GeneratorState::Yielded(res)
|
||||
# }
|
||||
#
|
||||
#
|
||||
# GeneratorA::Yield1 {borrowed, ..} => {
|
||||
# let borrowed: &String = unsafe {&**borrowed};
|
||||
# println!("{} world", borrowed);
|
||||
@@ -646,7 +646,7 @@ while using just safe Rust. This is a big problem!</p>
|
||||
<blockquote>
|
||||
<p>I've actually forced the code above to use the nightly version of the compiler.
|
||||
If you run <a href="https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5cbe9897c0e23a502afd2740c7e78b98">the example above on the playground</a>,
|
||||
you'll see that it runs without panicing on the current stable (1.42.0) but
|
||||
you'll see that it runs without panicking on the current stable (1.42.0) but
|
||||
panics on the current nightly (1.44.0). Scary!</p>
|
||||
</blockquote>
|
||||
<p>We'll explain exactly what happened here using a slightly simpler example in the next
|
||||
@@ -707,7 +707,7 @@ pub fn main() {
|
||||
yield borrowed.len();
|
||||
println!("{} world!", borrowed);
|
||||
};
|
||||
|
||||
|
||||
let gen2 = static || {
|
||||
let to_borrow = String::from("Hello");
|
||||
let borrowed = &to_borrow;
|
||||
@@ -721,7 +721,7 @@ pub fn main() {
|
||||
if let GeneratorState::Yielded(n) = pinned1.as_mut().resume(()) {
|
||||
println!("Gen1 got value {}", n);
|
||||
}
|
||||
|
||||
|
||||
if let GeneratorState::Yielded(n) = pinned2.as_mut().resume(()) {
|
||||
println!("Gen2 got value {}", n);
|
||||
};
|
||||
|
||||
@@ -177,7 +177,7 @@ that it's time to lay down the work and start over tomorrow with a fresh mind.</
|
||||
</blockquote>
|
||||
<p>On a more serious note, I feel obliged to mention that there are valid reasons
|
||||
for the names that were chosen. Naming is not easy, and I considered renaming
|
||||
<code>Unpin</code> and <code>!Unpin</code> in this book to make them easier to reason about. </p>
|
||||
<code>Unpin</code> and <code>!Unpin</code> in this book to make them easier to reason about.</p>
|
||||
<p>However, an experienced member of the Rust community convinced me that that there
|
||||
is just too many nuances and edge-cases to consider which is easily overlooked when
|
||||
naively giving these markers different names, and I'm convinced that we'll
|
||||
@@ -211,11 +211,11 @@ impl Test {
|
||||
let self_ref: *const String = &self.a;
|
||||
self.b = self_ref;
|
||||
}
|
||||
|
||||
|
||||
fn a(&self) -> &str {
|
||||
&self.a
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn b(&self) -> &String {
|
||||
unsafe {&*(self.b)}
|
||||
}
|
||||
@@ -246,7 +246,7 @@ you see, this works as expected:</p>
|
||||
# a: String,
|
||||
# b: *const String,
|
||||
# }
|
||||
#
|
||||
#
|
||||
# impl Test {
|
||||
# fn new(txt: &str) -> Self {
|
||||
# let a = String::from(txt);
|
||||
@@ -255,17 +255,17 @@ you see, this works as expected:</p>
|
||||
# b: std::ptr::null(),
|
||||
# }
|
||||
# }
|
||||
#
|
||||
#
|
||||
# // We need an `init` method to actually set our self-reference
|
||||
# fn init(&mut self) {
|
||||
# let self_ref: *const String = &self.a;
|
||||
# self.b = self_ref;
|
||||
# }
|
||||
#
|
||||
#
|
||||
# fn a(&self) -> &str {
|
||||
# &self.a
|
||||
# }
|
||||
#
|
||||
# }
|
||||
#
|
||||
# fn b(&self) -> &String {
|
||||
# unsafe {&*(self.b)}
|
||||
# }
|
||||
@@ -296,7 +296,7 @@ which <code>test1</code> is pointing to with the data stored at the memory locat
|
||||
# a: String,
|
||||
# b: *const String,
|
||||
# }
|
||||
#
|
||||
#
|
||||
# impl Test {
|
||||
# fn new(txt: &str) -> Self {
|
||||
# let a = String::from(txt);
|
||||
@@ -305,16 +305,16 @@ which <code>test1</code> is pointing to with the data stored at the memory locat
|
||||
# b: std::ptr::null(),
|
||||
# }
|
||||
# }
|
||||
#
|
||||
#
|
||||
# fn init(&mut self) {
|
||||
# let self_ref: *const String = &self.a;
|
||||
# self.b = self_ref;
|
||||
# }
|
||||
#
|
||||
#
|
||||
# fn a(&self) -> &str {
|
||||
# &self.a
|
||||
# }
|
||||
#
|
||||
# }
|
||||
#
|
||||
# fn b(&self) -> &String {
|
||||
# unsafe {&*(self.b)}
|
||||
# }
|
||||
@@ -352,7 +352,7 @@ be tied to the lifetime of <code>test2</code> anymore.</p>
|
||||
# a: String,
|
||||
# b: *const String,
|
||||
# }
|
||||
#
|
||||
#
|
||||
# impl Test {
|
||||
# fn new(txt: &str) -> Self {
|
||||
# let a = String::from(txt);
|
||||
@@ -361,16 +361,16 @@ be tied to the lifetime of <code>test2</code> anymore.</p>
|
||||
# b: std::ptr::null(),
|
||||
# }
|
||||
# }
|
||||
#
|
||||
#
|
||||
# fn init(&mut self) {
|
||||
# let self_ref: *const String = &self.a;
|
||||
# self.b = self_ref;
|
||||
# }
|
||||
#
|
||||
#
|
||||
# fn a(&self) -> &str {
|
||||
# &self.a
|
||||
# }
|
||||
#
|
||||
# }
|
||||
#
|
||||
# fn b(&self) -> &String {
|
||||
# unsafe {&*(self.b)}
|
||||
# }
|
||||
@@ -381,7 +381,7 @@ it's easy to create serious bugs using this code.</p>
|
||||
<p>I created a diagram to help visualize what's going on:</p>
|
||||
<p><strong>Fig 1: Before and after swap</strong>
|
||||
<img src="./assets/swap_problem.jpg" alt="swap_problem" /></p>
|
||||
<p>As you can see this results in unwanted behavior. It's easy to get this to
|
||||
<p>As you can see this results in unwanted behavior. It's easy to get this to
|
||||
segfault, show UB and fail in other spectacular ways as well.</p>
|
||||
<h2><a class="header" href="#pinning-to-the-stack" id="pinning-to-the-stack">Pinning to the stack</a></h2>
|
||||
<p>Now, we can solve this problem by using <code>Pin</code> instead. Let's take a look at what
|
||||
@@ -434,7 +434,7 @@ we'll show in a second.</p>
|
||||
// Notice how we shadow `test1` to prevent it from beeing accessed again
|
||||
let mut test1 = unsafe { Pin::new_unchecked(&mut test1) };
|
||||
Test::init(test1.as_mut());
|
||||
|
||||
|
||||
let mut test2 = Test::new("test2");
|
||||
let mut test2 = unsafe { Pin::new_unchecked(&mut test2) };
|
||||
Test::init(test2.as_mut());
|
||||
@@ -444,15 +444,15 @@ we'll show in a second.</p>
|
||||
}
|
||||
# use std::pin::Pin;
|
||||
# use std::marker::PhantomPinned;
|
||||
#
|
||||
#
|
||||
# #[derive(Debug)]
|
||||
# struct Test {
|
||||
# a: String,
|
||||
# b: *const String,
|
||||
# _marker: PhantomPinned,
|
||||
# }
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
# impl Test {
|
||||
# fn new(txt: &str) -> Self {
|
||||
# let a = String::from(txt);
|
||||
@@ -468,11 +468,11 @@ we'll show in a second.</p>
|
||||
# let this = unsafe { self.get_unchecked_mut() };
|
||||
# this.b = self_ptr;
|
||||
# }
|
||||
#
|
||||
#
|
||||
# fn a<'a>(self: Pin<&'a Self>) -> &'a str {
|
||||
# &self.get_ref().a
|
||||
# }
|
||||
#
|
||||
#
|
||||
# fn b<'a>(self: Pin<&'a Self>) -> &'a String {
|
||||
# unsafe { &*(self.b) }
|
||||
# }
|
||||
@@ -484,7 +484,7 @@ you'll get a compilation error.</p>
|
||||
let mut test1 = Test::new("test1");
|
||||
let mut test1 = unsafe { Pin::new_unchecked(&mut test1) };
|
||||
Test::init(test1.as_mut());
|
||||
|
||||
|
||||
let mut test2 = Test::new("test2");
|
||||
let mut test2 = unsafe { Pin::new_unchecked(&mut test2) };
|
||||
Test::init(test2.as_mut());
|
||||
@@ -495,15 +495,15 @@ you'll get a compilation error.</p>
|
||||
}
|
||||
# use std::pin::Pin;
|
||||
# use std::marker::PhantomPinned;
|
||||
#
|
||||
#
|
||||
# #[derive(Debug)]
|
||||
# struct Test {
|
||||
# a: String,
|
||||
# b: *const String,
|
||||
# _marker: PhantomPinned,
|
||||
# }
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
# impl Test {
|
||||
# fn new(txt: &str) -> Self {
|
||||
# let a = String::from(txt);
|
||||
@@ -519,11 +519,11 @@ you'll get a compilation error.</p>
|
||||
# let this = unsafe { self.get_unchecked_mut() };
|
||||
# this.b = self_ptr;
|
||||
# }
|
||||
#
|
||||
#
|
||||
# fn a<'a>(self: Pin<&'a Self>) -> &'a str {
|
||||
# &self.get_ref().a
|
||||
# }
|
||||
#
|
||||
#
|
||||
# fn b<'a>(self: Pin<&'a Self>) -> &'a String {
|
||||
# unsafe { &*(self.b) }
|
||||
# }
|
||||
@@ -536,7 +536,7 @@ us from swapping the pinned pointers.</p>
|
||||
stack frame we're in, so we can't create a self referential object in one
|
||||
stack frame and return it since any pointers we take to "self" is invalidated.</p>
|
||||
<p>It also puts a lot of responsibility in your hands if you pin a value to the
|
||||
stack. A mistake that is easy to make is, forgetting to shadow the original variable
|
||||
stack. A mistake that is easy to make is, forgetting to shadow the original variable
|
||||
since you could drop the pinned pointer and access the old value
|
||||
after it's initialized like this:</p>
|
||||
<pre><pre class="playpen"><code class="language-rust">fn main() {
|
||||
@@ -544,7 +544,7 @@ after it's initialized like this:</p>
|
||||
let mut test1_pin = unsafe { Pin::new_unchecked(&mut test1) };
|
||||
Test::init(test1_pin.as_mut());
|
||||
drop(test1_pin);
|
||||
|
||||
|
||||
let mut test2 = Test::new("test2");
|
||||
mem::swap(&mut test1, &mut test2);
|
||||
println!("Not self referential anymore: {:?}", test1.b);
|
||||
@@ -552,15 +552,15 @@ after it's initialized like this:</p>
|
||||
# use std::pin::Pin;
|
||||
# use std::marker::PhantomPinned;
|
||||
# use std::mem;
|
||||
#
|
||||
#
|
||||
# #[derive(Debug)]
|
||||
# struct Test {
|
||||
# a: String,
|
||||
# b: *const String,
|
||||
# _marker: PhantomPinned,
|
||||
# }
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
# impl Test {
|
||||
# fn new(txt: &str) -> Self {
|
||||
# let a = String::from(txt);
|
||||
@@ -576,11 +576,11 @@ after it's initialized like this:</p>
|
||||
# let this = unsafe { self.get_unchecked_mut() };
|
||||
# this.b = self_ptr;
|
||||
# }
|
||||
#
|
||||
#
|
||||
# fn a<'a>(self: Pin<&'a Self>) -> &'a str {
|
||||
# &self.get_ref().a
|
||||
# }
|
||||
#
|
||||
#
|
||||
# fn b<'a>(self: Pin<&'a Self>) -> &'a String {
|
||||
# unsafe { &*(self.b) }
|
||||
# }
|
||||
@@ -659,7 +659,7 @@ certain operations on this value.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Most standard library types implement <code>Unpin</code>. The same goes for most
|
||||
"normal" types you encounter in Rust. <code>Futures</code> and <code>Generators</code> are two
|
||||
"normal" types you encounter in Rust. <code>Future</code>s and <code>Generator</code>s are two
|
||||
exceptions.</p>
|
||||
</li>
|
||||
<li>
|
||||
@@ -688,8 +688,8 @@ by adding <code>std::marker::PhantomPinned</code> to your type on stable.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<blockquote>
|
||||
<p>Unsafe code does not mean it's literally "unsafe", it only relieves the
|
||||
guarantees you normally get from the compiler. An <code>unsafe</code> implementation can
|
||||
<p>Unsafe code does not mean it's literally "unsafe", it only relieves the
|
||||
guarantees you normally get from the compiler. An <code>unsafe</code> implementation can
|
||||
be perfectly safe to do, but you have no safety net.</p>
|
||||
</blockquote>
|
||||
<h3><a class="header" href="#projectionstructural-pinning" id="projectionstructural-pinning">Projection/structural pinning</a></h3>
|
||||
@@ -702,7 +702,7 @@ for that.</p>
|
||||
In the <code>Drop</code> implementation you take a mutable reference to <code>self</code>, which means
|
||||
extra care must be taken when implementing <code>Drop</code> for pinned types.</p>
|
||||
<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,
|
||||
<p>This is exactly what we'll do when we implement our own <code>Future</code>, so stay tuned,
|
||||
we're soon finished.</p>
|
||||
<h2><a class="header" href="#bonus-section-fixing-our-self-referential-generator-and-learning-more-about-pin" id="bonus-section-fixing-our-self-referential-generator-and-learning-more-about-pin">Bonus section: Fixing our self-referential generator and learning more about Pin</a></h2>
|
||||
<p>But now, let's prevent this problem using <code>Pin</code>. I've commented along the way to
|
||||
@@ -726,7 +726,7 @@ pub fn main() {
|
||||
let mut pinned1 = Box::pin(gen1);
|
||||
let mut pinned2 = Box::pin(gen2);
|
||||
|
||||
// Uncomment these if you think it's safe to pin the values to the stack instead
|
||||
// Uncomment these if you think it's safe to pin the values to the stack instead
|
||||
// (it is in this case). Remember to comment out the two previous lines first.
|
||||
//let mut pinned1 = unsafe { Pin::new_unchecked(&mut gen1) };
|
||||
//let mut pinned2 = unsafe { Pin::new_unchecked(&mut gen2) };
|
||||
@@ -734,7 +734,7 @@ pub fn main() {
|
||||
if let GeneratorState::Yielded(n) = pinned1.as_mut().resume() {
|
||||
println!("Gen1 got value {}", n);
|
||||
}
|
||||
|
||||
|
||||
if let GeneratorState::Yielded(n) = pinned2.as_mut().resume() {
|
||||
println!("Gen2 got value {}", n);
|
||||
};
|
||||
@@ -749,8 +749,8 @@ pub fn main() {
|
||||
}
|
||||
|
||||
enum GeneratorState<Y, R> {
|
||||
Yielded(Y),
|
||||
Complete(R),
|
||||
Yielded(Y),
|
||||
Complete(R),
|
||||
}
|
||||
|
||||
trait Generator {
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1><a class="header" href="#implementing-futures---main-example" id="implementing-futures---main-example">Implementing Futures - main example</a></h1>
|
||||
<p>We'll create our own <code>Futures</code> together with a fake reactor and a simple
|
||||
<p>We'll create our own Futures together with a fake reactor and a simple
|
||||
executor which allows you to edit, run an play around with the code right here
|
||||
in your browser.</p>
|
||||
<p>I'll walk you through the example, but if you want to check it out closer, you
|
||||
@@ -189,8 +189,8 @@ a <code>Future</code> has resolved and should be polled again.</p>
|
||||
fn block_on<F: Future>(mut future: F) -> F::Output {
|
||||
|
||||
// the first thing we do is to construct a `Waker` which we'll pass on to
|
||||
// the `reactor` so it can wake us up when an event is ready.
|
||||
let mywaker = Arc::new(MyWaker{ thread: thread::current() });
|
||||
// the `reactor` so it can wake us up when an event is ready.
|
||||
let mywaker = Arc::new(MyWaker{ thread: thread::current() });
|
||||
let waker = waker_into_waker(Arc::into_raw(mywaker));
|
||||
|
||||
// The context struct is just a wrapper for a `Waker` object. Maybe in the
|
||||
@@ -208,7 +208,7 @@ fn block_on<F: Future>(mut future: F) -> F::Output {
|
||||
// an event occurs, or a thread has a "spurious wakeup" (an unexpected wakeup
|
||||
// that can happen for no good reason).
|
||||
let val = loop {
|
||||
|
||||
|
||||
match Future::poll(pinned, &mut cx) {
|
||||
|
||||
// when the Future is ready we're finished
|
||||
@@ -224,23 +224,23 @@ fn block_on<F: Future>(mut future: F) -> F::Output {
|
||||
<p>In all the examples you'll see in this chapter I've chosen to comment the code
|
||||
extensively. I find it easier to follow along that way so I'll not repeat myself
|
||||
here and focus only on some important aspects that might need further explanation.</p>
|
||||
<p>Now that you've read so much about <code>Generators</code> and <code>Pin</code> already this should
|
||||
<p>Now that you've read so much about <code>Generator</code>s and <code>Pin</code> already this should
|
||||
be rather easy to understand. <code>Future</code> is a state machine, every <code>await</code> point
|
||||
is a <code>yield</code> point. We could borrow data across <code>await</code> points and we meet the
|
||||
exact same challenges as we do when borrowing across <code>yield</code> points.</p>
|
||||
<blockquote>
|
||||
<p><code>Context</code> is just a wrapper around the <code>Waker</code>. At the time of writing this
|
||||
book it's nothing more. In the future it might be possible that the <code>Context</code>
|
||||
object will do more than just wrapping a <code>Future</code> so having this extra
|
||||
object will do more than just wrapping a <code>Future</code> so having this extra
|
||||
abstraction gives some flexibility.</p>
|
||||
</blockquote>
|
||||
<p>As explained in the <a href="./3_generators_pin.html">chapter about generators</a>, we use
|
||||
<code>Pin</code> and the guarantees that give us to allow <code>Futures</code> to have self
|
||||
<code>Pin</code> and the guarantees that give us to allow <code>Future</code>s to have self
|
||||
references.</p>
|
||||
<h2><a class="header" href="#the-future-implementation" id="the-future-implementation">The <code>Future</code> implementation</a></h2>
|
||||
<p>Futures has a well defined interface, which means they can be used across the
|
||||
entire ecosystem. </p>
|
||||
<p>We can chain these <code>Futures</code> so that once a <strong>leaf-future</strong> is
|
||||
entire ecosystem.</p>
|
||||
<p>We can chain these <code>Future</code>s so that once a <strong>leaf-future</strong> is
|
||||
ready we'll perform a set of operations until either the task is finished or we
|
||||
reach yet another <strong>leaf-future</strong> which we'll wait for and yield control to the
|
||||
scheduler.</p>
|
||||
@@ -365,8 +365,8 @@ is pretty normal, and makes this easy and safe to work with. Cloning a <code>Wak
|
||||
is just increasing the refcount in this case.</p>
|
||||
<p>Dropping a <code>Waker</code> is as easy as decreasing the refcount. Now, in special
|
||||
cases we could choose to not use an <code>Arc</code>. So this low-level method is there
|
||||
to allow such cases. </p>
|
||||
<p>Indeed, if we only used <code>Arc</code> there is no reason for us to go through all the
|
||||
to allow such cases.</p>
|
||||
<p>Indeed, if we only used <code>Arc</code> there is no reason for us to go through all the
|
||||
trouble of creating our own <code>vtable</code> and a <code>RawWaker</code>. We could just implement
|
||||
a normal trait.</p>
|
||||
<p>Fortunately, in the future this will probably be possible in the standard
|
||||
@@ -382,7 +382,7 @@ and call park/unpark on it.</p>
|
||||
<ol>
|
||||
<li>A future could call <code>unpark</code> on the executor thread from a different thread</li>
|
||||
<li>Our <code>executor</code> thinks that data is ready and wakes up and polls the future</li>
|
||||
<li>The future is not ready yet when polled, but at that exact same time the
|
||||
<li>The future is not ready yet when polled, but at that exact same time the
|
||||
<code>Reactor</code> gets an event and calls <code>wake()</code> which also unparks our thread.</li>
|
||||
<li>This could happen before we go to sleep again since these processes
|
||||
run in parallel.</li>
|
||||
@@ -407,12 +407,12 @@ to have an example to run.</p>
|
||||
<p>Since concurrency mostly makes sense when interacting with the outside world (or
|
||||
at least some peripheral), we need something to actually abstract over this
|
||||
interaction in an asynchronous way.</p>
|
||||
<p>This is the <code>Reactors</code> job. Most often you'll see reactors in Rust use a library
|
||||
<p>This is the Reactors job. Most often you'll see reactors in Rust use a library
|
||||
called <a href="https://github.com/tokio-rs/mio">Mio</a>, which provides non blocking APIs and event notification for
|
||||
several platforms.</p>
|
||||
<p>The reactor will typically give you something like a <code>TcpStream</code> (or any other
|
||||
resource) which you'll use to create an I/O request. What you get in return is a
|
||||
<code>Future</code>. </p>
|
||||
<code>Future</code>.</p>
|
||||
<blockquote>
|
||||
<p>If our reactor did some real I/O work our <code>Task</code> in would instead be represent
|
||||
a non-blocking <code>TcpStream</code> which registers interest with the global <code>Reactor</code>.
|
||||
@@ -574,7 +574,7 @@ which you can edit and change the way you like.</p>
|
||||
# task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, mem,
|
||||
# thread::{self, JoinHandle}, time::{Duration, Instant}, collections::HashMap
|
||||
# };
|
||||
#
|
||||
#
|
||||
fn main() {
|
||||
// This is just to make it easier for us to see when our Future was resolved
|
||||
let start = Instant::now();
|
||||
@@ -636,32 +636,32 @@ fn main() {
|
||||
# };
|
||||
# val
|
||||
# }
|
||||
#
|
||||
#
|
||||
# // ====================== FUTURE IMPLEMENTATION ==============================
|
||||
# #[derive(Clone)]
|
||||
# struct MyWaker {
|
||||
# thread: thread::Thread,
|
||||
# }
|
||||
#
|
||||
#
|
||||
# #[derive(Clone)]
|
||||
# pub struct Task {
|
||||
# id: usize,
|
||||
# reactor: Arc<Mutex<Box<Reactor>>>,
|
||||
# data: u64,
|
||||
# }
|
||||
#
|
||||
#
|
||||
# fn mywaker_wake(s: &MyWaker) {
|
||||
# let waker_ptr: *const MyWaker = s;
|
||||
# let waker_arc = unsafe { Arc::from_raw(waker_ptr) };
|
||||
# waker_arc.thread.unpark();
|
||||
# }
|
||||
#
|
||||
#
|
||||
# fn mywaker_clone(s: &MyWaker) -> RawWaker {
|
||||
# let arc = unsafe { Arc::from_raw(s) };
|
||||
# std::mem::forget(arc.clone()); // increase ref count
|
||||
# RawWaker::new(Arc::into_raw(arc) as *const (), &VTABLE)
|
||||
# }
|
||||
#
|
||||
#
|
||||
# const VTABLE: RawWakerVTable = unsafe {
|
||||
# RawWakerVTable::new(
|
||||
# |s| mywaker_clone(&*(s as *const MyWaker)), // clone
|
||||
@@ -670,18 +670,18 @@ fn main() {
|
||||
# |s| drop(Arc::from_raw(s as *const MyWaker)), // decrease refcount
|
||||
# )
|
||||
# };
|
||||
#
|
||||
#
|
||||
# fn waker_into_waker(s: *const MyWaker) -> Waker {
|
||||
# let raw_waker = RawWaker::new(s as *const (), &VTABLE);
|
||||
# unsafe { Waker::from_raw(raw_waker) }
|
||||
# }
|
||||
#
|
||||
#
|
||||
# impl Task {
|
||||
# fn new(reactor: Arc<Mutex<Box<Reactor>>>, data: u64, id: usize) -> Self {
|
||||
# Task { id, reactor, data }
|
||||
# }
|
||||
# }
|
||||
#
|
||||
#
|
||||
# impl Future for Task {
|
||||
# type Output = usize;
|
||||
# fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
@@ -698,7 +698,7 @@ fn main() {
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
#
|
||||
#
|
||||
# // =============================== REACTOR ===================================
|
||||
# enum TaskState {
|
||||
# Ready,
|
||||
@@ -716,7 +716,7 @@ fn main() {
|
||||
# Close,
|
||||
# Timeout(u64, usize),
|
||||
# }
|
||||
#
|
||||
#
|
||||
# impl Reactor {
|
||||
# fn new() -> Arc<Mutex<Box<Self>>> {
|
||||
# let (tx, rx) = channel::<Event>();
|
||||
@@ -767,7 +767,7 @@ fn main() {
|
||||
# }
|
||||
# self.dispatcher.send(Event::Timeout(duration, id)).unwrap();
|
||||
# }
|
||||
#
|
||||
#
|
||||
# fn close(&mut self) {
|
||||
# self.dispatcher.send(Event::Close).unwrap();
|
||||
# }
|
||||
@@ -779,7 +779,7 @@ fn main() {
|
||||
# }).unwrap_or(false)
|
||||
# }
|
||||
# }
|
||||
#
|
||||
#
|
||||
# impl Drop for Reactor {
|
||||
# fn drop(&mut self) {
|
||||
# self.handle.take().map(|h| h.join().unwrap()).unwrap();
|
||||
@@ -797,7 +797,7 @@ two things:</p>
|
||||
<p>The <code>async</code> keyword can be used on functions as in <code>async fn(...)</code> or on a
|
||||
block as in <code>async { ... }</code>. Both will turn your function, or block, into a
|
||||
<code>Future</code>.</p>
|
||||
<p>These <code>Futures</code> are rather simple. Imagine our generator from a few chapters
|
||||
<p>These Futures are rather simple. Imagine our generator from a few chapters
|
||||
back. Every <code>await</code> point is like a <code>yield</code> point.</p>
|
||||
<p>Instead of <code>yielding</code> a value we pass in, we yield the result of calling <code>poll</code> on
|
||||
the next <code>Future</code> we're awaiting.</p>
|
||||
@@ -811,7 +811,7 @@ to <code>spawn</code> them so the executor starts running them concurrently.</p>
|
||||
<pre><code class="language-ignore">Future got 1 at time: 1.00.
|
||||
Future got 2 at time: 3.00.
|
||||
</code></pre>
|
||||
<p>If these <code>Futures</code> were executed asynchronously we would expect to see:</p>
|
||||
<p>If these Futures were executed asynchronously we would expect to see:</p>
|
||||
<pre><code class="language-ignore">Future got 1 at time: 1.00.
|
||||
Future got 2 at time: 2.00.
|
||||
</code></pre>
|
||||
@@ -828,7 +828,7 @@ the concept of Futures by now helping you along the way.</p>
|
||||
how they implement different ways of running Futures to completion.</p>
|
||||
<p><a href="./conclusion.html#building-a-better-exectuor">If I were you I would read this next, and try to implement it for our example.</a>.</p>
|
||||
<p>That's actually it for now. There as probably much more to learn, this is enough
|
||||
for today. </p>
|
||||
for today.</p>
|
||||
<p>I hope exploring Futures and async in general gets easier after this read and I
|
||||
do really hope that you do continue to explore further.</p>
|
||||
<p>Don't forget the exercises in the last chapter 😊.</p>
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1><a class="header" href="#futures-explained-in-200-lines-of-rust" id="futures-explained-in-200-lines-of-rust">Futures Explained in 200 Lines of Rust</a></h1>
|
||||
<p>This book aims to explain <code>Futures</code> in Rust using an example driven approach,
|
||||
<p>This book aims to explain Futures in Rust using an example driven approach,
|
||||
exploring why they're designed the way they are, and how they work. We'll also
|
||||
take a look at some of the alternatives we have when dealing with concurrency
|
||||
in programming.</p>
|
||||
@@ -175,7 +175,7 @@ about runtimes and how they work, especially:</p>
|
||||
<p>I've limited myself to a 200 line main example (hence the title) to limit the
|
||||
scope and introduce an example that can easily be explored further.</p>
|
||||
<p>However, there is a lot to digest and it's not what I would call easy, but we'll
|
||||
take everything step by step so get a cup of tea and relax. </p>
|
||||
take everything step by step so get a cup of tea and relax.</p>
|
||||
<p>I hope you enjoy the ride.</p>
|
||||
<blockquote>
|
||||
<p>This book is developed in the open, and contributions are welcome. You'll find
|
||||
@@ -196,8 +196,8 @@ in Rust. If you like it, you might want to check out the others as well:</p>
|
||||
</ul>
|
||||
<h2><a class="header" href="#credits-and-thanks" id="credits-and-thanks">Credits and thanks</a></h2>
|
||||
<p>I'd like to take this chance to thank the people behind <code>mio</code>, <code>tokio</code>,
|
||||
<code>async_std</code>, <code>Futures</code>, <code>libc</code>, <code>crossbeam</code> which underpins so much of the
|
||||
async ecosystem and rarely gets enough praise in my eyes.</p>
|
||||
<code>async_std</code>, <code>futures</code>, <code>libc</code>, <code>crossbeam</code> which underpins so much of the
|
||||
async ecosystem and and rarely gets enough praise in my eyes.</p>
|
||||
<p>A special thanks to <a href="https://twitter.com/jonhoo">jonhoo</a> who was kind enough to
|
||||
give me some valuable feedback on a very early draft of this book. He has not
|
||||
read the finished product, but a big thanks is definitely due.</p>
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1><a class="header" href="#futures-explained-in-200-lines-of-rust" id="futures-explained-in-200-lines-of-rust">Futures Explained in 200 Lines of Rust</a></h1>
|
||||
<p>This book aims to explain <code>Futures</code> in Rust using an example driven approach,
|
||||
<p>This book aims to explain Futures in Rust using an example driven approach,
|
||||
exploring why they're designed the way they are, and how they work. We'll also
|
||||
take a look at some of the alternatives we have when dealing with concurrency
|
||||
in programming.</p>
|
||||
@@ -175,7 +175,7 @@ about runtimes and how they work, especially:</p>
|
||||
<p>I've limited myself to a 200 line main example (hence the title) to limit the
|
||||
scope and introduce an example that can easily be explored further.</p>
|
||||
<p>However, there is a lot to digest and it's not what I would call easy, but we'll
|
||||
take everything step by step so get a cup of tea and relax. </p>
|
||||
take everything step by step so get a cup of tea and relax.</p>
|
||||
<p>I hope you enjoy the ride.</p>
|
||||
<blockquote>
|
||||
<p>This book is developed in the open, and contributions are welcome. You'll find
|
||||
@@ -196,8 +196,8 @@ in Rust. If you like it, you might want to check out the others as well:</p>
|
||||
</ul>
|
||||
<h2><a class="header" href="#credits-and-thanks" id="credits-and-thanks">Credits and thanks</a></h2>
|
||||
<p>I'd like to take this chance to thank the people behind <code>mio</code>, <code>tokio</code>,
|
||||
<code>async_std</code>, <code>Futures</code>, <code>libc</code>, <code>crossbeam</code> which underpins so much of the
|
||||
async ecosystem and rarely gets enough praise in my eyes.</p>
|
||||
<code>async_std</code>, <code>futures</code>, <code>libc</code>, <code>crossbeam</code> which underpins so much of the
|
||||
async ecosystem and and rarely gets enough praise in my eyes.</p>
|
||||
<p>A special thanks to <a href="https://twitter.com/jonhoo">jonhoo</a> who was kind enough to
|
||||
give me some valuable feedback on a very early draft of this book. He has not
|
||||
read the finished product, but a big thanks is definitely due.</p>
|
||||
|
||||
315
book/print.html
315
book/print.html
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user