Skip to content

Commit 1594c30

Browse files
committed
Backport changes to md
1 parent 6e68368 commit 1594c30

File tree

2 files changed

+72
-64
lines changed

2 files changed

+72
-64
lines changed

nostarch/chapter21.md

Lines changed: 72 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@ lessons.
1616
For our final project, we’ll make a web server that says “hello” and looks like
1717
Figure 21-1 in a web browser.
1818

19-
!hello from rust at *img/trpl21-01.png*
20-
21-
Figure 21-1: Our final shared project
22-
2319
Here is our plan for building the web server:
2420

2521
1. Learn a bit about TCP and HTTP.
@@ -28,12 +24,16 @@ Here is our plan for building the web server:
2824
1. Create a proper HTTP response.
2925
1. Improve the throughput of our server with a thread pool.
3026

31-
Before we get started, we should mention two details: First, the method we’ll
27+
!hello from rust at *img/trpl21-01.png*
28+
29+
Figure 21-1: Our final shared project
30+
31+
Before we get started, we should mention two details. First, the method we’ll
3232
use won’t be the best way to build a web server with Rust. Community members
33-
have published a number of production-ready crates available on
34-
crates.io at *https://crates.io/* that provide more complete web server and thread
35-
pool implementations than we’ll build. However, our intention in this chapter is
36-
to help you learn, not to take the easy route. Because Rust is a systems
33+
have published a number of production-ready crates available at
34+
https://crates.io that provide more complete web server and thread pool
35+
implementations than we’ll build. However, our intention in this chapter is to
36+
help you learn, not to take the easy route. Because Rust is a systems
3737
programming language, we can choose the level of abstraction we want to work
3838
with and can go to a lower level than is possible or practical in other
3939
languages.
@@ -106,7 +106,7 @@ Using `TcpListener`, we can listen for TCP connections at the address
106106
`127.0.0.1:7878`. In the address, the section before the colon is an IP address
107107
representing your computer (this is the same on every computer and doesn’t
108108
represent the authors’ computer specifically), and `7878` is the port. We’ve
109-
chosen this port for two reasons: HTTP isn’t normally accepted on this port so
109+
chosen this port for two reasons: HTTP isn’t normally accepted on this port, so
110110
our server is unlikely to conflict with any other web server you might have
111111
running on your machine, and 7878 is *rust* typed on a telephone.
112112

@@ -116,14 +116,11 @@ because, in networking, connecting to a port to listen to is known as “binding
116116
to a port.”
117117

118118
The `bind` function returns a `Result<T, E>`, which indicates that it’s
119-
possible for binding to fail. For example, connecting to port 80 requires
120-
administrator privileges (non-administrators can listen only on ports higher
121-
than 1023), so if we tried to connect to port 80 without being an
122-
administrator, binding wouldn’t work. Binding also wouldn’t work, for example,
123-
if we ran two instances of our program and so had two programs listening to the
124-
same port. Because we’re writing a basic server just for learning purposes, we
125-
won’t worry about handling these kinds of errors; instead, we use `unwrap` to
126-
stop the program if errors happen.
119+
possible for binding to fail. For example, if we ran two instances of our
120+
program and so had two programs listening to the same port. Because we’re
121+
writing a basic server just for learning purposes, we won’t worry about
122+
handling these kinds of errors; instead, we use `unwrap` to stop the program if
123+
errors happen.
127124

128125
The `incoming` method on `TcpListener` returns an iterator that gives us a
129126
sequence of streams (more specifically, streams of type `TcpStream`). A single
@@ -172,7 +169,7 @@ part of the `drop` implementation. Browsers sometimes deal with closed
172169
connections by retrying, because the problem might be temporary. The important
173170
factor is that we’ve successfully gotten a handle to a TCP connection!
174171

175-
Remember to stop the program by pressing <kbd>ctrl</kbd>-<kbd>c</kbd> when
172+
Remember to stop the program by pressing <kbd>ctrl</kbd>-<kbd>C</kbd> when
176173
you’re done running a particular version of the code. Then restart the program
177174
by invoking the `cargo run` command after you’ve made each set of code changes
178175
to make sure you’re running the newest code.
@@ -190,7 +187,7 @@ src/main.rs
190187

