Shouldn't a loop spawned in a thread print repeatedly? - rust

Example code:
fn main() {
use std::thread::spawn;
spawn(|| { loop { println!("a") } });
// `a` is never printed
}
fn main() {
use std::thread::spawn;
spawn(|| { loop { println!("a") } });
loop { }
// `a` is printed repeatedly
}
a prints to the standard output in the second case, but the same is not true in the first case. Why is that? Shouldn't a print repeatedly in the first case as well?

Shouldn't a print repeatedly in the first case as well?
No. The documentation of thread:spawn says (emphasis mine):
The join handle will implicitly detach the child thread upon being dropped. In this case, the child thread may outlive the parent (unless the parent thread is the main thread; the whole process is terminated when the main thread finishes.) Additionally, the join handle provides a join method that can be used to join the child thread. If the child thread panics, join will return an Err containing the argument given to panic.
Your entire program exits because the main thread has exited. There was never even a chance for the child thread to start, much less print anything.
In the second example, you prevent the main thread from exiting by also causing that to spin forever.
What happens when you spawn a loop?
That thread will spin in the loop, as long as the program executes.
Idiomatically, you don't need the extra curly braces in the spawn, and it's more standard to only import the std::thread and then call thread::spawn:
fn main() {
use std::thread;
thread::spawn(|| loop {
println!("a")
});
}
To have the main thread wait for the child, you need to keep the JoinHandle from thread::spawn and call join on it:
fn main() {
use std::thread;
let handle = thread::spawn(|| loop {
println!("a")
});
handle.join().expect("The thread panicked");
}

Related

Wake another thread periodically with Rust

Condvar in Rust is good for waking another thread, but in this case below, I don't really need the true, I just want to wake the other thread periodically
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
fn main() {
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair2 = Arc::clone(&pair);
// Inside of our lock, spawn a new thread, and then wait for it to start.
thread::spawn(move|| {
let (lock, cvar) = &*pair2;
let mut started = lock.lock().unwrap();
// We notify the condvar that the value has changed.
loop{
*started = true;
cvar.notify_one();
std::thread::sleep(std::time::Duration::from_millis(20));
}
});
// Wait for the thread to start up.
let (lock, cvar) = &*pair;
let mut started = lock.lock().unwrap();
loop {
started = cvar.wait(started).unwrap();
println!("done");
}
println!("end");
}
Also, this example does not even work, I don't know why. It should wake the main thread every 20 ms.
The reason this doesn't work is that you're holding the mutex while you sleep. The main thread is only woken up after it has been hit with notify_one and its mutex is lockable. But the spawned thread holds the mutex locked forever.
Playground
In fact, you don't need the lock at all in your spawned thread and you could make it contain no data by constructing it as Mutex::new(()). However, if you do that, it is possible that your main thread hasn't finished one loop by the time the spawned thread finishes its sleep. The mutex ensures that when you call notify_one, the main thread is definitely waiting to be notified. If you want that behavior or not is up to you. With locking the mutex in the spawned thread, the main thread is woken up immediately if its previous loop took longer than one tick. Without the locking, wake-ups may be skipped and the next wake-up is aligned to the next tick.
But really, do what's in the answers #Finomnis suggested and use channels.

Why can't I communicate with a forked child process using Tokio UnixStream?

