Hello there.
I have recently been working on a Rust projects that features multithreading.
The application is structured like this:
A CLI thread (handles stdin etc.)
The main thread listening for connections (server)
Each connection is then processed in a different thread.
I have a stop command that shuts the server down, therefore I need to pass a reference to the TcpListener in order to drop it.
Since I need multiple ownership (the listener is also used for the connections), I use Arc<TcpListener>, then extract a reference using as_ref() in the new thread.
Everything works fine, however when I analyze the executable (in release mode) with Valgrind, I get warned that 304 bytes could have been possibly lost.
I can't manage to find the cause to this nor fix it.
/// Attempts to stop the server.
pub fn stop(listener: &TcpListener) {
println!("Stopping server...");
unsafe { crate::api::server_unload(); }
println!("Shutting down TCP listener...");
drop(listener); // this does nothing
print!("Exited succesfully.");
std::process::exit(0);
}
Your listener is a shared reference (which is therefore Copy), so drop-ping it does literally nothing. I advise you to use clippy (rustup component add clippy once, followed by cargo clippy instead of cargo check), since it has many lints against this kind of code smells
Now, regarding the memory leak, in
pub fn accept_user_input(listener: &TcpListener) {
loop {
print!("> ");
// stdout only flushes on newlines,
// and the above 'print' macro does not include one.
io::stdout().flush();
let mut buf = String::new();
io::stdin().read_line(&mut buf);
match buf.trim() {
"stop" => {
crate::net::stop(listener); // calls process::exit()
},
(input) => {
println!("{}: command not found.", input);
}
}
}
}
one thing that I am pretty sure is never freed is buf, since the program dies before it goes out of scope.
You can add
"stop" => {
buf = String::new(); // overrides allocated buffer with an empty string, effectively freeing the buffer
crate::net::stop(listener); // calls process::exit()
},
Finally, calling exit() is an abrupt way to end your program, I think it will generally be a tough challenge to avoid a "valgrind leak" in such cases