1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
use std::sync::mpsc;
use std::sync::mpsc::TryRecvError;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
use std::thread::JoinHandle;

use log::info;

#[derive(Default)]
pub struct Worker {
    cancellation: Option<mpsc::Sender<()>>,
}

impl Worker {
    pub fn run<F, T>(&mut self, func: F) -> JoinHandle<T>
    where
        F: 'static + Send + FnOnce(Cancel) -> T,
        T: 'static + Send,
    {
        if let Some(cancellation) = self.cancellation.as_mut() {
            info!("asking existing worker to cancel");
            let _ = cancellation.send(());
        }
        let (cancellation, done) = mpsc::channel();
        self.cancellation = Some(cancellation);
        thread::spawn(move || func(Cancel::OnMessage(Arc::new(Mutex::new(done)))))
    }
}

pub enum Cancel {
    OnMessage(Arc<Mutex<mpsc::Receiver<()>>>),
    Never,
}

impl Cancel {
    pub fn check_done(&self) -> bool {
        match self {
            Cancel::OnMessage(done) => match done.lock().unwrap().try_recv() {
                Ok(()) | Err(TryRecvError::Disconnected) => true,
                Err(TryRecvError::Empty) => false,
            },
            Cancel::Never => false,
        }
    }

    pub fn maybe_abort(&self) {
        assert!(!self.check_done())
    }
}