I'm trying to get a parent process and a child process to communicate with each other using a tokio::net::UnixStream. For some reason the child is unable to read whatever the parent writes to the socket, and presumably the other way around.
The function I have is similar to the following:
pub async fn run() -> Result<(), Error> {
let mut socks = UnixStream::pair()?;
match fork() {
Ok(ForkResult::Parent { .. }) => {
socks.0.write_u32(31337).await?;
Ok(())
}
Ok(ForkResult::Child) => {
eprintln!("Reading from master");
let msg = socks.1.read_u32().await?;
eprintln!("Read from master {}", msg);
Ok(())
}
Err(_) => Err(Error),
}
}
The socket doesn't get closed, otherwise I'd get an immediate error trying to read from socks.1. If I move the read into the parent process it works as expected. The first line "Reading from master" gets printed, but the second line never gets called.
I cannot change the communication paradigm, since I'll be using execve to start another binary that expects to be talking to a socketpair.
Any idea what I'm doing wrong here? Is it something to do with the async/await?
When you call the fork() system call:
The child process is created with a single thread—the one that called fork().
The default executor in tokio is a thread pool executor. The child process will only get one of the threads in the pool, so it won't work properly.
I found I was able to make your program work by setting the thread pool to contain only a single thread, like this:
use tokio::prelude::*;
use tokio::net::UnixStream;
use nix::unistd::{fork, ForkResult};
use nix::sys::wait;
use std::io::Error;
use std::io::ErrorKind;
use wait::wait;
// Limit to 1 thread
#[tokio::main(core_threads = 1)]
async fn main() -> Result<(), Error> {
let mut socks = UnixStream::pair()?;
match fork() {
Ok(ForkResult::Parent { .. }) => {
eprintln!("Writing!");
socks.0.write_u32(31337).await?;
eprintln!("Written!");
wait().unwrap();
Ok(())
}
Ok(ForkResult::Child) => {
eprintln!("Reading from master");
let msg = socks.1.read_u32().await?;
eprintln!("Read from master {}", msg);
Ok(())
}
Err(_) => Err(Error::new(ErrorKind::Other, "oh no!")),
}
}
Another change I had to make was to force the parent to wait for the child to complete, by calling wait() - also something you probably do not want to be doing in a real async program.
Most of the advice I have read that if you need to fork from a threaded program, either do it before creating any threads, or call exec_ve() in the child immediately after forking (which is what you plan to do anyway).

How can I cause a panic on a thread to immediately end the main thread?

In Rust, a panic terminates the current thread but is not sent back to the main thread. The solution we are told is to use join. However, this blocks the currently executing thread. So if my main thread spawns 2 threads, I cannot join both of them and immediately get a panic back.
let jh1 = thread::spawn(|| { println!("thread 1"); sleep(1000000); };
let jh2 = thread::spawn(|| { panic!("thread 2") };
In the above, if I join on thread 1 and then on thread 2 I will be waiting for 1 before ever receiving a panic from either thread
Although in some cases I desire the current behavior, my goal is to default to Go's behavior where I can spawn a thread and have it panic on that thread and then immediately end the main thread. (The Go specification also documents a protect function, so it is easy to achieve Rust behavior in Go).
Updated for Rust 1.10+, see revision history for the previous version of the answer
good point, in go the main thread doesn't get unwound, the program just crashes, but the original panic is reported. This is in fact the behavior I want (although ideally resources would get cleaned up properly everywhere).
This you can achieve with the recently stable std::panic::set_hook() function. With it, you can set a hook which prints the panic info and then exits the whole process, something like this:
use std::thread;
use std::panic;
use std::process;
fn main() {
// take_hook() returns the default hook in case when a custom one is not set
let orig_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {
// invoke the default handler and exit the process
orig_hook(panic_info);
process::exit(1);
}));
thread::spawn(move || {
panic!("something bad happened");
}).join();
// this line won't ever be invoked because of process::exit()
println!("Won't be printed");
}
Try commenting the set_hook() call out, and you'll see that the println!() line gets executed.
However, this approach, due to the use of process::exit(), will not allow resources allocated by other threads to be freed. In fact, I'm not sure that Go runtime allows this as well; it is likely that it uses the same approach with aborting the process.
I tried to force my code to stop processing when any of threads panicked. The only more-or-less clear solution without using unstable features was to use Drop trait implemented on some struct. This can lead to a resource leak, but in my scenario I'm ok with this.
use std::process;
use std::thread;
use std::time::Duration;
static THREAD_ERROR_CODE: i32 = 0x1;
static NUM_THREADS: u32 = 17;
static PROBE_SLEEP_MILLIS: u64 = 500;
struct PoisonPill;
impl Drop for PoisonPill {
fn drop(&mut self) {
if thread::panicking() {
println!("dropped while unwinding");
process::exit(THREAD_ERROR_CODE);
}
}
}
fn main() {
let mut thread_handles = vec![];
for i in 0..NUM_THREADS {
thread_handles.push(thread::spawn(move || {
let b = PoisonPill;
thread::sleep(Duration::from_millis(PROBE_SLEEP_MILLIS));
if i % 2 == 0 {
println!("kill {}", i);
panic!();
}
println!("this is thread number {}", i);
}));
}
for handle in thread_handles {
let _ = handle.join();
}
}
No matter how b = PoisonPill leaves it's scope, normal or after panic!, its Drop method kicks in. You can distinguish if the caller panicked using thread::panicking and take some action — in my case killing the process.
Looks like exiting the whole process on a panic in any thread is now (rust 1.62) as simple as adding this to your Cargo.toml:
[profile.release]
panic = 'abort'
[profile.dev]
panic = 'abort'
A panic in a thread then looks like this, with exit code 134:
thread '<unnamed>' panicked at 'panic in thread', src/main.rs:5:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Aborted (core dumped)

