Unlocking Rust’s Power: Easy Insights into the Guarded Suspension Pattern

Photo by Josh Sorenson: https://www.pexels.com/photo/green-trees-1054394/

Introduction

In multithreaded applications, it’s common for one thread to let another know when specific conditions are met, like when data is ready or a time-consuming task is finished. In Rust we will use the Mutex and Condvar to achieve this communication. This example demonstrates implementing a GuardedGarage to park ExpensiveCar objects.

Implementation in Rust

We will start by importing the crates we need:

use std::sync::{Arc,Mutex,Condvar};
use std::thread;
use std::fmt;

Next we will implement an ExpensiveCar object, which just has a brand and a color. We also implement the Display trait, to be able to print our objects:

struct ExpensiveCar {
    brand: String,
    color: String,
}

impl fmt::Display for ExpensiveCar {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "brand: {}, color: {}", self.brand, self.color)
    }
}

impl ExpensiveCar {
    fn new(brand: String, color: String) -> ExpensiveCar {
        ExpensiveCar {
            brand,
            color,
        }
    }
}

Now we come to the GuardedGarage itself. The GuardedGarage has two fields:

  1. A Vec of ExpensiveCar wrapped in a Mutex to make sure only one thread can access.
  2. And a guard of the CondVar which can block, and wake up threads as we shall shee.

In the park() method, we lock our queue, push the car. The guard then notifies one waiting thread that there might be data ready to consume.

In the get() method, we also acquire a look on the Mutex. After which we wait till something apears in the queue, by checking whether the queue is empty and waiting for a signal from our guard. If such an event happens, we remove the first item on the queue and return it.

struct GuardedGarage {
    queue:Mutex<Vec<ExpensiveCar>>,
    guard:Condvar,
}

impl GuardedGarage {
    fn new()->Self {
        GuardedGarage {
            queue:Mutex::new(Vec::new()),
            guard: Condvar::new(),
        }
    }

    fn park(&self,car:ExpensiveCar) {
        let mut queue=self.queue.lock().unwrap();
        queue.push(car);
        self.guard.notify_one();
    }

    fn get(&self)->ExpensiveCar {
        let mut queue=self.queue.lock().unwrap();
        while queue.is_empty() {
            queue=self.guard.wait(queue).unwrap();
        }
        queue.remove(0)
    }


}

As you can see, the combination of the Mutex with the Condvar really makes for safe communication between threads, ensuring only one thread at a time can access it by signalling it.

Another way to do this would be to use channels, but I will implement that solution in a later post.

Testing time

In the main() method we create a producer and consumer thread respectively using the GuardedGarage:

fn main() {
    let guarded_garage=Arc::new(GuardedGarage::new());
    let producer={
        let guarded_garage=Arc::clone(&guarded_garage);
        thread::spawn(move || {
            for i in 0..10 {
                let expensive_car=ExpensiveCar::new(format!("Car {}",i),format!("red{}",i));
                guarded_garage.park(expensive_car);
            }

        })
    };

    let consumer={
        let guarded_garage=Arc::clone(&guarded_garage);
        thread::spawn(move || {
            for _ in 0..10 {
                let expensive_car=guarded_garage.get();
                println!("got a car: {}",expensive_car);
            }

        })
    };

    producer.join().unwrap();
    consumer.join().unwrap();
}

Conclusion

As you can see this pattern is not very hard to implement, using the basic building blocks that the standard library gives us.

There are a few enhancements:

  1. Error handling should be implemented. Using unwrap is not recommended for production code. I did it here to keep the code short and simple
  2. Perhaps we could make this structure generic, like vector. That will be subject of another post
  3. Another way to implement this pattern is using channels, This is also the subject for another post.

Leave a Reply

Your email address will not be published. Required fields are marked *