1## Graceful Shutdown and Cleanup
2
3The code in Listing 20-20 is responding to requests asynchronously through the
4use of a thread pool, as we intended. We get some warnings about the `workers`,
5`id`, and `thread` fields that we’re not using in a direct way that reminds us
6we’re not cleaning up anything. When we use the less elegant <span
7class="keystroke">ctrl-c</span> method to halt the main thread, all other
8threads are stopped immediately as well, even if they’re in the middle of
9serving a request.
10
11Now we’ll implement the `Drop` trait to call `join` on each of the threads in
12the pool so they can finish the requests they’re working on before closing.
13Then we’ll implement a way to tell the threads they should stop accepting new
14requests and shut down. To see this code in action, we’ll modify our server to
15accept only two requests before gracefully shutting down its thread pool.
16
17### Implementing the `Drop` Trait on `ThreadPool`
18
19Let’s start with implementing `Drop` on our thread pool. When the pool is
20dropped, our threads should all join to make sure they finish their work.
21Listing 20-22 shows a first attempt at a `Drop` implementation; this code won’t
22quite work yet.
23
24<span class="filename">Filename: src/lib.rs</span>
25
26```rust,ignore,does_not_compile
27{{#rustdoc_include ../listings/ch20-web-server/listing-20-22/src/lib.rs:here}}
28```
29
30<span class="caption">Listing 20-22: Joining each thread when the thread pool
31goes out of scope</span>
32
33First, we loop through each of the thread pool `workers`. We use `&mut` for
34this because `self` is a mutable reference, and we also need to be able to
35mutate `worker`. For each worker, we print a message saying that this
36particular worker is shutting down, and then we call `join` on that worker’s
37thread. If the call to `join` fails, we use `unwrap` to make Rust panic and go
38into an ungraceful shutdown.
39
40Here is the error we get when we compile this code:
41
42```console
43{{#include ../listings/ch20-web-server/listing-20-22/output.txt}}
44```
45
46The error tells us we can’t call `join` because we only have a mutable borrow
47of each `worker` and `join` takes ownership of its argument. To solve this
48issue, we need to move the thread out of the `Worker` instance that owns
49`thread` so `join` can consume the thread. We did this in Listing 17-15: if
50`Worker` holds an `Option<thread::JoinHandle<()>>` instead, we can call the
51`take` method on the `Option` to move the value out of the `Some` variant and
52leave a `None` variant in its place. In other words, a `Worker` that is running
53will have a `Some` variant in `thread`, and when we want to clean up a
54`Worker`, we’ll replace `Some` with `None` so the `Worker` doesn’t have a
55thread to run.
56
57So we know we want to update the definition of `Worker` like this:
58
59<span class="filename">Filename: src/lib.rs</span>
60
61```rust,ignore,does_not_compile
62{{#rustdoc_include ../listings/ch20-web-server/no-listing-04-update-worker-definition/src/lib.rs:here}}
63```
64
65Now let’s lean on the compiler to find the other places that need to change.
66Checking this code, we get two errors:
67
68```console
69{{#include ../listings/ch20-web-server/no-listing-04-update-worker-definition/output.txt}}
70```
71
72Let’s address the second error, which points to the code at the end of
73`Worker::new`; we need to wrap the `thread` value in `Some` when we create a
74new `Worker`. Make the following changes to fix this error:
75
76<span class="filename">Filename: src/lib.rs</span>
77
78```rust,ignore
79{{#rustdoc_include ../listings/ch20-web-server/no-listing-05-fix-worker-new/src/lib.rs:here}}
80```
81
82The first error is in our `Drop` implementation. We mentioned earlier that we
83intended to call `take` on the `Option` value to move `thread` out of `worker`.
84The following changes will do so:
85
86<span class="filename">Filename: src/lib.rs</span>
87
88```rust,ignore
89{{#rustdoc_include ../listings/ch20-web-server/no-listing-06-fix-threadpool-drop/src/lib.rs:here}}
90```
91
92As discussed in Chapter 17, the `take` method on `Option` takes the `Some`
93variant out and leaves `None` in its place. We’re using `if let` to destructure
94the `Some` and get the thread; then we call `join` on the thread. If a worker’s
95thread is already `None`, we know that worker has already had its thread
96cleaned up, so nothing happens in that case.
97
98### Signaling to the Threads to Stop Listening for Jobs
99
100With all the changes we’ve made, our code compiles without any warnings. But
101the bad news is this code doesn’t function the way we want it to yet. The key
102is the logic in the closures run by the threads of the `Worker` instances: at
103the moment, we call `join`, but that won’t shut down the threads because they
104`loop` forever looking for jobs. If we try to drop our `ThreadPool` with our
105current implementation of `drop`, the main thread will block forever waiting
106for the first thread to finish.
107
108To fix this problem, we’ll modify the threads so they listen for either a `Job`
109to run or a signal that they should stop listening and exit the infinite loop.
110Instead of `Job` instances, our channel will send one of these two enum
111variants.
112
113<span class="filename">Filename: src/lib.rs</span>
114
115```rust,noplayground
116{{#rustdoc_include ../listings/ch20-web-server/no-listing-07-define-message-enum/src/lib.rs:here}}
117```
118
119This `Message` enum will either be a `NewJob` variant that holds the `Job` the
120thread should run, or it will be a `Terminate` variant that will cause the
121thread to exit its loop and stop.
122
123We need to adjust the channel to use values of type `Message` rather than type
124`Job`, as shown in Listing 20-23.
125
126<span class="filename">Filename: src/lib.rs</span>
127
128```rust,ignore
129{{#rustdoc_include ../listings/ch20-web-server/listing-20-23/src/lib.rs:here}}
130```
131
132<span class="caption">Listing 20-23: Sending and receiving `Message` values and
133exiting the loop if a `Worker` receives `Message::Terminate`</span>
134
135To incorporate the `Message` enum, we need to change `Job` to `Message` in two
136places: the definition of `ThreadPool` and the signature of `Worker::new`. The
137`execute` method of `ThreadPool` needs to send jobs wrapped in the
138`Message::NewJob` variant. Then, in `Worker::new` where a `Message` is received
139from the channel, the job will be processed if the `NewJob` variant is
140received, and the thread will break out of the loop if the `Terminate` variant
141is received.
142
143With these changes, the code will compile and continue to function in the same
144way as it did after Listing 20-20. But we’ll get a warning because we aren’t
145creating any messages of the `Terminate` variety. Let’s fix this warning by
146changing our `Drop` implementation to look like Listing 20-24.
147
148<span class="filename">Filename: src/lib.rs</span>
149
150```rust,ignore
151{{#rustdoc_include ../listings/ch20-web-server/listing-20-24/src/lib.rs:here}}
152```
153
154<span class="caption">Listing 20-24: Sending `Message::Terminate` to the
155workers before calling `join` on each worker thread</span>
156
157We’re now iterating over the workers twice: once to send one `Terminate`
158message for each worker and once to call `join` on each worker’s thread. If we
159tried to send a message and `join` immediately in the same loop, we couldn’t
160guarantee that the worker in the current iteration would be the one to get the
161message from the channel.
162
163To better understand why we need two separate loops, imagine a scenario with
164two workers. If we used a single loop to iterate through each worker, on the
165first iteration a terminate message would be sent down the channel and `join`
166called on the first worker’s thread. If that first worker was busy processing a
167request at that moment, the second worker would pick up the terminate message
168from the channel and shut down. We would be left waiting on the first worker to
169shut down, but it never would because the second thread picked up the terminate
170message. Deadlock!
171
172To prevent this scenario, we first put all of our `Terminate` messages on the
173channel in one loop; then we join on all the threads in another loop. Each
174worker will stop receiving requests on the channel once it gets a terminate
175message. So, we can be sure that if we send the same number of terminate
176messages as there are workers, each worker will receive a terminate message
177before `join` is called on its thread.
178
179To see this code in action, let’s modify `main` to accept only two requests
180before gracefully shutting down the server, as shown in Listing 20-25.
181
182<span class="filename">Filename: src/bin/main.rs</span>
183
184```rust,ignore
185{{#rustdoc_include ../listings/ch20-web-server/listing-20-25/src/bin/main.rs:here}}
186```
187
188<span class="caption">Listing 20-25: Shut down the server after serving two
189requests by exiting the loop</span>
190
191You wouldn’t want a real-world web server to shut down after serving only two
192requests. This code just demonstrates that the graceful shutdown and cleanup is
193in working order.
194
195The `take` method is defined in the `Iterator` trait and limits the iteration
196to the first two items at most. The `ThreadPool` will go out of scope at the
197end of `main`, and the `drop` implementation will run.
198
199Start the server with `cargo run`, and make three requests. The third request
200should error, and in your terminal you should see output similar to this:
201
202<!-- manual-regeneration
203cd listings/ch20-web-server/listing-20-25
204cargo run
205curl http://127.0.0.1:7878
206curl http://127.0.0.1:7878
207curl http://127.0.0.1:7878
208third request will error because server will have shut down
209copy output below
210Can't automate because the output depends on making requests
211-->
212
213```console
214$ cargo run
215   Compiling hello v0.1.0 (file:///projects/hello)
216    Finished dev [unoptimized + debuginfo] target(s) in 1.0s
217     Running `target/debug/main`
218Worker 0 got a job; executing.
219Worker 3 got a job; executing.
220Shutting down.
221Sending terminate message to all workers.
222Shutting down all workers.
223Shutting down worker 0
224Worker 1 was told to terminate.
225Worker 2 was told to terminate.
226Worker 0 was told to terminate.
227Worker 3 was told to terminate.
228Shutting down worker 1
229Shutting down worker 2
230Shutting down worker 3
231```
232
233You might see a different ordering of workers and messages printed. We can see
234how this code works from the messages: workers 0 and 3 got the first two
235requests, and then on the third request, the server stopped accepting
236connections. When the `ThreadPool` goes out of scope at the end of `main`, its
237`Drop` implementation kicks in, and the pool tells all workers to terminate.
238The workers each print a message when they see the terminate message, and then
239the thread pool calls `join` to shut down each worker thread.
240
241Notice one interesting aspect of this particular execution: the `ThreadPool`
242sent the terminate messages down the channel, and before any worker received
243the messages, we tried to join worker 0. Worker 0 had not yet received the
244terminate message, so the main thread blocked waiting for worker 0 to finish.
245In the meantime, each of the workers received the termination messages. When
246worker 0 finished, the main thread waited for the rest of the workers to
247finish. At that point, they had all received the termination message and were
248able to shut down.
249
250Congrats! We’ve now completed our project; we have a basic web server that uses
251a thread pool to respond asynchronously. We’re able to perform a graceful
252shutdown of the server, which cleans up all the threads in the pool.
253
254Here’s the full code for reference:
255
256<span class="filename">Filename: src/bin/main.rs</span>
257
258```rust,ignore
259{{#rustdoc_include ../listings/ch20-web-server/no-listing-08-final-code/src/bin/main.rs}}
260```
261
262<span class="filename">Filename: src/lib.rs</span>
263
264```rust,noplayground
265{{#rustdoc_include ../listings/ch20-web-server/no-listing-08-final-code/src/lib.rs}}
266```
267
268We could do more here! If you want to continue enhancing this project, here are
269some ideas:
270
271* Add more documentation to `ThreadPool` and its public methods.
272* Add tests of the library’s functionality.
273* Change calls to `unwrap` to more robust error handling.
274* Use `ThreadPool` to perform some task other than serving web requests.
275* Find a thread pool crate on [crates.io](https://crates.io/) and implement a
276  similar web server using the crate instead. Then compare its API and
277  robustness to the thread pool we implemented.
278
279## Summary
280
281Well done! You’ve made it to the end of the book! We want to thank you for
282joining us on this tour of Rust. You’re now ready to implement your own Rust
283projects and help with other peoples’ projects. Keep in mind that there is a
284welcoming community of other Rustaceans who would love to help you with any
285challenges you encounter on your Rust journey.
286