Hello all.
I've found that a recurring pattern in our codebase is to use hidden worker threads for handling I/O. Given certain constraints on the design, this commonly results in something like the following code being created.
struct Owner {
running: AtomicBool,
worker: Mutex<Option<JoinHandle<()>>>,
}
impl Owner {
fn new() -> Arc<Owner> {
let owner = Arc::new(Owner {
running: AtomicBool::new(true),
worker: Mutex::new(None),
});
let owner_ref = owner.clone();
let worker = thread::spawn(move || {
while owner_ref.running.load(Ordering::Relaxed) {
// Do something with owner_ref now
}
});
*owner.worker.lock().unwrap() = Some(worker);
owner
}
fn stop(&self) {
self.running.store(false, Ordering::Relaxed);
self.worker.lock().unwrap().take().map(|w| w.join());
}
}
fn main_test() {
let owner = Owner::new();
owner.stop();
}
This is fairly horrid.
It gets even worse when I want to avoid having to manually call .stop()
. Because the Arc
won't go out of scope (it's alive in the worker) I'm left with either using a Weak
within the worker thread that gets upgraded every iteration, or doing some magic with a proxy type.
What patterns do you use to implement worker threads? How can I improve the state of this?