rust-lang / book

The Rust Programming Language
https://doc.rust-lang.org/book/
Other
15k stars 3.39k forks source link

Chapter 20.3 Webserver Deadlock #3356

Open buggymcbugfix opened 2 years ago

buggymcbugfix commented 2 years ago

When I run the final webserver project, exactly as given in the book, it deadlocks for me. My setup is running the server in one shell and the following loop in the other:

for i in {1..10}
do
        curl http://127.0.0.1:7878/sleep &
done

I got the following output:

-> % cargo run
   Compiling hello v0.1.0 (/Users/me/rust/hello)
    Finished dev [unoptimized + debuginfo] target(s) in 0.47s
     Running `target/debug/hello`
Shutting down.
Shutting down worker 0
Worker 0 got a job; executing.
Worker 1 got a job; executing.
^C

Apparently the server deadlocked because I left it for several minutes without anything happening until I ctrl-cd. I thought it was a bit suspicious that the shutdown and "got a job" messages are inverted, although obviously one can't necessarily rely on the order of the output.

I believe this may not be a known issue, as I haven't found anything in the issue tracker, but I might be wrong, so I haven't done any further investitation.

buggymcbugfix commented 2 years ago

Chances are that I misunderstood something and that this is exactly the expected behaviour.

hongtron commented 1 year ago

@buggymcbugfix are you certain that you are running the code exactly as given in the book, i.e. you're able to reproduce by copy/pasting/compiling?

I only ask because I don't see any Worker {id} disconnected; shutting down. messages - I'm no expert, but I believe that when the mpsc sender is dropped (which should happen immediately after sending the second job, as it's the first step in dropping the ThreadPool), you should at least see other workers shutting down, even if the two workers that handled the requests are somehow deadlocked.

Ask me how I know - when I was going through the exercise, I forgot to drop the sender (i.e. drop(self.sender.take())), so:

  1. receiver.lock().unwrap().recv() never returned an Err result
  2. My workers' loops never broke
  3. They blocked the ThreadPool from ever dropping, because join() was waiting on their (infinite) loops
  4. My server never shut down :)
eduarddejong commented 1 year ago

Works all like a charm for me. :)

But I arived at 20.3 after reading the entire book, and I experimented with a so much more things than I the exact thing in the book anyway. Learing a language like Rust is a different story when already being an engineer for 20 years in many other languages. Though definitely still a learning curve. But absolutely worth it! 🦀

What I would love to see is that the book had also ended with a code that was able to be shut down by user input, by breaking out of the loop in main based on that, instead of the ugly .take(2). In my own version of the program I do a shutdown via an URL "/shutdown" for fun (in the real world you would put such a thing behind a secure login barrier of course, but the idea was nice).

Looks like there's also an error with a sentence about Option and take which is referring to the wrong things, but I should probably open a new issue for that.