Terminating Detached Thread with Blocking Resource


#1

I have a question with regards to the termination of detached threads that may be holding on to blocking resource.

Assuming a scenario where there is a child thread that blocks on some condition variable which is never notified, and consequently causing the parent thread unable to terminate if it were joined in the parent thread, but allowing the parent thread to terminate (gracefully?) if it was left to detach, such as the following Rust code:

use std::thread;
use std::sync::{Arc, Condvar, Mutex};

fn main() {
    let m = Arc::new(Mutex::new(()));
    let m = m.clone();

    let cv = Arc::new(Condvar::new());
    let cv = cv.clone();

    let _ = thread::spawn(move || {
        let m = m.lock().unwrap();
        let _ = cv.wait(m).unwrap();
    });

    // let the above thread detach
    // if thread were to join, the CondVar wait will cause the program not to terminate
}

If the above idea were to be implemented in C++ using similar constructs (such as shared_ptr, mutex, condition_variable and thread), it should exhibit similar behaviour where the program would terminate (also gracefully?). But using Rust and implementing the same idea and compiling the code without any error provides more confidence that the program is indeed not exhibiting any undefined behaviour. :grin:

Would it be okay to always expect that the detached child thread (which is holding on to some resource which it would normally not be unblocked/freed if joined instead) not to cause problem to the program and gracefully terminate? Or would the termination behaviour of detached thread be OS dependent?

Thank you for your help in advance! :smiley:


#2

Well, I’m guess what’s happening is that when the parent thread reaches the end of its main function, somewhere in the cleanup code it reaches an exit system call on Linux (ExitProcess kernel call on Windows), and that basically just tells the OS to clean up all the stuff associated with the process, including other threads, like the child thread that’s deadlocked. The child thread isn’t gracefully terminating, it’s getting reaped at the end of the process’s lifetime. This behavior is definitely OS dependent, but fortunately seems to be working in a nice manner across mainstream platforms as you’ve noticed :). I wouldn’t recommend relying on it too much though…


#3

You will have a resource leak, but this is fine if it happens just once. At least on GNU/Linux, after exiting from main, the run-time will not wait for other threads to terminate, so there is no deadlock there. All process resources are freed by the kernel on process termination, too.

Some C++ programs use cancellation to clean up such threads, but cancellation is tricky to use correctly and not directly available from Rust right now.


#4

Thanks for the responses and clarifications on the sequence of termination!

Then I suppose it would generally be better to have the child thread be in a state where it will always eventually reach the end of its execution, and have the parent thread join the child thread. This should probably to be true even if the thread were to be detached.

I have encountered scenarios during coding in C++ where I require a class instance to have a child thread to keep waiting on a condition variable for new result to arrive (basically a signalling queue). For termination, I would usually set some status to indicate that the instance is destructing, and then create a dummy/error result to be inserted into this signalling queue, so that the child thread can have the opportunity to unblock and check that the instance is terminating and stop the blocking wait, and eventually have the parent thread to join this child thread.

Are there better techniques to handle termination of blocking waits in child threads, other than sending dummy results to unblock them so that they can check for termination? Would it be better to block with given a timeout instead, so that the child thread can check for termination by itself for each timeout (no need for dummy result), and repeat to block with timeout if it has not been terminated?

Thanks in advance again! :grin: