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.
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.
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:
Spawn thread A which reads data from a server.
Check if the user passed a command line option.
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.
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.
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)
}