Stepan Yakovenko
Stepan Yakovenko

Reputation: 9216

How to freeze a thread and notify it from another?

I need to pause the current thread in Rust and notify it from another thread. In Java I would write:

synchronized(myThread) {
    myThread.wait();
}

and from the second thread (to resume main thread):

synchronized(myThread){
    myThread.notify();
}

Is is possible to do the same in Rust?

Upvotes: 7

Views: 6240

Answers (4)

WiSaGaN
WiSaGaN

Reputation: 48127

You can use std::thread::park() and std::thread::Thread::unpark() to achieve this.

In the thread you want to wait,

fn worker_thread() {
    std::thread::park();
}

in the controlling thread, which has a thread handle already,

fn main_thread(worker_thread: std::thread::Thread) {
    worker_thread.unpark();
}

Note that the parking thread can wake up spuriously, which means the thread can sometimes wake up without the any other threads calling unpark on it. You should prepare for this situation in your code, or use something like std::sync::mpsc::channel that is suggested in the accepted answer.

Upvotes: 3

kirillkh
kirillkh

Reputation: 193

There is a monitor crate that provides this functionality by combining Mutex with Condvar in a convenience structure.

(Full disclosure: I am the author.)

Briefly, it can be used like this:

    let mon = Arc::new(Monitor::new(false));
    {
        let mon = mon.clone();
        let _ = thread::spawn(move || {
            thread::sleep(Duration::from_millis(1000));

            mon.with_lock(|mut done| {     // done is a monitor::MonitorGuard<bool>
                *done = true;
                done.notify_one();
            });
        });
    }

    mon.with_lock(|mut done| {
        while !*done {
            done.wait();
        }
        println!("finished waiting");
    });

Here, mon.with_lock(...) is semantically equivalent to Java's synchronized(mon) {...}.

Upvotes: 2

Linear
Linear

Reputation: 22216

Using a channel that sends type () is probably easiest:

use std::sync::mpsc::channel;
use std::thread;

let (tx,rx) = channel();

// Spawn your worker thread, giving it `send` and whatever else it needs
thread::spawn(move|| {
    // Do whatever
    tx.send(()).expect("Could not send signal on channel.");
    // Continue
});

// Do whatever
rx.recv().expect("Could not receive from channel.");
// Continue working

The () type is because it's effectively zero-information, which means it's pretty clear you're only using it as a signal. The fact that it's size zero means it's also potentially faster in some scenarios (but realistically probably not any faster than a normal machine word write).

If you just need to notify the program that a thread is done, you can grab its join guard and wait for it to join.

let guard = thread::spawn( ... ); // This will automatically join when finished computing

guard.join().expect("Could not join thread");

Upvotes: 10

Matthieu M.
Matthieu M.

Reputation: 300119

There are multiple ways to achieve this in Rust.

The underlying model in Java is that each object contains both a mutex and a condition variable, if I remember correctly. So using a mutex and condition variable would work...

... however, I would personally switch to using a channel instead:

  • the "waiting" thread has the receiving end of the channel, and waits for it
  • the "notifying" thread has the sending end of the channel, and sends a message

It is easier to manipulate than a condition variable, notably because there is no risk to accidentally use a different mutex when locking the variable.

The std::sync::mpsc has two channels (asynchronous and synchronous) depending on your needs. Here, the asynchronous one matches more closely: std::sync::mpsc::channel.

Upvotes: 2

Related Questions