audit pass introduction and background_information

This commit is contained in:
cfsamson
2020-04-06 12:28:49 +02:00
parent 9b9b72afa5
commit 9c2079c839
17 changed files with 639 additions and 71 deletions

View File

@@ -153,8 +153,8 @@
<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>
<p>While we do that we'll also explain some aspects when it comes to 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
@@ -210,7 +210,7 @@ fn main() {
</code></pre></pre>
<p>OS threads sure has some pretty big advantages. So why all this talk about
&quot;async&quot; 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
<p>First of all. For computers to be <a href="https://en.wikipedia.org/wiki/Efficiency"><em>efficient</em></a> they 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>)
you'll see concurrency everywhere. It's very fundamental in everything we do.</p>
<p>Secondly, we have the web. </p>
@@ -218,10 +218,9 @@ you'll see concurrency everywhere. It's very fundamental in everything we do.</p
(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>
<p>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>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>
<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>
@@ -237,15 +236,16 @@ 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>
<p>The typical flow will be like this:</p>
<p><strong>The typical flow looks like this:</strong></p>
<ol>
<li>Run som non-blocking code</li>
<li>Run some non-blocking code</li>
<li>Make a blocking call to some external resource</li>
<li>CPU jumps to the &quot;main&quot; thread which schedules a different thread to run and
&quot;jumps&quot; to that stack</li>
<li>Run some non-blocking code on the new thread until a new blocking call or the
task is finished</li>
<li>&quot;jumps&quot; back to the &quot;main&quot; thread, schedule a new thread to run and jump to that</li>
<li>&quot;jumps&quot; back to the &quot;main&quot; thread, schedule a new thread which is ready to make
progress and jump to that.</li>
</ol>
<p>These &quot;jumps&quot; are know as <strong>context switches</strong>. Your OS is doing it many times each
second as you read this.</p>
@@ -501,16 +501,17 @@ 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>
</blockquote>
<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
instructions we want to run later together with whatever state is needed. In rust this
would be a <code>closure</code>. In the example below, we save this information in a <code>HashMap</code>
but it's not the only option.</p>
<p>The basic idea of <em>not</em> involving threads as a primary way to achieve concurrency
is the common denominator for the rest of the approaches. Including the one
Rust uses today which we'll soon get to.</p>
<p><strong>Advantages:</strong></p>
<ul>
<li>Easy to implement in most languages</li>
<li>No context switching</li>
<li>Low memory overhead (in most cases)</li>
<li>Relatively low memory overhead (in most cases)</li>
</ul>
<p><strong>Drawbacks:</strong></p>
<ul>
@@ -594,10 +595,10 @@ impl Runtime {
</code></pre></pre>
<p>We're keeping this super simple, and you might wonder what's the difference
between this approach and the one using OS threads an passing in the callbacks
to the OS threads directly. </p>
to the OS threads directly.</p>
<p>The difference is that the callbacks are run on the
same thread using this example. The OS threads we create are basically just used
as timers.</p>
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
@@ -652,7 +653,7 @@ Rusts Futures 3.0 is a lot like async/await in our last example.</p>
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
<p>To avoid confusion later on: There's 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.</p>

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

10
book/elasticlunr.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -151,8 +151,9 @@
<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,
exploring why they're designed the way they are, the alternatives and how
they work.</p>
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>
<p>Going into the level of detail I do in this book is not needed to use futures
or async/await in Rust. It's for the curious out there that want to know <em>how</em>
it all works.</p>
@@ -162,9 +163,8 @@ topic of different types of executors and runtimes. We'll just implement a very
simple runtime in this book introducing some concepts but it's enough to get
started.</p>
<p><a href="https://github.com/stjepang">Stjepan Glavina</a> has made an excellent series of
articles about async runtimes and executors, and if the rumors are right he's
even working on a new async runtime that should be easy enough to use as
learning material.</p>
articles about async runtimes and executors, and if the rumors are right there
is more to come from him in the near future.</p>
<p>The way you should go about it is to read this book first, then continue
reading the <a href="https://stjepang.github.io/">articles from stejpang</a> to learn more
about runtimes and how they work, especially:</p>
@@ -182,6 +182,7 @@ take everything step by step so get a cup of tea and relax. </p>
<a href="https://github.com/cfsamson/books-futures-explained">the repository for the book itself here</a>. The final example which
you can clone, fork or copy <a href="https://github.com/cfsamson/examples-futures">can be found here</a>. Any suggestions
or improvements can be filed as a PR or in the issue tracker for the book.</p>
<p>As always, all kinds of feedback is welcome.</p>
</blockquote>
<h2><a class="header" href="#reader-exercises-and-further-reading" id="reader-exercises-and-further-reading">Reader exercises and further reading</a></h2>
<p>In the last <a href="conclusion.html">chapter</a> I've taken the liberty to suggest some

View File

@@ -151,8 +151,9 @@
<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,
exploring why they're designed the way they are, the alternatives and how
they work.</p>
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>
<p>Going into the level of detail I do in this book is not needed to use futures
or async/await in Rust. It's for the curious out there that want to know <em>how</em>
it all works.</p>
@@ -162,9 +163,8 @@ topic of different types of executors and runtimes. We'll just implement a very
simple runtime in this book introducing some concepts but it's enough to get
started.</p>
<p><a href="https://github.com/stjepang">Stjepan Glavina</a> has made an excellent series of
articles about async runtimes and executors, and if the rumors are right he's
even working on a new async runtime that should be easy enough to use as
learning material.</p>
articles about async runtimes and executors, and if the rumors are right there
is more to come from him in the near future.</p>
<p>The way you should go about it is to read this book first, then continue
reading the <a href="https://stjepang.github.io/">articles from stejpang</a> to learn more
about runtimes and how they work, especially:</p>
@@ -182,6 +182,7 @@ take everything step by step so get a cup of tea and relax. </p>
<a href="https://github.com/cfsamson/books-futures-explained">the repository for the book itself here</a>. The final example which
you can clone, fork or copy <a href="https://github.com/cfsamson/examples-futures">can be found here</a>. Any suggestions
or improvements can be filed as a PR or in the issue tracker for the book.</p>
<p>As always, all kinds of feedback is welcome.</p>
</blockquote>
<h2><a class="header" href="#reader-exercises-and-further-reading" id="reader-exercises-and-further-reading">Reader exercises and further reading</a></h2>
<p>In the last <a href="conclusion.html">chapter</a> I've taken the liberty to suggest some

7
book/mark.min.js vendored Normal file

File diff suppressed because one or more lines are too long

7
book/mode-rust.js Normal file

File diff suppressed because one or more lines are too long

View File

@@ -153,8 +153,9 @@
<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,
exploring why they're designed the way they are, the alternatives and how
they work.</p>
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>
<p>Going into the level of detail I do in this book is not needed to use futures
or async/await in Rust. It's for the curious out there that want to know <em>how</em>
it all works.</p>
@@ -164,9 +165,8 @@ topic of different types of executors and runtimes. We'll just implement a very
simple runtime in this book introducing some concepts but it's enough to get
started.</p>
<p><a href="https://github.com/stjepang">Stjepan Glavina</a> has made an excellent series of
articles about async runtimes and executors, and if the rumors are right he's
even working on a new async runtime that should be easy enough to use as
learning material.</p>
articles about async runtimes and executors, and if the rumors are right there
is more to come from him in the near future.</p>
<p>The way you should go about it is to read this book first, then continue
reading the <a href="https://stjepang.github.io/">articles from stejpang</a> to learn more
about runtimes and how they work, especially:</p>
@@ -184,6 +184,7 @@ take everything step by step so get a cup of tea and relax. </p>
<a href="https://github.com/cfsamson/books-futures-explained">the repository for the book itself here</a>. The final example which
you can clone, fork or copy <a href="https://github.com/cfsamson/examples-futures">can be found here</a>. Any suggestions
or improvements can be filed as a PR or in the issue tracker for the book.</p>
<p>As always, all kinds of feedback is welcome.</p>
</blockquote>
<h2><a class="header" href="#reader-exercises-and-further-reading" id="reader-exercises-and-further-reading">Reader exercises and further reading</a></h2>
<p>In the last <a href="conclusion.html">chapter</a> I've taken the liberty to suggest some
@@ -206,8 +207,8 @@ finished product and has in no way endorsed it, but a thanks is definitely due.<
<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>
<p>While we do that we'll also explain some aspects when it comes to 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
@@ -263,7 +264,7 @@ fn main() {
</code></pre></pre>
<p>OS threads sure has some pretty big advantages. So why all this talk about
&quot;async&quot; 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
<p>First of all. For computers to be <a href="https://en.wikipedia.org/wiki/Efficiency"><em>efficient</em></a> they 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>)
you'll see concurrency everywhere. It's very fundamental in everything we do.</p>
<p>Secondly, we have the web. </p>
@@ -271,10 +272,9 @@ you'll see concurrency everywhere. It's very fundamental in everything we do.</p
(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>
<p>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>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>
<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>
@@ -290,15 +290,16 @@ 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>
<p>The typical flow will be like this:</p>
<p><strong>The typical flow looks like this:</strong></p>
<ol>
<li>Run som non-blocking code</li>
<li>Run some non-blocking code</li>
<li>Make a blocking call to some external resource</li>
<li>CPU jumps to the &quot;main&quot; thread which schedules a different thread to run and
&quot;jumps&quot; to that stack</li>
<li>Run some non-blocking code on the new thread until a new blocking call or the
task is finished</li>
<li>&quot;jumps&quot; back to the &quot;main&quot; thread, schedule a new thread to run and jump to that</li>
<li>&quot;jumps&quot; back to the &quot;main&quot; thread, schedule a new thread which is ready to make
progress and jump to that.</li>
</ol>
<p>These &quot;jumps&quot; are know as <strong>context switches</strong>. Your OS is doing it many times each
second as you read this.</p>
@@ -554,16 +555,17 @@ 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>
</blockquote>
<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
instructions we want to run later together with whatever state is needed. In rust this
would be a <code>closure</code>. In the example below, we save this information in a <code>HashMap</code>
but it's not the only option.</p>
<p>The basic idea of <em>not</em> involving threads as a primary way to achieve concurrency
is the common denominator for the rest of the approaches. Including the one
Rust uses today which we'll soon get to.</p>
<p><strong>Advantages:</strong></p>
<ul>
<li>Easy to implement in most languages</li>
<li>No context switching</li>
<li>Low memory overhead (in most cases)</li>
<li>Relatively low memory overhead (in most cases)</li>
</ul>
<p><strong>Drawbacks:</strong></p>
<ul>
@@ -647,10 +649,10 @@ impl Runtime {
</code></pre></pre>
<p>We're keeping this super simple, and you might wonder what's the difference
between this approach and the one using OS threads an passing in the callbacks
to the OS threads directly. </p>
to the OS threads directly.</p>
<p>The difference is that the callbacks are run on the
same thread using this example. The OS threads we create are basically just used
as timers.</p>
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
@@ -705,7 +707,7 @@ Rusts Futures 3.0 is a lot like async/await in our last example.</p>
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
<p>To avoid confusion later on: There's 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.</p>

477
book/searcher.js Normal file
View File

@@ -0,0 +1,477 @@
"use strict";
window.search = window.search || {};
(function search(search) {
// Search functionality
//
// You can use !hasFocus() to prevent keyhandling in your key
// event handlers while the user is typing their search.
if (!Mark || !elasticlunr) {
return;
}
//IE 11 Compatibility from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
if (!String.prototype.startsWith) {
String.prototype.startsWith = function(search, pos) {
return this.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search;
};
}
var search_wrap = document.getElementById('search-wrapper'),
searchbar = document.getElementById('searchbar'),
searchbar_outer = document.getElementById('searchbar-outer'),
searchresults = document.getElementById('searchresults'),
searchresults_outer = document.getElementById('searchresults-outer'),
searchresults_header = document.getElementById('searchresults-header'),
searchicon = document.getElementById('search-toggle'),
content = document.getElementById('content'),
searchindex = null,
doc_urls = [],
results_options = {
teaser_word_count: 30,
limit_results: 30,
},
search_options = {
bool: "AND",
expand: true,
fields: {
title: {boost: 1},
body: {boost: 1},
breadcrumbs: {boost: 0}
}
},
mark_exclude = [],
marker = new Mark(content),
current_searchterm = "",
URL_SEARCH_PARAM = 'search',
URL_MARK_PARAM = 'highlight',
teaser_count = 0,
SEARCH_HOTKEY_KEYCODE = 83,
ESCAPE_KEYCODE = 27,
DOWN_KEYCODE = 40,
UP_KEYCODE = 38,
SELECT_KEYCODE = 13;
function hasFocus() {
return searchbar === document.activeElement;
}
function removeChildren(elem) {
while (elem.firstChild) {
elem.removeChild(elem.firstChild);
}
}
// Helper to parse a url into its building blocks.
function parseURL(url) {
var a = document.createElement('a');
a.href = url;
return {
source: url,
protocol: a.protocol.replace(':',''),
host: a.hostname,
port: a.port,
params: (function(){
var ret = {};
var seg = a.search.replace(/^\?/,'').split('&');
var len = seg.length, i = 0, s;
for (;i<len;i++) {
if (!seg[i]) { continue; }
s = seg[i].split('=');
ret[s[0]] = s[1];
}
return ret;
})(),
file: (a.pathname.match(/\/([^/?#]+)$/i) || [,''])[1],
hash: a.hash.replace('#',''),
path: a.pathname.replace(/^([^/])/,'/$1')
};
}
// Helper to recreate a url string from its building blocks.
function renderURL(urlobject) {
var url = urlobject.protocol + "://" + urlobject.host;
if (urlobject.port != "") {
url += ":" + urlobject.port;
}
url += urlobject.path;
var joiner = "?";
for(var prop in urlobject.params) {
if(urlobject.params.hasOwnProperty(prop)) {
url += joiner + prop + "=" + urlobject.params[prop];
joiner = "&";
}
}
if (urlobject.hash != "") {
url += "#" + urlobject.hash;
}
return url;
}
// Helper to escape html special chars for displaying the teasers
var escapeHTML = (function() {
var MAP = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&#34;',
"'": '&#39;'
};
var repl = function(c) { return MAP[c]; };
return function(s) {
return s.replace(/[&<>'"]/g, repl);
};
})();
function formatSearchMetric(count, searchterm) {
if (count == 1) {
return count + " search result for '" + searchterm + "':";
} else if (count == 0) {
return "No search results for '" + searchterm + "'.";
} else {
return count + " search results for '" + searchterm + "':";
}
}
function formatSearchResult(result, searchterms) {
var teaser = makeTeaser(escapeHTML(result.doc.body), searchterms);
teaser_count++;
// The ?URL_MARK_PARAM= parameter belongs inbetween the page and the #heading-anchor
var url = doc_urls[result.ref].split("#");
if (url.length == 1) { // no anchor found
url.push("");
}
return '<a href="' + path_to_root + url[0] + '?' + URL_MARK_PARAM + '=' + searchterms + '#' + url[1]
+ '" aria-details="teaser_' + teaser_count + '">' + result.doc.breadcrumbs + '</a>'
+ '<span class="teaser" id="teaser_' + teaser_count + '" aria-label="Search Result Teaser">'
+ teaser + '</span>';
}
function makeTeaser(body, searchterms) {
// The strategy is as follows:
// First, assign a value to each word in the document:
// Words that correspond to search terms (stemmer aware): 40
// Normal words: 2
// First word in a sentence: 8
// Then use a sliding window with a constant number of words and count the
// sum of the values of the words within the window. Then use the window that got the
// maximum sum. If there are multiple maximas, then get the last one.
// Enclose the terms in <em>.
var stemmed_searchterms = searchterms.map(function(w) {
return elasticlunr.stemmer(w.toLowerCase());
});
var searchterm_weight = 40;
var weighted = []; // contains elements of ["word", weight, index_in_document]
// split in sentences, then words
var sentences = body.toLowerCase().split('. ');
var index = 0;
var value = 0;
var searchterm_found = false;
for (var sentenceindex in sentences) {
var words = sentences[sentenceindex].split(' ');
value = 8;
for (var wordindex in words) {
var word = words[wordindex];
if (word.length > 0) {
for (var searchtermindex in stemmed_searchterms) {
if (elasticlunr.stemmer(word).startsWith(stemmed_searchterms[searchtermindex])) {
value = searchterm_weight;
searchterm_found = true;
}
};
weighted.push([word, value, index]);
value = 2;
}
index += word.length;
index += 1; // ' ' or '.' if last word in sentence
};
index += 1; // because we split at a two-char boundary '. '
};
if (weighted.length == 0) {
return body;
}
var window_weight = [];
var window_size = Math.min(weighted.length, results_options.teaser_word_count);
var cur_sum = 0;
for (var wordindex = 0; wordindex < window_size; wordindex++) {
cur_sum += weighted[wordindex][1];
};
window_weight.push(cur_sum);
for (var wordindex = 0; wordindex < weighted.length - window_size; wordindex++) {
cur_sum -= weighted[wordindex][1];
cur_sum += weighted[wordindex + window_size][1];
window_weight.push(cur_sum);
};
if (searchterm_found) {
var max_sum = 0;
var max_sum_window_index = 0;
// backwards
for (var i = window_weight.length - 1; i >= 0; i--) {
if (window_weight[i] > max_sum) {
max_sum = window_weight[i];
max_sum_window_index = i;
}
};
} else {
max_sum_window_index = 0;
}
// add <em/> around searchterms
var teaser_split = [];
var index = weighted[max_sum_window_index][2];
for (var i = max_sum_window_index; i < max_sum_window_index+window_size; i++) {
var word = weighted[i];
if (index < word[2]) {
// missing text from index to start of `word`
teaser_split.push(body.substring(index, word[2]));
index = word[2];
}
if (word[1] == searchterm_weight) {
teaser_split.push("<em>")
}
index = word[2] + word[0].length;
teaser_split.push(body.substring(word[2], index));
if (word[1] == searchterm_weight) {
teaser_split.push("</em>")
}
};
return teaser_split.join('');
}
function init(config) {
results_options = config.results_options;
search_options = config.search_options;
searchbar_outer = config.searchbar_outer;
doc_urls = config.doc_urls;
searchindex = elasticlunr.Index.load(config.index);
// Set up events
searchicon.addEventListener('click', function(e) { searchIconClickHandler(); }, false);
searchbar.addEventListener('keyup', function(e) { searchbarKeyUpHandler(); }, false);
document.addEventListener('keydown', function(e) { globalKeyHandler(e); }, false);
// If the user uses the browser buttons, do the same as if a reload happened
window.onpopstate = function(e) { doSearchOrMarkFromUrl(); };
// Suppress "submit" events so the page doesn't reload when the user presses Enter
document.addEventListener('submit', function(e) { e.preventDefault(); }, false);
// If reloaded, do the search or mark again, depending on the current url parameters
doSearchOrMarkFromUrl();
}
function unfocusSearchbar() {
// hacky, but just focusing a div only works once
var tmp = document.createElement('input');
tmp.setAttribute('style', 'position: absolute; opacity: 0;');
searchicon.appendChild(tmp);
tmp.focus();
tmp.remove();
}
// On reload or browser history backwards/forwards events, parse the url and do search or mark
function doSearchOrMarkFromUrl() {
// Check current URL for search request
var url = parseURL(window.location.href);
if (url.params.hasOwnProperty(URL_SEARCH_PARAM)
&& url.params[URL_SEARCH_PARAM] != "") {
showSearch(true);
searchbar.value = decodeURIComponent(
(url.params[URL_SEARCH_PARAM]+'').replace(/\+/g, '%20'));
searchbarKeyUpHandler(); // -> doSearch()
} else {
showSearch(false);
}
if (url.params.hasOwnProperty(URL_MARK_PARAM)) {
var words = url.params[URL_MARK_PARAM].split(' ');
marker.mark(words, {
exclude: mark_exclude
});
var markers = document.querySelectorAll("mark");
function hide() {
for (var i = 0; i < markers.length; i++) {
markers[i].classList.add("fade-out");
window.setTimeout(function(e) { marker.unmark(); }, 300);
}
}
for (var i = 0; i < markers.length; i++) {
markers[i].addEventListener('click', hide);
}
}
}
// Eventhandler for keyevents on `document`
function globalKeyHandler(e) {
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey || e.target.type === 'textarea') { return; }
if (e.keyCode === ESCAPE_KEYCODE) {
e.preventDefault();
searchbar.classList.remove("active");
setSearchUrlParameters("",
(searchbar.value.trim() !== "") ? "push" : "replace");
if (hasFocus()) {
unfocusSearchbar();
}
showSearch(false);
marker.unmark();
} else if (!hasFocus() && e.keyCode === SEARCH_HOTKEY_KEYCODE) {
e.preventDefault();
showSearch(true);
window.scrollTo(0, 0);
searchbar.select();
} else if (hasFocus() && e.keyCode === DOWN_KEYCODE) {
e.preventDefault();
unfocusSearchbar();
searchresults.firstElementChild.classList.add("focus");
} else if (!hasFocus() && (e.keyCode === DOWN_KEYCODE
|| e.keyCode === UP_KEYCODE
|| e.keyCode === SELECT_KEYCODE)) {
// not `:focus` because browser does annoying scrolling
var focused = searchresults.querySelector("li.focus");
if (!focused) return;
e.preventDefault();
if (e.keyCode === DOWN_KEYCODE) {
var next = focused.nextElementSibling;
if (next) {
focused.classList.remove("focus");
next.classList.add("focus");
}
} else if (e.keyCode === UP_KEYCODE) {
focused.classList.remove("focus");
var prev = focused.previousElementSibling;
if (prev) {
prev.classList.add("focus");
} else {
searchbar.select();
}
} else { // SELECT_KEYCODE
window.location.assign(focused.querySelector('a'));
}
}
}
function showSearch(yes) {
if (yes) {
search_wrap.classList.remove('hidden');
searchicon.setAttribute('aria-expanded', 'true');
} else {
search_wrap.classList.add('hidden');
searchicon.setAttribute('aria-expanded', 'false');
var results = searchresults.children;
for (var i = 0; i < results.length; i++) {
results[i].classList.remove("focus");
}
}
}
function showResults(yes) {
if (yes) {
searchresults_outer.classList.remove('hidden');
} else {
searchresults_outer.classList.add('hidden');
}
}
// Eventhandler for search icon
function searchIconClickHandler() {
if (search_wrap.classList.contains('hidden')) {
showSearch(true);
window.scrollTo(0, 0);
searchbar.select();
} else {
showSearch(false);
}
}
// Eventhandler for keyevents while the searchbar is focused
function searchbarKeyUpHandler() {
var searchterm = searchbar.value.trim();
if (searchterm != "") {
searchbar.classList.add("active");
doSearch(searchterm);
} else {
searchbar.classList.remove("active");
showResults(false);
removeChildren(searchresults);
}
setSearchUrlParameters(searchterm, "push_if_new_search_else_replace");
// Remove marks
marker.unmark();
}
// Update current url with ?URL_SEARCH_PARAM= parameter, remove ?URL_MARK_PARAM and #heading-anchor .
// `action` can be one of "push", "replace", "push_if_new_search_else_replace"
// and replaces or pushes a new browser history item.
// "push_if_new_search_else_replace" pushes if there is no `?URL_SEARCH_PARAM=abc` yet.
function setSearchUrlParameters(searchterm, action) {
var url = parseURL(window.location.href);
var first_search = ! url.params.hasOwnProperty(URL_SEARCH_PARAM);
if (searchterm != "" || action == "push_if_new_search_else_replace") {
url.params[URL_SEARCH_PARAM] = searchterm;
delete url.params[URL_MARK_PARAM];
url.hash = "";
} else {
delete url.params[URL_SEARCH_PARAM];
}
// A new search will also add a new history item, so the user can go back
// to the page prior to searching. A updated search term will only replace
// the url.
if (action == "push" || (action == "push_if_new_search_else_replace" && first_search) ) {
history.pushState({}, document.title, renderURL(url));
} else if (action == "replace" || (action == "push_if_new_search_else_replace" && !first_search) ) {
history.replaceState({}, document.title, renderURL(url));
}
}
function doSearch(searchterm) {
// Don't search the same twice
if (current_searchterm == searchterm) { return; }
else { current_searchterm = searchterm; }
if (searchindex == null) { return; }
// Do the actual search
var results = searchindex.search(searchterm, search_options);
var resultcount = Math.min(results.length, results_options.limit_results);
// Display search metrics
searchresults_header.innerText = formatSearchMetric(resultcount, searchterm);
// Clear and insert results
var searchterms = searchterm.split(' ');
removeChildren(searchresults);
for(var i = 0; i < resultcount ; i++){
var resultElem = document.createElement('li');
resultElem.innerHTML = formatSearchResult(results[i], searchterms);
searchresults.appendChild(resultElem);
}
// Display results
showResults(true);
}
fetch(path_to_root + 'searchindex.json')
.then(response => response.json())
.then(json => init(json))
.catch(error => { // Try to load searchindex.js if fetch failed
var script = document.createElement('script');
script.src = path_to_root + 'searchindex.js';
script.onload = () => init(window.search);
document.head.appendChild(script);
});
// Exported functions
search.hasFocus = hasFocus;
})(window.search);

1
book/searchindex.js Normal file

File diff suppressed because one or more lines are too long

1
book/searchindex.json Normal file

File diff suppressed because one or more lines are too long

7
book/theme-dawn.js Normal file
View File

@@ -0,0 +1,7 @@
ace.define("ace/theme/dawn",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!1,t.cssClass="ace-dawn",t.cssText=".ace-dawn .ace_gutter {background: #ebebeb;color: #333}.ace-dawn .ace_print-margin {width: 1px;background: #e8e8e8}.ace-dawn {background-color: #F9F9F9;color: #080808}.ace-dawn .ace_cursor {color: #000000}.ace-dawn .ace_marker-layer .ace_selection {background: rgba(39, 95, 255, 0.30)}.ace-dawn.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #F9F9F9;}.ace-dawn .ace_marker-layer .ace_step {background: rgb(255, 255, 0)}.ace-dawn .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgba(75, 75, 126, 0.50)}.ace-dawn .ace_marker-layer .ace_active-line {background: rgba(36, 99, 180, 0.12)}.ace-dawn .ace_gutter-active-line {background-color : #dcdcdc}.ace-dawn .ace_marker-layer .ace_selected-word {border: 1px solid rgba(39, 95, 255, 0.30)}.ace-dawn .ace_invisible {color: rgba(75, 75, 126, 0.50)}.ace-dawn .ace_keyword,.ace-dawn .ace_meta {color: #794938}.ace-dawn .ace_constant,.ace-dawn .ace_constant.ace_character,.ace-dawn .ace_constant.ace_character.ace_escape,.ace-dawn .ace_constant.ace_other {color: #811F24}.ace-dawn .ace_invalid.ace_illegal {text-decoration: underline;font-style: italic;color: #F8F8F8;background-color: #B52A1D}.ace-dawn .ace_invalid.ace_deprecated {text-decoration: underline;font-style: italic;color: #B52A1D}.ace-dawn .ace_support {color: #691C97}.ace-dawn .ace_support.ace_constant {color: #B4371F}.ace-dawn .ace_fold {background-color: #794938;border-color: #080808}.ace-dawn .ace_list,.ace-dawn .ace_markup.ace_list,.ace-dawn .ace_support.ace_function {color: #693A17}.ace-dawn .ace_storage {font-style: italic;color: #A71D5D}.ace-dawn .ace_string {color: #0B6125}.ace-dawn .ace_string.ace_regexp {color: #CF5628}.ace-dawn .ace_comment {font-style: italic;color: #5A525F}.ace-dawn .ace_heading,.ace-dawn .ace_markup.ace_heading {color: #19356D}.ace-dawn .ace_variable {color: #234A97}.ace-dawn .ace_indent-guide {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYLh/5+x/AAizA4hxNNsZAAAAAElFTkSuQmCC) right repeat-y}";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}); (function() {
ace.require(["ace/theme/dawn"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();

View File

@@ -0,0 +1,7 @@
ace.define("ace/theme/tomorrow_night",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!0,t.cssClass="ace-tomorrow-night",t.cssText=".ace-tomorrow-night .ace_gutter {background: #25282c;color: #C5C8C6}.ace-tomorrow-night .ace_print-margin {width: 1px;background: #25282c}.ace-tomorrow-night {background-color: #1D1F21;color: #C5C8C6}.ace-tomorrow-night .ace_cursor {color: #AEAFAD}.ace-tomorrow-night .ace_marker-layer .ace_selection {background: #373B41}.ace-tomorrow-night.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #1D1F21;}.ace-tomorrow-night .ace_marker-layer .ace_step {background: rgb(102, 82, 0)}.ace-tomorrow-night .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid #4B4E55}.ace-tomorrow-night .ace_marker-layer .ace_active-line {background: #282A2E}.ace-tomorrow-night .ace_gutter-active-line {background-color: #282A2E}.ace-tomorrow-night .ace_marker-layer .ace_selected-word {border: 1px solid #373B41}.ace-tomorrow-night .ace_invisible {color: #4B4E55}.ace-tomorrow-night .ace_keyword,.ace-tomorrow-night .ace_meta,.ace-tomorrow-night .ace_storage,.ace-tomorrow-night .ace_storage.ace_type,.ace-tomorrow-night .ace_support.ace_type {color: #B294BB}.ace-tomorrow-night .ace_keyword.ace_operator {color: #8ABEB7}.ace-tomorrow-night .ace_constant.ace_character,.ace-tomorrow-night .ace_constant.ace_language,.ace-tomorrow-night .ace_constant.ace_numeric,.ace-tomorrow-night .ace_keyword.ace_other.ace_unit,.ace-tomorrow-night .ace_support.ace_constant,.ace-tomorrow-night .ace_variable.ace_parameter {color: #DE935F}.ace-tomorrow-night .ace_constant.ace_other {color: #CED1CF}.ace-tomorrow-night .ace_invalid {color: #CED2CF;background-color: #DF5F5F}.ace-tomorrow-night .ace_invalid.ace_deprecated {color: #CED2CF;background-color: #B798BF}.ace-tomorrow-night .ace_fold {background-color: #81A2BE;border-color: #C5C8C6}.ace-tomorrow-night .ace_entity.ace_name.ace_function,.ace-tomorrow-night .ace_support.ace_function,.ace-tomorrow-night .ace_variable {color: #81A2BE}.ace-tomorrow-night .ace_support.ace_class,.ace-tomorrow-night .ace_support.ace_type {color: #F0C674}.ace-tomorrow-night .ace_heading,.ace-tomorrow-night .ace_markup.ace_heading,.ace-tomorrow-night .ace_string {color: #B5BD68}.ace-tomorrow-night .ace_entity.ace_name.ace_tag,.ace-tomorrow-night .ace_entity.ace_other.ace_attribute-name,.ace-tomorrow-night .ace_meta.ace_tag,.ace-tomorrow-night .ace_string.ace_regexp,.ace-tomorrow-night .ace_variable {color: #CC6666}.ace-tomorrow-night .ace_comment {color: #969896}.ace-tomorrow-night .ace_indent-guide {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYHB3d/8PAAOIAdULw8qMAAAAAElFTkSuQmCC) right repeat-y}";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}); (function() {
ace.require(["ace/theme/tomorrow_night"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();

View File

@@ -4,8 +4,8 @@ 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.
While we do that we'll also explain some aspects when it comes to 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
@@ -68,7 +68,7 @@ fn main() {
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
First of all. For computers to be [_efficient_](https://en.wikipedia.org/wiki/Efficiency) they needs to multitask. Once you
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.
@@ -79,10 +79,9 @@ Webservers is all about I/O and handling small tasks
threads as of today because of the memory they require and the overhead involved
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.
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.
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
@@ -105,15 +104,16 @@ 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 `async`, `await`, `Futures` or `Pin`.
The typical flow will be like this:
**The typical flow looks like this:**
1. Run som non-blocking code
1. Run some non-blocking code
2. Make a blocking call to some external resource
3. CPU jumps to the "main" thread which schedules a different thread to run and
"jumps" to that stack
4. Run some non-blocking code on the new thread until a new blocking call or the
task is finished
5. "jumps" back to the "main" thread, schedule a new thread to run and jump to that
5. "jumps" back to the "main" thread, schedule a new thread which is ready to make
progress and jump to that.
These "jumps" are know as **context switches**. Your OS is doing it many times each
second as you read this.
@@ -374,10 +374,11 @@ 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.
instructions we want to run later together with whatever state is needed. In rust this
would be a `closure`. In the example below, we save this information in a `HashMap`
but it's not the only option.
The basic idea of not involving threads as a primary way to achieve concurrency
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.
@@ -385,7 +386,7 @@ Rust uses today which we'll soon get to.
- Easy to implement in most languages
- No context switching
- Low memory overhead (in most cases)
- Relatively low memory overhead (in most cases)
**Drawbacks:**
@@ -472,11 +473,11 @@ impl Runtime {
We're keeping this super simple, and you might wonder what's the difference
between this approach and the one using OS threads an passing in the callbacks
to the OS threads directly.
to the OS threads directly.
The difference is that the callbacks are run on the
same thread using this example. The OS threads we create are basically just used
as timers.
as timers but could represent any kind of resource that we'll have to wait for.
## From callbacks to promises
@@ -552,7 +553,7 @@ 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
> To avoid confusion later on: There's 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.

View File

@@ -1,8 +1,9 @@
# Futures Explained in 200 Lines of Rust
This book aims to explain `Futures` in Rust using an example driven approach,
exploring why they're designed the way they are, the alternatives and how
they work.
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.
Going into the level of detail I do in this book is not needed to use futures
or async/await in Rust. It's for the curious out there that want to know _how_
@@ -16,9 +17,8 @@ simple runtime in this book introducing some concepts but it's enough to get
started.
[Stjepan Glavina](https://github.com/stjepang) has made an excellent series of
articles about async runtimes and executors, and if the rumors are right he's
even working on a new async runtime that should be easy enough to use as
learning material.
articles about async runtimes and executors, and if the rumors are right there
is more to come from him in the near future.
The way you should go about it is to read this book first, then continue
reading the [articles from stejpang](https://stjepang.github.io/) to learn more
@@ -39,6 +39,8 @@ I hope you enjoy the ride.
> [the repository for the book itself here][book_repo]. The final example which
> you can clone, fork or copy [can be found here][example_repo]. Any suggestions
> or improvements can be filed as a PR or in the issue tracker for the book.
>
> As always, all kinds of feedback is welcome.
## Reader exercises and further reading