rustthread-safetyuci

How to share a pointer to a mutable variable to a thread?


I have to implement the UCI protocol for my chess engine.

It requires to read commands from command line. When the go command is sent, a search has to be started. However, during this search, other commands such as stop still have to be received. In the case of stop, the search has to quit altogether.

This is how the code looks (with leaving out non-important details).

pub fn main() {
    let mut stop: bool = false;
    loop {
        line.clear();
        stdin.read_line(&mut line).ok().unwrap();
        let arg: Vec<&str> = line.split_whitespace().collect();
        let cmd = arg[0];
        match cmd.trim() {
            "" => continue,
            "go" => {
                stop = false;
                thread::spawn(move || start_search(&stop, GameState, History, Timecontrol));
            }
            "stop" => {
                stop = true;
                thread::sleep(Duration::from_millis(50));
            }
            "quit" => {
                stop = true;
                thread::sleep(Duration::from_millis(50));
                break;
            }
            _ => {
                println!("Unknown command {}", line);
            }
        }
    }
}

pub fn start_search(stop_reference: &bool, _: GameState, _: History, _: Timecontrol) {
    /* Do search stuff here...
     */
    //Has to break when stop_reference is set to true
}

This code doesn't work because I assume the field just get's copied. However I have tried giving structs and then the code complains because you can't have a mutable reference and a normal reference both at once. I have also looked into ways to communicate with a thread. However most of the solutions used a channel to achieve that, but I think a channel doesn't work in my case since the thread is always calculating, so it would only receive the channels' command after it has terminated anyway.


Solution

  • You'll need to use a special reference, an Arc to share the boolean value between threads:

    pub fn main() {
        let stop = Arc::new(AtomicBool::new(false));
        loop {
            //...
    
            let stop = Arc::clone(&stop);
            thread::spawn(move || {
                start_search(stop);
            })
        }
    }
    
    pub fn start_search(stop: Arc<AtomicBool>) {
        loop {
            if stop.load(Ordering::Relaxed) {
                // STOP
            }
        }
    }