Kill child process while waiting for it

I want to execute another process and normally want to wait until it has finished. Lets say we spawn and wait for the process in thread T1:
let child = Command::new("rustc").spawn().unwrap();
child.wait();
Now, if a special event occurs (which thread T0 is waiting for) I want to kill the spawned process:
if let Ok(event) = special_event_notifier.recv() {
child.kill();
}
But I don't see a way to do it: both kill and wait take a mutable reference to Child and are therefore mutually exclusive. After calling wait no one can have any reference to child anymore.
I've found the wait-timeout crate, but I want to know if there's another way.
If the child subprocess do not close stdout before finishing, it's possible to wait reading stdout. Here is an example
use std::io::Read;
use std::process::*;
use std::thread;
use std::time::Duration;
fn wait_on_output(mut out: ChildStdout) {
while out.read_exact(&mut [0; 1024]).is_ok() { }
}
fn wait_or_kill(cmd: &mut Command, max: Duration) {
let mut child = cmd.stdout(Stdio::piped())
.spawn()
.expect("Cannot spawn child");
let out = child.stdout.take().expect("No stdout on child");
let h = thread::spawn(move || {
thread::sleep(max);
child.kill().expect("Cannot kill child");
println!("{:?}", child.wait());
});
wait_on_output(out);
h.join().expect("join fail");
}
fn main() {
wait_or_kill(Command::new("sleep").arg("1"), Duration::new(2, 0));
wait_or_kill(Command::new("sleep").arg("3"), Duration::new(2, 0));
}
The output of this program on my system is
Ok(ExitStatus(ExitStatus(0)))
Ok(ExitStatus(ExitStatus(9)))
Although not in the docs, killing a finished child returns Ok.
This works because killing a process close the files associated with it. However, if the child spawn new processes, killing the child may not kill these other processes and they may keep the stdout opened.
Obviously, you can just kill the process yourself. The Child::id method gives you the "OS-assigned process identifier" that should be sufficient for that.
The only problem is that killing a process is a platform-dependent action. On UNIX killing a process is handled with the kill function:
#![feature(libc)]
extern crate libc;
use std::env::args;
use std::process::Command;
use std::thread::{spawn, sleep};
use std::time::Duration;
use libc::{kill, SIGTERM};
fn main() {
let mut child = Command::new("/bin/sh").arg("-c").arg("sleep 1; echo foo").spawn().unwrap();
let child_id = child.id();
if args().any(|arg| arg == "--kill") {
spawn(move || {
sleep(Duration::from_millis(100));
unsafe {
kill(child_id as i32, SIGTERM);
}
});
}
child.wait().unwrap();
}
On Windows you might try the OpenProcess and TerminateProcess functions (available with the kernel32-sys crate).

Code not running in parallel when using thread::scoped

Can someone please explain why the code below does not run in parallel? I guess I don't understand how thread::scoped works..
use std::thread;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use std::old_io::timer;
fn main() {
let buf = Arc::new(Mutex::new(Vec::<String>::new()));
let res = test(buf);
println!("{:?}", *res.lock().unwrap());
}
fn test(buf: Arc<Mutex<Vec<String>>>) -> Arc<Mutex<Vec<String>>> {
let guards: Vec<_> = (0..3).map( |i| {
let mtx = buf.clone();
thread::scoped(|| {
println!("Thread: {}", i);
let mut res = mtx.lock().unwrap();
timer::sleep(Duration::seconds(5));
res.push(format!("thread {}", i));
});
}).collect();
buf
}
The code is based on the examples here where it's stated:
The scoped function takes one argument, a closure, indicated by the double bars ||. This closure is executed in a new thread created by scoped. The method is called scoped because it returns a 'join guard', which will automatically join the child thread when it goes out of scope. Because we collect these guards into a Vec, and that vector goes out of scope at the end of our program, our program will wait for every thread to finish before finishing.
Thanks
This is a tricky case. The problem is the humble semicolon. Look at this minimized code:
thread::scoped(|| {});
That semicolon means that the result of the collect isn't a vector of JoinGuards — it's a Vec<()>! Each JoinGuard is dropped immediately, forcing the thread to finish before the next iteration starts.
When you fix this issue, you'll hit the next problem, which is that i and mtx don't live long enough. You'll need to move them into the closure:
thread::scoped(move || {})

Resources