pm100
pm100

Reputation: 50190

trying to do a thread join in a method

I have this code. It starts a background thread and passes work to it. I want to be able wait for the background thread to close cleanly (I know there is no way for it to stop at the moment, and I know it should be in a Drop implementation too. I am just trying to get the basic bones going)

use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
use std::thread;
use std::thread::JoinHandle;

pub struct S2 {
    jh: JoinHandle<()>,
    tx: Sender<i32>,
}

impl S2 {
    pub fn new() -> S2 {
        let (tx, rx): (Sender<i32>, Receiver<i32>) = mpsc::channel();

        let jh = thread::spawn(move || loop {
            let item = rx.recv().unwrap();
            println!("got {:?}", item)
        });
        S2 { jh, tx }
    }

    pub fn queue(&self, item: i32) {
        self.tx.send(item).expect("oops");
    }

    pub fn wait(&mut self) {
        self.jh.join().expect("oops");
    }
}

fn main() {
    let s2 = S2::new();
    s2.queue(42);
    // s2.jh.join().expect("oops");
}

The commented out line works fine. But that exposes the implementation details to the caller. I want it in a method. But the method one wont compile.

   Compiling tq3 v0.1.0 (C:\work\rust\tq3)
error[E0507]: cannot move out of `self.jh` which is behind a mutable reference
  --> src\main.rs:32:9
   |
32 |         self.jh.join().expect("oops");
   |         ^^^^^^^ move occurs because `self.jh` has type `std::thread::JoinHandle<()>`, which does not implement the `Copy` trait

I have tried my usual guesses at satisfying rust but so far have not been able to find the right one.

Upvotes: 0

Views: 772

Answers (1)

Michael Anderson
Michael Anderson

Reputation: 73490

The wait function as you define it,

pub fn wait(&mut self) {
     self.jh.join().expect("oops");
}

takes a mutable reference to an S2. But JoinHandle::join consumes the handle (it doesn't take a reference, but a value)

The key to understanding how to work around the error is to think about what state the S2 object will be in after s2.wait().

If you want the object to still be valid, but the join handle it contains will no longer be valid then the type of the handle can't be JoinHandle<()>, but maybe Option<JoinHandle<()>>.

If the object is no longer valid after a call to wait, then you can make wait take self rather than &mut self.

You can see examples with both these approaches here. They really deserve better error handling, especially the S3 case - where a double call to .wait() should be handled better. (You can't do a double call on S2 in my version since the first call consumes your instance).

Upvotes: 1

Related Questions