//! Small utility to correctly spawn crossbeam-channel based worker threads. use crossbeam_channel::{bounded, unbounded, Receiver, Sender}; /// A wrapper around event-processing thread with automatic shutdown semantics. pub struct Worker<I, O> { // XXX: field order is significant here. // // In Rust, fields are dropped in the declaration order, and we rely on this // here. We must close input first, so that the `thread` (who holds the // opposite side of the channel) noticed shutdown. Then, we must join the // thread, but we must keep out alive so that the thread does not panic. // // Note that a potential problem here is that we might drop some messages // from receiver on the floor. This is ok for rust-analyzer: we have only a // single client, so, if we are shutting down, nobody is interested in the // unfinished work anyway! sender: Sender<I>, _thread: jod_thread::JoinHandle<()>, receiver: Receiver<O>, } impl<I, O> Worker<I, O> { pub fn spawn<F>(name: &'static str, buf: usize, f: F) -> Worker<I, O> where F: FnOnce(Receiver<I>, Sender<O>) + Send + 'static, I: Send + 'static, O: Send + 'static, { // Set up worker channels in a deadlock-avoiding way. If one sets both input // and output buffers to a fixed size, a worker might get stuck. let (sender, input_receiver) = bounded::<I>(buf); let (output_sender, receiver) = unbounded::<O>(); let _thread = jod_thread::Builder::new() .name(name.to_string()) .spawn(move || f(input_receiver, output_sender)) .expect("failed to spawn a thread"); Worker { sender, _thread, receiver } } } impl<I, O> Worker<I, O> { pub fn sender(&self) -> &Sender<I> { &self.sender } pub fn receiver(&self) -> &Receiver<O> { &self.receiver } }