191188
```
192189
use std::{
193-
io::{prelude::*, BufReader},
190+
io::{BufReader, prelude::*},
194191
net::{TcpListener, TcpStream},
195192
};
196193
@@ -225,7 +222,7 @@ connection, we now call the new `handle_connection` function and pass the
225222
`stream` to it.
226223

227224
In the `handle_connection` function, we create a new `BufReader` instance that
228-
wraps a reference to the `stream`. The `BufReader` adds buffering by managing calls
225+
wraps a reference to the `stream`. `BufReader` adds buffering by managing calls
229226
to the `std::io::Read` trait methods for us.
230227

231228
We create a variable named `http_request` to collect the lines of the request
@@ -303,7 +300,7 @@ The next part of the request line is */*, which indicates the *uniform resource
303300
identifier* *(URI)* the client is requesting: a URI is almost, but not quite,
304301
the same as a *uniform resource locator* *(URL)*. The difference between URIs
305302
and URLs isn’t important for our purposes in this chapter, but the HTTP spec
306-
uses the term URI, so we can just mentally substitute *URL* for *URI* here.
303+
uses the term *URI*, so we can just mentally substitute *URL* for *URI* here.
307304

308305
The last part is the HTTP version the client uses, and then the request line
309306
ends in a CRLF sequence. (CRLF stands for *carriage return* and *line feed*,
@@ -421,7 +418,7 @@ src/main.rs
421418
```
422419
use std::{
423420
fs,
424-
io::{prelude::*, BufReader},
421+
io::{BufReader, prelude::*},
425422
net::{TcpListener, TcpStream},
426423
};
427424
// --snip--
@@ -472,7 +469,7 @@ request to */*.
472469

473470
Right now, our web server will return the HTML in the file no matter what the
474471
client requested. Let’s add functionality to check that the browser is
475-
requesting */* before returning the HTML file and return an error if the
472+
requesting */* before returning the HTML file, and return an error if the
476473
browser requests anything else. For this we need to modify `handle_connection`,
477474
as shown in Listing 21-6. This new code checks the content of the request
478475
received against what we know a request for */* looks like and adds `if` and
@@ -552,7 +549,7 @@ Listing 21-7: Responding with status code 404 and an error page if anything othe
552549
Here, our response has a status line with status code 404 and the reason phrase
553550
`NOT FOUND`. The body of the response will be the HTML in the file *404.html*.
554551
You’ll need to create a *404.html* file next to *hello.html* for the error
555-
page; again feel free to use any HTML you want or use the example HTML in
552+
page; again feel free to use any HTML you want, or use the example HTML in
556553
Listing 21-8.
557554

558555
404.html
@@ -645,7 +642,10 @@ process, subsequent requests will have to wait until the long request is
645642
finished, even if the new requests can be processed quickly. We’ll need to fix
646643
this, but first we’ll look at the problem in action.
647644

648-
### Simulating a Slow Request in the Current Server Implementation
645+
<!-- Old headings. Do not remove or links may break. -->
646+
<a id="simulating-a-slow-request-in-the-current-server-implementation"></a>
647+
648+
### Simulating a Slow Request
649649

650650
We’ll look at how a slow-processing request can affect other requests made to
651651
our current server implementation. Listing 21-10 implements handling a request
@@ -657,7 +657,7 @@ src/main.rs
657657
```
658658
use std::{
659659
fs,
660-
io::{prelude::*, BufReader},
660+
io::{BufReader, prelude::*},
661661
net::{TcpListener, TcpStream},
662662
thread,
663663
time::Duration,
@@ -680,10 +680,10 @@ fn handle_connection(mut stream: TcpStream) {
680680
}
681681
```
682682

683-
Listing 21-10: Simulating a slow request by sleeping for 5 seconds
683+
Listing 21-10: Simulating a slow request by sleeping for five seconds
684684

685685
We switched from `if` to `match` now that we have three cases. We need to
686-
explicitly match on a slice of `request_line` to pattern match against the
686+
explicitly match on a slice of `request_line` to pattern-match against the
687687
string literal values; `match` doesn’t do automatic referencing and
688688
dereferencing, like the equality method does.
689689

@@ -696,10 +696,10 @@ You can see how primitive our server is: real libraries would handle the
696696
recognition of multiple requests in a much less verbose way!
697697

