Worker threads: How do you commonly implement them?

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?