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