Deploy cfsamson/books-futures-explained to github.com/cfsamson/books-futures-explained.git:gh-pages
This commit is contained in:
471
1_futures_in_rust.html
Normal file
471
1_futures_in_rust.html
Normal file
@@ -0,0 +1,471 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="sidebar-visible no-js light">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Futures in Rust - Futures Explained in 200 Lines of Rust</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="This book aims to explain Futures in Rust using an example driven approach.">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
|
||||
<link rel="icon" href="favicon.svg">
|
||||
<link rel="shortcut icon" href="favicon.png">
|
||||
<link rel="stylesheet" href="css/variables.css">
|
||||
<link rel="stylesheet" href="css/general.css">
|
||||
<link rel="stylesheet" href="css/chrome.css">
|
||||
<link rel="stylesheet" href="css/print.css" media="print">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="highlight.css">
|
||||
<link rel="stylesheet" href="tomorrow-night.css">
|
||||
<link rel="stylesheet" href="ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<!-- Provide site root to javascript -->
|
||||
<script>
|
||||
var path_to_root = "";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
|
||||
</script>
|
||||
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
var html = document.querySelector('html');
|
||||
html.classList.remove('no-js')
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add('js');
|
||||
</script>
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
var html = document.querySelector('html');
|
||||
var sidebar = 'hidden';
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
}
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div class="sidebar-scrollbox">
|
||||
<ol class="chapter"><li class="chapter-item expanded affix "><a href="introduction.html">Introduction</a></li><li class="chapter-item expanded "><a href="0_background_information.html"><strong aria-hidden="true">1.</strong> Background information</a></li><li class="chapter-item expanded "><a href="1_futures_in_rust.html" class="active"><strong aria-hidden="true">2.</strong> Futures in Rust</a></li><li class="chapter-item expanded "><a href="2_a_mental_model_for_futures.html"><strong aria-hidden="true">3.</strong> A mental model of how Futures work</a></li><li class="chapter-item expanded "><a href="3_waker_context.html"><strong aria-hidden="true">4.</strong> Waker and Context</a></li><li class="chapter-item expanded "><a href="4_generators_async_await.html"><strong aria-hidden="true">5.</strong> Generators and async/await</a></li><li class="chapter-item expanded "><a href="5_pin.html"><strong aria-hidden="true">6.</strong> Pin</a></li><li class="chapter-item expanded "><a href="6_future_example.html"><strong aria-hidden="true">7.</strong> Implementing Futures</a></li><li class="chapter-item expanded "><a href="7_finished_example.html"><strong aria-hidden="true">8.</strong> Finished example (editable)</a></li><li class="chapter-item expanded affix "><a href="conclusion.html">Conclusion and exercises</a></li></ol>
|
||||
</div>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky bordered">
|
||||
<div class="left-buttons">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Futures Explained in 200 Lines of Rust</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="print.html" title="Print this book" aria-label="Print this book">
|
||||
<i id="print-button" class="fa fa-print"></i>
|
||||
</a>
|
||||
<a href="https://github.com/cfsamson/books-futures-explained" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="futures-in-rust"><a class="header" href="#futures-in-rust">Futures in Rust</a></h1>
|
||||
<blockquote>
|
||||
<p><strong>Overview:</strong></p>
|
||||
<ul>
|
||||
<li>Get a high level introduction to concurrency in Rust</li>
|
||||
<li>Know what Rust provides and not when working with async code</li>
|
||||
<li>Get to know why we need a runtime-library in Rust</li>
|
||||
<li>Understand the difference between "leaf-future" and a "non-leaf-future"</li>
|
||||
<li>Get insight on how to handle CPU intensive tasks</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<h2 id="futures"><a class="header" href="#futures">Futures</a></h2>
|
||||
<p>So what is a future?</p>
|
||||
<p>A future is a representation of some operation which will complete in the
|
||||
future.</p>
|
||||
<p>Async in Rust uses a <code>Poll</code> based approach, in which an asynchronous task will
|
||||
have three phases.</p>
|
||||
<ol>
|
||||
<li><strong>The Poll phase.</strong> A Future is polled which results in the task progressing until
|
||||
a point where it can no longer make progress. We often refer to the part of the
|
||||
runtime which polls a Future as an executor.</li>
|
||||
<li><strong>The Wait phase.</strong> An event source, most often referred to as a reactor,
|
||||
registers that a Future is waiting for an event to happen and makes sure that it
|
||||
will wake the Future when that event is ready.</li>
|
||||
<li><strong>The Wake phase.</strong> The event happens and the Future is woken up. It's now up
|
||||
to the executor which polled the Future in step 1 to schedule the future to be
|
||||
polled again and make further progress until it completes or reaches a new point
|
||||
where it can't make further progress and the cycle repeats.</li>
|
||||
</ol>
|
||||
<p>Now, when we talk about futures I find it useful to make a distinction between
|
||||
<strong>non-leaf</strong> futures and <strong>leaf</strong> futures early on because in practice they're
|
||||
pretty different from one another.</p>
|
||||
<h3 id="leaf-futures"><a class="header" href="#leaf-futures">Leaf futures</a></h3>
|
||||
<p>Runtimes create <em>leaf futures</em> which represent a resource like a socket.</p>
|
||||
<pre><code class="language-rust ignore noplaypen">// stream is a **leaf-future**
|
||||
let mut stream = tokio::net::TcpStream::connect("127.0.0.1:3000");</code></pre>
|
||||
<p>Operations on these resources, like a <code>Read</code> on a socket, will be non-blocking
|
||||
and return a future which we call a leaf future since it's the future which
|
||||
we're actually waiting on.</p>
|
||||
<p>It's unlikely that you'll implement a leaf future yourself unless you're writing
|
||||
a runtime, but we'll go through how they're constructed in this book as well.</p>
|
||||
<p>It's also unlikely that you'll pass a leaf-future to a runtime and run it to
|
||||
completion alone as you'll understand by reading the next paragraph.</p>
|
||||
<h3 id="non-leaf-futures"><a class="header" href="#non-leaf-futures">Non-leaf-futures</a></h3>
|
||||
<p>Non-leaf-futures are the kind of futures we as <em>users</em> of a runtime write
|
||||
ourselves using the <code>async</code> keyword to create a <strong>task</strong> which can be run on the
|
||||
executor.</p>
|
||||
<p>The bulk of an async program will consist of non-leaf-futures, which are a kind
|
||||
of pause-able computation. This is an important distinction since these futures represents a <em>set of operations</em>. Often, such a task will <code>await</code> a leaf future
|
||||
as one of many operations to complete the task.</p>
|
||||
<pre><code class="language-rust ignore noplaypen edition2018">// Non-leaf-future
|
||||
let non_leaf = async {
|
||||
let mut stream = TcpStream::connect("127.0.0.1:3000").await.unwrap();// <- yield
|
||||
println!("connected!");
|
||||
let result = stream.write(b"hello world\n").await; // <- yield
|
||||
println!("message sent!");
|
||||
...
|
||||
};</code></pre>
|
||||
<p>The key to these tasks is that they're able to yield control to the runtime's
|
||||
scheduler and then resume execution again where it left off at a later point.</p>
|
||||
<p>In contrast to leaf futures, these kind of futures do not themselves represent
|
||||
an I/O resource. When we poll them they will run until they get to a
|
||||
leaf-future which returns <code>Pending</code> and then yield control to the scheduler
|
||||
(which is a part of what we call the runtime).</p>
|
||||
<h2 id="runtimes"><a class="header" href="#runtimes">Runtimes</a></h2>
|
||||
<p>Languages like C#, JavaScript, Java, GO, and many others comes with a runtime
|
||||
for handling concurrency. So if you come from one of those languages this will
|
||||
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 provides
|
||||
this for you.</p>
|
||||
<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
|
||||
learning one makes learning the next much easier.</p>
|
||||
<p>The difference between Rust and other languages is that you have to make an
|
||||
active choice when it comes to picking a runtime. Most often in other languages,
|
||||
you'll just use the one provided for you.</p>
|
||||
<h3 id="a-useful-mental-model-of-an-async-runtime"><a class="header" href="#a-useful-mental-model-of-an-async-runtime">A useful mental model of an async runtime</a></h3>
|
||||
<p>I find it easier to reason about how Futures work by creating a high level mental model we can use.
|
||||
To do that I have to introduce the concept of a runtime which will drive our Futures to completion.</p>
|
||||
<blockquote>
|
||||
<p>Please note that the mental model I create here is not the <strong>only</strong> way to drive Futures to
|
||||
completion and that Rust’s Futures does not impose any restrictions on how you actually accomplish
|
||||
this task.</p>
|
||||
</blockquote>
|
||||
<p><strong>A fully working async system in Rust can be divided into three parts:</strong></p>
|
||||
<ol>
|
||||
<li>Reactor</li>
|
||||
<li>Executor</li>
|
||||
<li>Future</li>
|
||||
</ol>
|
||||
<p>So, how does these three parts work together? They do that through an object called the <code>Waker</code>.
|
||||
The <code>Waker</code> is how the reactor tells the executor that a specific Future is ready to run. Once you
|
||||
understand the life cycle and ownership of a Waker, you'll understand how futures work from a user's
|
||||
perspective. Here is the life cycle:</p>
|
||||
<ul>
|
||||
<li>A Waker is created by the <strong>executor.</strong></li>
|
||||
<li>When a future is polled the first time by the executor, it’s given a clone of the Waker
|
||||
object created by the executor. Since this is a shared object (e.g. an
|
||||
<code>Arc<T></code>), all clones actually point to the same underlying object. Thus,
|
||||
anything that calls <em>any</em> clone of the original Waker will wake the particular
|
||||
Future that was registered to it.</li>
|
||||
<li>The future clones the Waker and passes it to the reactor, which stores it to
|
||||
use later.</li>
|
||||
</ul>
|
||||
<p>You could think of a "future" like a channel for the <code>Waker</code>: The channel starts with the future that's polled the first time by the executor and is passed a handle to a <code>Waker</code>. It ends in a leaf-future which passes that handle to the reactor.</p>
|
||||
<blockquote>
|
||||
<p>Note that the <code>Waker</code> is wrapped in a rather uninteresting <code>Context</code> struct which we will learn more about later. The interesting part is the <code>Waker</code> that is passed on.</p>
|
||||
</blockquote>
|
||||
<p>At some point in the future, the reactor will decide that the future is ready to run. It will wake the future via the Waker that it stored. This action will do what is necessary to get the executor in a position to poll the future. We'll go into more detail on Wakers in the <a href="3_waker_context.html#understanding-the-waker">Waker and Context chapter.</a></p>
|
||||
<p>Since the interface is the same across all executors, reactors can <em>in theory</em> be completely
|
||||
oblivious to the type of the executor, and vice-versa. <strong>Executors and reactors never need to
|
||||
communicate with one another directly.</strong></p>
|
||||
<p>This design is what gives the futures framework it's power and flexibility and allows the Rust
|
||||
standard library to provide an ergonomic, zero-cost abstraction for us to use.</p>
|
||||
<p>In an effort to try to visualize how these parts work together I put together
|
||||
a set of slides in the next chapter that I hope will help.</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>
|
||||
</ul>
|
||||
<h3 id="what-rusts-standard-library-takes-care-of"><a class="header" href="#what-rusts-standard-library-takes-care-of">What Rust's standard library takes care of</a></h3>
|
||||
<ol>
|
||||
<li>A common interface representing an operation which will be completed in the
|
||||
future through the <code>Future</code> trait.</li>
|
||||
<li>An ergonomic way of creating tasks which can be suspended and resumed through
|
||||
the <code>async</code> and <code>await</code> keywords.</li>
|
||||
<li>A defined interface to wake up a suspended task through the <code>Waker</code> type.</li>
|
||||
</ol>
|
||||
<p>That's really what Rust's standard library does. As you see there is no definition
|
||||
of non-blocking I/O, how these tasks are created, or how they're run.</p>
|
||||
<h2 id="io-vs-cpu-intensive-tasks"><a class="header" href="#io-vs-cpu-intensive-tasks">I/O vs CPU intensive tasks</a></h2>
|
||||
<p>As you know now, what you normally write are called non-leaf futures. Let's
|
||||
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
|
||||
};</code></pre>
|
||||
<p>Now, as you'll see when we go through how Futures work, the code we write between
|
||||
the yield points are run on the same thread as our executor.</p>
|
||||
<p>That means that while our <code>analyzer</code> is working on the dataset, the executor
|
||||
is busy doing calculations instead of handling new requests.</p>
|
||||
<p>Fortunately there are a few ways to handle this, and it's not difficult, but it's
|
||||
something you must be aware of:</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>We could create a new leaf future which sends our task to another thread and
|
||||
resolves when the task is finished. We could <code>await</code> this leaf-future like any
|
||||
other future.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>The runtime could have some kind of supervisor that monitors how much time
|
||||
different tasks take, and move the executor itself to a different thread so it can
|
||||
continue to run even though our <code>analyzer</code> task is blocking the original executor thread.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>You can create a reactor yourself which is compatible with the runtime which
|
||||
does the analysis any way you see fit, and returns a Future which can be awaited.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>Now, #1 is the usual way of handling this, but some executors implement #2 as well.
|
||||
The problem with #2 is that if you switch runtime you need to make sure that it
|
||||
supports this kind of supervision as well or else you will end up blocking the
|
||||
executor.</p>
|
||||
<p>And #3 is more of theoretical importance, normally you'd be happy by sending the task
|
||||
to the thread-pool most runtimes provide.</p>
|
||||
<p>Most executors have a way to accomplish #1 using methods like <code>spawn_blocking</code>.</p>
|
||||
<p>These methods send the task to a thread-pool created by the runtime where you
|
||||
can either perform CPU-intensive tasks or "blocking" tasks which are 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 are lots of details to cover.</p>
|
||||
<p>Take a break or a cup of coffee and get ready as we go for a deep dive in the next chapters.</p>
|
||||
<h2 id="want-to-learn-more-about-concurrency-and-async"><a class="header" href="#want-to-learn-more-about-concurrency-and-async">Want to learn more about concurrency and async?</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 Rust's
|
||||
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>
|
||||
<li><a href="https://cfsamson.github.io/book-exploring-async-basics/5_strategies_for_handling_io.html">Async Basics - Strategies for handling I/O</a></li>
|
||||
<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>
|
||||
<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>
|
||||
<h2 id="bonus-section---additional-notes-on-futures-and-wakers"><a class="header" href="#bonus-section---additional-notes-on-futures-and-wakers">Bonus section - additional notes on Futures and Wakers</a></h2>
|
||||
<blockquote>
|
||||
<p>In this section we take a deeper look at some advantages of having a loose
|
||||
coupling between the Executor-part and Reactor-part of an async runtime.</p>
|
||||
</blockquote>
|
||||
<p>Earlier in this chapter, I mentioned that it is common for the
|
||||
executor to create a new Waker for each Future that is registered with the
|
||||
executor, but that the Waker is a shared object similar to a <code>Arc<T></code>. One of
|
||||
the reasons for this design is that it allows different Reactors the
|
||||
ability to Wake a Future.</p>
|
||||
<p>As an example of how this can be used, consider how you could create a new type
|
||||
of Future that has the ability to be canceled:</p>
|
||||
<p>One way to achieve this would be to add an
|
||||
<a href="https://doc.rust-lang.org/std/sync/atomic/struct.AtomicBool.html"><code>AtomicBool</code></a>
|
||||
to the instance of the future, and an extra method called <code>cancel()</code>. The
|
||||
<code>cancel()</code> method will first set the
|
||||
<a href="https://doc.rust-lang.org/std/sync/atomic/struct.AtomicBool.html"><code>AtomicBool</code></a>
|
||||
to signal that the future is now canceled, and then immediately call instance's
|
||||
own copy of the Waker.</p>
|
||||
<p>Once the executor starts executing the Future, the
|
||||
<em>Future</em> will know that it was canceled, and will do the appropriate cleanup
|
||||
actions to terminate itself.</p>
|
||||
<p>The main reason for designing the Future in this manner is because we don't have
|
||||
to modify either the Executor or the other Reactors; they are all oblivious to
|
||||
the change.</p>
|
||||
<p>The only possible issue is with the design of the Future itself; a
|
||||
Future that is canceled still needs to terminate correctly according to the
|
||||
rules outlined in the docs for
|
||||
<a href="https://doc.rust-lang.org/std/future/trait.Future.html"><code>Future</code></a>. That means
|
||||
that it can't just delete it's resources and then sit there; it needs to return
|
||||
a value. It is up to you to decide if a canceled future will return
|
||||
<a href="https://doc.rust-lang.org/std/task/enum.Poll.html#variant.Pending"><code>Pending</code></a>
|
||||
forever, or if it will return a value in
|
||||
<a href="https://doc.rust-lang.org/std/task/enum.Poll.html#variant.Ready"><code>Ready</code></a>. Just
|
||||
be aware that if other Futures are <code>await</code>ing it, they won't be able to start
|
||||
until <a href="https://doc.rust-lang.org/std/task/enum.Poll.html#variant.Ready"><code>Ready</code></a>
|
||||
is returned.</p>
|
||||
<p>A common technique for cancelable Futures is to have them return a
|
||||
Result with an error that signals the Future was canceled; that will permit any
|
||||
Futures that are awaiting the canceled Future a chance to progress, with the
|
||||
knowledge that the Future they depended on was canceled. There are additional
|
||||
concerns as well, but beyond the scope of this book. Read the documentation and
|
||||
code for the <a href="https://crates.io/crates/futures"><code>futures</code></a> crate for a better
|
||||
understanding of what the concerns are.</p>
|
||||
<blockquote>
|
||||
<p><em>Thanks to <a href="https://github.com/ckaran">@ckaran</a> for contributing this bonus segment.</em></p>
|
||||
</blockquote>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<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>
|
||||
|
||||
<a rel="next" href="2_a_mental_model_for_futures.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" 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>
|
||||
|
||||
<a rel="next" href="2_a_mental_model_for_futures.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Google Analytics Tag -->
|
||||
<script>
|
||||
var localAddrs = ["localhost", "127.0.0.1", ""];
|
||||
|
||||
// make sure we don't activate google analytics if the developer is
|
||||
// inspecting the book locally...
|
||||
if (localAddrs.indexOf(document.location.hostname) === -1) {
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-157536992-1', 'auto');
|
||||
ga('send', 'pageview');
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
window.playground_line_numbers = true;
|
||||
</script>
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
<script src="ace.js"></script>
|
||||
<script src="editor.js"></script>
|
||||
<script src="mode-rust.js"></script>
|
||||
<script src="theme-dawn.js"></script>
|
||||
<script src="theme-tomorrow_night.js"></script>
|
||||
|
||||
<script src="elasticlunr.min.js"></script>
|
||||
<script src="mark.min.js"></script>
|
||||
<script src="searcher.js"></script>
|
||||
|
||||
<script src="clipboard.min.js"></script>
|
||||
<script src="highlight.js"></script>
|
||||
<script src="book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user