698698
Start the server using `cargo run`. Then open two browser windows: one for
699-
*http://127.0.0.1:7878/* and the other for *http://127.0.0.1:7878/sleep*. If
700-
you enter the */* URI a few times, as before, you’ll see it respond quickly.
701-
But if you enter */sleep* and then load */*, you’ll see that */* waits until
702-
`sleep` has slept for its full five seconds before loading.
699+
*http://127.0.0.1:7878* and the other for *http://127.0.0.1:7878/sleep*. If you
700+
enter the */* URI a few times, as before, you’ll see it respond quickly. But if
701+
you enter */sleep* and then load */*, you’ll see that */* waits until `sleep`
702+
has slept for its full five seconds before loading.
703703

704704
There are multiple techniques we could use to avoid requests backing up behind
705705
a slow request, including using async as we did Chapter 17; the one we’ll
@@ -734,7 +734,7 @@ we can handle before reaching that point.
734734

735735
This technique is just one of many ways to improve the throughput of a web
736736
server. Other options you might explore are the fork/join model, the
737-
single-threaded async I/O model, and the multi-threaded async I/O model. If
737+
single-threaded async I/O model, and the multithreaded async I/O model. If
738738
you’re interested in this topic, you can read more about other solutions and
739739
try to implement them; with a low-level language like Rust, all of these
740740
options are possible.
@@ -762,9 +762,10 @@ First, let’s explore how our code might look if it did create a new thread for
762762
every connection. As mentioned earlier, this isn’t our final plan due to the
763763
problems with potentially spawning an unlimited number of threads, but it is a
764764
starting point to get a working multithreaded server first. Then we’ll add the
765-
thread pool as an improvement, and contrasting the two solutions will be
766-
easier. Listing 21-11 shows the changes to make to `main` to spawn a new thread
767-
to handle each stream within the `for` loop.
765+
thread pool as an improvement, and contrasting the two solutions will be easier.
766+
767+
Listing 21-11 shows the changes to make to `main` to spawn a new thread to
768+
handle each stream within the `for` loop.
768769

769770
src/main.rs
770771

@@ -830,13 +831,13 @@ of threads, in this case four. Then, in the `for` loop, `pool.execute` has a
830831
similar interface as `thread::spawn` in that it takes a closure the pool should
831832
run for each stream. We need to implement `pool.execute` so it takes the
832833
closure and gives it to a thread in the pool to run. This code won’t yet
833-
compile, but we’ll try so the compiler can guide us in how to fix it.
834+
compile, but we’ll try so that the compiler can guide us in how to fix it.
834835

835836
<!-- Old headings. Do not remove or links may break. -->
836837

837838
<a id="building-the-threadpool-struct-using-compiler-driven-development"></a>
838839

839-
#### Building ThreadPool Using Compiler Driven Development
840+
#### Building ThreadPool Using Compiler-Driven Development
840841

841842
Make the changes in Listing 21-12 to *src/main.rs*, and then let’s use the
842843
compiler errors from `cargo check` to drive our development. Here is the first
@@ -874,7 +875,7 @@ pub struct ThreadPool;
874875

875876

876877

877-
Then edit *main.rs* file to bring `ThreadPool` into scope from the library
878+
Then edit the *main.rs* file to bring `ThreadPool` into scope from the library
878879
crate by adding the following code to the top of *src/main.rs*:
879880

880881
src/main.rs
@@ -1020,7 +1021,7 @@ yet!
10201021
> writing unit tests to check that the code compiles *and* has the behavior we
10211022
> want.
10221023
1023-
Consider: what would be different here if we were going to execute a *future*
1024+
Consider: what would be different here if we were going to execute a future
10241025
instead of a closure?
10251026

10261027
#### Validating the Number of Threads in new
@@ -1141,7 +1142,10 @@ which resizes itself as elements are inserted.
11411142

11421143
When you run `cargo check` again, it should succeed.
11431144

1144-
#### A Worker Struct Responsible for Sending Code from the ThreadPool to a Thread
1145+
<!-- Old headings. Do not remove or links may break. -->
1146+
<a id ="a-worker-struct-responsible-for-sending-code-from-the-threadpool-to-a-thread"></a>
1147+
1148+
#### Sending Code from the ThreadPool to a Thread
11451149

