I am reading about concurrency in Rust so created a cargo lib to test the code. I wrote this basic function
use std::thread;
fn main() {
thread::spawn( || {
// I created 20 lines of print statements in thread, none prints out
println!("Hello 1, world thread!");
println!("Hello 2, world thread!");
println!("Hello 3, world thread!");
});
// also 20 lines here, they all executed
println!("Hello 1, world main function!");
println!("Hello 2, world main function!");
}
Code compiles, only print statements after the thread are logged on the terminal. I run cargo run many times but still same result.
to increase the chance to thread switching, inside the thread, I have 20 lines of print statements and I also put 20 lines of print statements outside the thread. But none of the print statements inside thread logs. I expected to see mixed logs from the spawn thread and the main thread.
I want to test out that when I have too many print statements, I will see some print out from the spawn thread and some from the main thread in mixed order. But I do not see any print out from spawn thread. I can use
use std::thread;
use std::time::Duration;
thread::sleep(Duration::from_millis(1));
or join.handle
but I do not understand why I do not see the expected behavior in the first place
I am using Kali Linux. I suspect that this might be related to my linux os but I could not find anything online related to this.
The program exits when the main thread ends. We can't say for sure which thread will exit first, but odds are it will be the thread that is already running. You can make the main thread wait for your other thread to finish by using the new thread's JoinHandle to wait until it finishes before exiting the program.
use std::thread;
fn main() {
let join_handle = thread::spawn( || {
println!("Hello 1, world thread!");
println!("Hello 2, world thread!");
println!("Hello 3, world thread!");
});
println!("Hello 1, world main function!");
println!("Hello 2, world main function!");
// Wait until other thread has finished
join_handle.join().expect("thread did not panic");
}
Rust Playground
Related
I am trying to introduce a timeout in my RPC requests using tokio:timer:Timeout:
use std::time::{Duration, Instant};
use tokio::prelude::*;
use tokio::timer::Delay;
fn main() {
let when = Instant::now() + Duration::from_millis(4000);
let task = Delay::new(when)
.and_then(|_| {
println!("Hello world!");
Ok(())
})
.map_err(|e| panic!("delay errored; err={:?}", e));
let task_with_timeout = task
.timeout(Duration::from_millis(3000))
.map_err(|e| println!("Timeout hit {:?}", e));
let _ = task_with_timeout.wait().expect("Failure");
// tokio::run(task_with_timeout);
}
If I run my future_with_timeout with tokio::run(), it works as expected.
However, calling wait on the task_with_timeout results in the task future getting an error:
thread 'main' panicked at 'delay errored; err=Error(Shutdown)'
instead of getting
Timeout hit Error(Elapsed)
I don't understand the difference here between using tokio::run() and wait().
Playground link
How do I make the code work using wait?
I wouldn't, and it's possible that you just can't.
Read the documentation for the timer module:
These types must be used from within the context of the Runtime or a timer context must be setup explicitly. See the tokio-timer crate for more details on how to setup a timer context.
Following the thread, we get to tokio_timer::with_default which requires a Tokio executor and a Timer. The executor uses the Enter type, which itself wants a future to block on.
All of this is to say that Tokio's futures may rely on features outside of the pure executor. If I understand the terms correctly (and it's likely I do not), those features are provided by the reactor. Calling wait has no knowledge of that.
See also:
How do I synchronously return a value calculated in an asynchronous Future in stable Rust?
I have the following code:
extern crate futures;
extern crate futures_cpupool;
extern crate tokio_timer;
use std::time::Duration;
use futures::Future;
use futures_cpupool::CpuPool;
use tokio_timer::Timer;
fn work(foo: Foo) {
std::thread::sleep(std::time::Duration::from_secs(10));
}
#[derive(Debug)]
struct Foo { }
impl Drop for Foo {
fn drop(&mut self) {
println!("Dropping Foo");
}
}
fn main() {
let pool = CpuPool::new_num_cpus();
let foo = Foo { };
let work_future = pool.spawn_fn(|| {
let work = work(foo);
let res: Result<(), ()> = Ok(work);
res
});
println!("Created the future");
let timer = Timer::default();
let timeout = timer.sleep(Duration::from_millis(750))
.then(|_| Err(()));
let select = timeout.select(work_future).map(|(win, _)| win);
match select.wait() {
Ok(()) => { },
Err(_) => { },
}
}
It seems this code doesn't execute Foo::drop - no message is printed.
I expected foo to be dropped as soon as timeout future resolves in select, as it's a part of environment of a closure, passed to dropped future.
How to make it execute Foo::drop?
The documentation for CpuPool states:
The worker threads associated with a thread pool are kept alive so long as there is an open handle to the CpuPool or there is work running on them. Once all work has been drained and all references have gone away the worker threads will be shut down.
Additionally, you transfer ownership of foo from main to the closure, which then transfers it to work. work will drop foo at the end of the block. However, work is also performing a blocking sleep operation. This sleep counts as work running on the thread.
The sleep is still going when the main thread exits, which immediately tears down the program, and all the threads, without any time to clean up.
As pointed out in How to terminate or suspend a Rust thread from another thread? (and other questions in other languages), there's no safe way to terminate a thread.
I expected foo to be dropped as soon as timeout future resolves in select, as it's a part of environment of a closure, passed to dropped future.
The future doesn't actually "have" the closure or foo. All it has is a handle to the thread:
pub struct CpuFuture<T, E> {
inner: Receiver<thread::Result<Result<T, E>>>,
keep_running_flag: Arc<AtomicBool>,
}
Strangely, the docs say:
If the returned future is dropped then this CpuPool will attempt to cancel the computation, if possible. That is, if the computation is in the middle of working, it will be interrupted when possible.
However, I don't see any implementation for Drop for CpuFuture, so I don't see how it could be possible (or safe). Instead of Drop, the threadpool itself runs a Future. When that future is polled, it checks to see if the receiver has been dropped. This behavior is provided by the oneshot::Receiver. However, this has nothing to do with threads, which are outside the view of the future.
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");
}
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)
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).