Conditionally join threads

It's no doubt there's got to be an idiomatic solution to my problem, however
I'm trying to solve it as simple as possible, here the problem in Rust pseudo-code:


let h = std::thread::spawn(|| some_heavy_work());

if some_condition {
      let result = h.join().unwrap();
      use_result();
      std::process::exit(0);
}

let data = some_other_work_in_main();
let result = h.join().unwrap(); 
use_data_and_result()

As you can see, I'm spawning a task and it runs in parallel, but I might or might not need the result of it earlier. Rust doesn't like two calls to join() even it's obvious that only one will be called.

How to solve this?

There must be something different in your real code, because your example does work:

I'm not sure I understand your question exactly. But a similar question I had recently was about running many threads at the same time but only needing to get the result of one of them, the one that finished first. Or similarly, having many threads running which are expected to run forever, and detecting when any of them stops running.

The issue then is not being able to call join on more that one thread. If you join anyone of them are then stuck there, and cannot see if any other thread terminates.

The only answer I got was to use an mpsc channel over which the treads would post their results when they are done. The main line would then wait on receiving a message from that channel.

Yeah, I forgot one more conditional check wich breaks it, please check:

Playground

If some_condition is true and the second condition is false (assuming it’s not actually hard coded to true), you’ll end up trying to join the thread twice and use the result twice which naturally won’t work.

Even if you get passed that, you’re still taking the result in the first conditional and passing ownership of it to use_result which means you won’t be able to use it later.

Is there anything else relevant that’s been stripped out of the code? I can’t figure out what the real world use case is for this code.

No, the second condition is not hardcoded, it's a user input. The use case is the following:

  1. Spawn thread A which reads data from a server.
  2. Check if the user passed a command line option.
  3. If the option was passed, then join thread A and check it's data against user input. It could conditionally exit the program and the rest of the main won't execute.
  4. If NO option was passed, do some other work in the main thread and later join the A thread and work with it.

Important note. If step 3 happened, and we didn't exit, the data from the thread must be of course available is in the scope of main, so in step 4 we should not join again.
So basically I want to allow the main thread to do some work until the data of thread A is needed.
Hope it helps to get the idea. :slight_smile:

thread_join

Ok. I came up with this. What makes this complicated is the potentially needing to reuse the joined result after the conditional. I made a small two state state machine. There seems like there's probably a cleaner way to do this, but it at least compiles. You can also do this with a couple Option's (one for the handle and one for the result), but this way there's no risk of being in an invalid state.


fn main() {
    enum State {
        Handle(std::thread::JoinHandle<Heavy>),
        Data(Heavy),
    }
    let mut s = State::Handle(std::thread::spawn(|| some_heavy_work()));

    if some_condition() {
        if let State::Handle(h) = s {
            let result = h.join().unwrap();
            use_result(&result);
            if true {
                std::process::exit(0);
            }
            s = State::Data(result)
        }
    }

    let data = some_other_work_in_main();
    let result = match s {
        State::Handle(h) => h.join().unwrap(),
        State::Data(r) => r,
    };
    use_data_and_result(data, result)
}

Playground

2 Likes

Oh, I like it! It's clean and easy to read. I'll give it a shot, thank you :+1:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.