11461150
We left a comment in the `for` loop in Listing 21-14 regarding the creation of
11471151
threads. Here, we’ll look at how we actually create threads. The standard
@@ -1156,11 +1160,11 @@ We’ll implement this behavior by introducing a new data structure between the
11561160
`ThreadPool` and the threads that will manage this new behavior. We’ll call
11571161
this data structure *Worker*, which is a common term in pooling
11581162
implementations. The `Worker` picks up code that needs to be run and runs the
1159-
code in the Worker’s thread.
1163+
code in its thread.
11601164

11611165
Think of people working in the kitchen at a restaurant: the workers wait until
11621166
orders come in from customers, and then they’re responsible for taking those
1163-
orders and fulfilling them.
1167+
orders and filling them.
11641168

11651169
Instead of storing a vector of `JoinHandle<()>` instances in the thread pool,
11661170
we’ll store instances of the `Worker` struct. Each `Worker` will store a single
@@ -1179,7 +1183,7 @@ set up in this way:
11791183
`Worker` instance that holds the `id` and a thread spawned with an empty
11801184
closure.
11811185
1. In `ThreadPool::new`, use the `for` loop counter to generate an `id`, create
1182-
a new `Worker` with that `id`, and store the worker in the vector.
1186+
a new `Worker` with that `id`, and store the `Worker` in the vector.
11831187

11841188
If you’re up for a challenge, try implementing these changes on your own before
11851189
looking at the code in Listing 21-15.
@@ -1413,7 +1417,7 @@ src/lib.rs
14131417

14141418
```
14151419
use std::{
1416-
sync::{mpsc, Arc, Mutex},
1420+
sync::{Arc, Mutex, mpsc},
14171421
thread,
14181422
};
14191423
// --snip--
@@ -1608,8 +1612,9 @@ and 21-20 would be different if we were using futures instead of a closure for
16081612
the work to be done. What types would change? How would the method signatures be
16091613
different, if at all? What parts of the code would stay the same?
16101614

1611-
After learning about the `while let` loop in Chapters 17 and 18, you might be
1612-
wondering why we didn’t write the worker thread code as shown in Listing 21-21.
1615+
After learning about the `while let` loop in Chapter 17 and Chapter 19, you
1616+
might be wondering why we didn’t write the `Worker` thread code as shown in
1617+
Listing 21-21.
16131618

16141619
src/lib.rs
16151620

@@ -1645,7 +1650,7 @@ longer than intended if we aren’t mindful of the lifetime of the
16451650
`MutexGuard<T>`.
16461651

16471652
The code in Listing 21-20 that uses `let job = receiver.lock().unwrap().recv().unwrap();` works because with `let`, any
1648-
temporary values used in the expression on the right hand side of the equal
1653+
temporary values used in the expression on the right-hand side of the equal
16491654
sign are immediately dropped when the `let` statement ends. However, `while let` (and `if let` and `match`) does not drop temporary values until the end of
16501655
the associated block. In Listing 21-21, the lock remains held for the duration
16511656
of the call to `job()`, meaning other `Worker` instances cannot receive jobs.
@@ -1656,7 +1661,7 @@ The code in Listing 21-20 is responding to requests asynchronously through the
16561661
use of a thread pool, as we intended. We get some warnings about the `workers`,
16571662
`id`, and `thread` fields that we’re not using in a direct way that reminds us
16581663
we’re not cleaning up anything. When we use the less elegant
1659-
<kbd>ctrl</kbd>-<kbd>c</kbd> method to halt the main thread, all other threads
1664+
<kbd>ctrl</kbd>-<kbd>C</kbd> method to halt the main thread, all other threads
16601665
are stopped immediately as well, even if they’re in the middle of serving a
16611666
request.
16621667

@@ -1694,9 +1699,9 @@ impl Drop for ThreadPool {
16941699

16951700
Listing 21-22: Joining each thread when the thread pool goes out of scope
16961701

1697-
First, we loop through each of the thread pool `workers`. We use `&mut` for this
1702+
First we loop through each of the thread pool `workers`. We use `&mut` for this
16981703
because `self` is a mutable reference, and we also need to be able to mutate
1699-
`worker`. For each worker, we print a message saying that this particular
1704+
`worker`. For each `worker`, we print a message saying that this particular
17001705
`Worker` instance is shutting down, and then we call `join` on that `Worker`
17011706
instance’s thread. If the call to `join` fails, we use `unwrap` to make Rust
17021707
panic and go into an ungraceful shutdown.
@@ -1738,14 +1743,14 @@ wouldn’t have a thread to run.
17381743
However, the *only* time this would come up would be when dropping the `Worker`.
17391744
In exchange, we’d have to deal with an `Option<thread::JoinHandle<()>>` anywhere
17401745
we accessed `worker.thread`. Idiomatic Rust uses `Option` quite a bit, but when
1741-
you find yourself wrapping something you know will always be present in `Option`
1742-
as a workaround like this, it’s a good idea to look for alternative approaches.
1743-
They can make your code cleaner and less error-prone.
1746+
you find yourself wrapping something you know will always be present in an
1747+
`Option` as a workaround like this, it’s a good idea to look for alternative
1748+
approaches to make your code cleaner and less error-prone.
17441749

17451750
In this case, a better alternative exists: the `Vec::drain` method. It accepts
1746-
a range parameter to specify which items to remove from the `Vec`, and returns
1751+
a range parameter to specify which items to remove from the vector and returns
17471752
an iterator of those items. Passing the `..` range syntax will remove *every*
1748-
value from the `Vec`.
1753+
value from the vector.
17491754

17501755
So we need to update the `ThreadPool` `drop` implementation like this:
17511756

@@ -1766,17 +1771,20 @@ impl Drop for ThreadPool {
17661771

17671772

17681773
This resolves the compiler error and does not require any other changes to our
1769-
code.
1774+
code. Note that, because drop can be called when panicking, the unwrap
1775+
could also panic and cause a double panic, which immediately crashes the
1776+
program and ends any cleanup in progress. This is fine for an example program,
1777+
but isn’t recommended for production code.
17701778

17711779
### Signaling to the Threads to Stop Listening for Jobs
17721780

17731781
With all the changes we’ve made, our code compiles without any warnings.
17741782
However, the bad news is that this code doesn’t function the way we want it to
17751783
yet. The key is the logic in the closures run by the threads of the `Worker`
1776-
instances: at the moment, we call `join`, but that won’t shut down the threads
1777-
because they `loop` forever looking for jobs. If we try to drop our `ThreadPool`
1778-
with our current implementation of `drop`, the main thread will block forever,
1779-
waiting for the first thread to finish.
1784+
instances: at the moment, we call `join`, but that won’t shut down the threads,
1785+
because they `loop` forever looking for jobs. If we try to drop our
1786+
`ThreadPool` with our current implementation of `drop`, the main thread will
1787+
block forever, waiting for the first thread to finish.
17801788

17811789
To fix this problem, we’ll need a change in the `ThreadPool` `drop`
17821790
implementation and then a change in the `Worker` loop.
@@ -1828,7 +1836,7 @@ impl Drop for ThreadPool {
18281836
}
18291837
```
18301838

1831-
Listing 21-23: Explicitly drop `sender` before joining the `Worker` threads
1839+
Listing 21-23: Explicitly dropping `sender` before joining the `Worker` threads
18321840

18331841
Dropping `sender` closes the channel, which indicates no more messages will be
18341842
sent. When that happens, all the calls to `recv` that the `Worker` instances do
@@ -1940,7 +1948,7 @@ wait for each `Worker` thread to finish.
19401948
Notice one interesting aspect of this particular execution: the `ThreadPool`
19411949
dropped the `sender`, and before any `Worker` received an error, we tried to
19421950
join `Worker` 0. `Worker` 0 had not yet gotten an error from `recv`, so the main
1943-
thread blocked waiting for `Worker` 0 to finish. In the meantime, `Worker` 3
1951+
thread blocked, waiting for `Worker` 0 to finish. In the meantime, `Worker` 3
19441952
received a job and then all threads received an error. When `Worker` 0 finished,
19451953
the main thread waited for the rest of the `Worker` instances to finish. At that
19461954
point, they had all exited their loops and stopped.

nostarch/docx/chapter21.docx

4.36 KB
Binary file not shown.

0 commit comments

Comments
 (0)