I think it's a reasonable solution, but it would be a bit more idiomatic/semantically correct to leave the only copy of the strong Arc
in the parent thread, and only leave a weak reference in the child. Then you could do
while keepalive_recv.upgrade().is_some() {
...
}
which IMO signals the intention of single owneship better.
I would also recommend newtyping both halves, so that you can't accidentally (or intentionally) clone either half, and there is always truly a single owner only. I'd do that at least for the receiving end so that the child can't keep itself alive.
Actually, it's pretty easy to incorporate this into a single newtype and a function hiding the implementation details of the keepalive-checking loop: Playground
use core::time::Duration;
use core::ops::Deref;
use std::sync::Arc;
use std::thread::{self, JoinHandle};
#[derive(Debug)]
pub struct KeepaliveHandle {
keepalive: Arc<()>,
join: JoinHandle<()>,
}
impl Deref for KeepaliveHandle {
type Target = JoinHandle<()>;
fn deref(&self) -> &Self::Target {
&self.join
}
}
pub fn spawn_keepalive<F: FnMut() + Send + 'static>(mut worker: F) -> KeepaliveHandle {
let keepalive = Arc::default();
let recv = Arc::downgrade(&keepalive);
let join = thread::spawn(move || {
while recv.upgrade().is_some() {
worker();
}
});
KeepaliveHandle { keepalive, join }
}
fn main() {
let mut counter = 0;
let handle = spawn_keepalive(move || {
println!("working: {}", counter);
counter += 1;
thread::sleep(Duration::from_secs(1));
});
println!("Spawned thread");
thread::sleep(Duration::from_secs(5));
drop(handle);
println!("Finished");
}